ETH Price: $1,869.86 (+0.02%)

Transaction Decoder

Block:
18116134 at Sep-11-2023 10:36:59 PM +UTC
Transaction Fee:
0.003195637748825335 ETH $5.98
Gas Used:
203,797 Gas / 15.680494555 Gwei

Emitted Events:

140 Bueno721Drop.Transfer( from=0xce96e62c9042ccf58ae46a2920f311622c53cc6b, to=[Sender] 0xae19e69d592812d9c53d265e0ce4458714261dcb, tokenId=4256 )
141 Seaport.OrderFulfilled( orderHash=7E7E6BCDA296F57785032A5A93C0E6B988C508CC66EDA0B6EC3710E3C93EDCC5, offerer=0xce96e62c9042ccf58ae46a2920f311622c53cc6b, zone=0x004C0050...C00560C00, recipient=[Sender] 0xae19e69d592812d9c53d265e0ce4458714261dcb, offer=, consideration= )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...c0aAF14dC
(Seaport 1.5)
0x0000a26b...000fAa719
28.440821493877152396 Eth28.441446493877152396 Eth0.000625
0xA5b276D7...ED852950f
0xAE19E69D...714261DCb
0.032402422020445117 Eth
Nonce: 660
0.004206784271619782 Eth
Nonce: 661
0.028195637748825335
0xB14276D8...072BB38f9 0.016170046869678804 Eth0.016720046869678804 Eth0.00055
(MEV Builder: 0xBaF...e19)
2.601515298202687447 Eth2.601820993702687447 Eth0.0003056955
0xCe96e62C...22c53cC6b 0.0371472791775178 Eth0.0609722791775178 Eth0.023825

Execution Trace

ETH 0.025 Seaport.fulfillOrder( [{name:parameters, type:tuple, order:1, indexed:false, value:[{name:offerer, type:address, order:1, indexed:false, value:0xCe96e62C9042CCf58AE46a2920F311622c53cC6b, valueString:0xCe96e62C9042CCf58AE46a2920F311622c53cC6b}, {name:zone, type:address, order:2, indexed:false, value:0x004C00500000aD104D7DBd00e3ae0A5C00560C00, valueString:0x004C00500000aD104D7DBd00e3ae0A5C00560C00}, {name:offer, type:tuple[], order:3, indexed:false}, {name:consideration, type:tuple[], order:4, indexed:false}, {name:orderType, type:uint8, order:5, indexed:false, value:0, valueString:0}, {name:startTime, type:uint256, order:6, indexed:false, value:1694470476, valueString:1694470476}, {name:endTime, type:uint256, order:7, indexed:false, value:1697062476, valueString:1697062476}, {name:zoneHash, type:bytes32, order:8, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:salt, type:uint256, order:9, indexed:false, value:24446860302761739304752683030156737591518664810215442929810972844415227209740, valueString:24446860302761739304752683030156737591518664810215442929810972844415227209740}, {name:conduitKey, type:bytes32, order:10, indexed:false, value:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000, valueString:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000}, {name:totalOriginalConsiderationItems, type:uint256, order:11, indexed:false, value:3, valueString:3}], valueString:[{name:offerer, type:address, order:1, indexed:false, value:0xCe96e62C9042CCf58AE46a2920F311622c53cC6b, valueString:0xCe96e62C9042CCf58AE46a2920F311622c53cC6b}, {name:zone, type:address, order:2, indexed:false, value:0x004C00500000aD104D7DBd00e3ae0A5C00560C00, valueString:0x004C00500000aD104D7DBd00e3ae0A5C00560C00}, {name:offer, type:tuple[], order:3, indexed:false}, {name:consideration, type:tuple[], order:4, indexed:false}, {name:orderType, type:uint8, order:5, indexed:false, value:0, valueString:0}, {name:startTime, type:uint256, order:6, indexed:false, value:1694470476, valueString:1694470476}, {name:endTime, type:uint256, order:7, indexed:false, value:1697062476, valueString:1697062476}, {name:zoneHash, type:bytes32, order:8, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:salt, type:uint256, order:9, indexed:false, value:24446860302761739304752683030156737591518664810215442929810972844415227209740, valueString:24446860302761739304752683030156737591518664810215442929810972844415227209740}, {name:conduitKey, type:bytes32, order:10, indexed:false, value:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000, valueString:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000}, {name:totalOriginalConsiderationItems, type:uint256, order:11, indexed:false, value:3, valueString:3}]}, {name:signature, type:bytes, order:2, indexed:false, value:0x3FC85CF49307B259AAF50CE34A94D10E6463B4CAA87BF0C583DA45F2943A4142BB5290212B78D946FBDEFEECD4BF12D5D5E07C5EC6A29C536AABC67F3F6BE86D, valueString:0x3FC85CF49307B259AAF50CE34A94D10E6463B4CAA87BF0C583DA45F2943A4142BB5290212B78D946FBDEFEECD4BF12D5D5E07C5EC6A29C536AABC67F3F6BE86D}], fulfillerConduitKey=0000000000000000000000000000000000000000000000000000000000000000 ) => ( fulfilled=True )
  • Null: 0x000...001.665a22c4( )
  • ETH 0.023825 0xce96e62c9042ccf58ae46a2920f311622c53cc6b.CALL( )
  • ETH 0.000625 PayableProxy.CALL( )
  • ETH 0.00055 0xb14276d8da92bd0c71506f62c27827d072bb38f9.CALL( )
  • Conduit.execute( transfers= ) => ( transfers= )
    • Bueno721Drop.transferFrom( from=0xCe96e62C9042CCf58AE46a2920F311622c53cC6b, to=0xAE19E69D592812d9c53d265e0Ce4458714261DCb, tokenId=4256 )
      • Bueno721Drop.transferFrom( from=0xCe96e62C9042CCf58AE46a2920F311622c53cC6b, to=0xAE19E69D592812d9c53d265e0Ce4458714261DCb, tokenId=4256 )
        • OperatorFilterRegistry.isOperatorAllowed( registrant=0xA5b276D7C697a7fBB3437253F3fd7EeED852950f, operator=0x1E0049783F008A0085193E00003D00cd54003c71 ) => ( True )
          File 1 of 6: Seaport
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { Consideration } from "./lib/Consideration.sol";
          /**
           * @title Seaport
           * @custom:version 1.5
           * @author 0age (0age.eth)
           * @custom:coauthor d1ll0n (d1ll0n.eth)
           * @custom:coauthor transmissions11 (t11s.eth)
           * @custom:coauthor James Wenzel (emo.eth)
           * @custom:contributor Kartik (slokh.eth)
           * @custom:contributor LeFevre (lefevre.eth)
           * @custom:contributor Joseph Schiarizzi (CupOJoseph.eth)
           * @custom:contributor Aspyn Palatnick (stuckinaboot.eth)
           * @custom:contributor Stephan Min (stephanm.eth)
           * @custom:contributor Ryan Ghods (ralxz.eth)
           * @custom:contributor Daniel Viau (snotrocket.eth)
           * @custom:contributor hack3r-0m (hack3r-0m.eth)
           * @custom:contributor Diego Estevez (antidiego.eth)
           * @custom:contributor Chomtana (chomtana.eth)
           * @custom:contributor Saw-mon and Natalie (sawmonandnatalie.eth)
           * @custom:contributor 0xBeans (0xBeans.eth)
           * @custom:contributor 0x4non (punkdev.eth)
           * @custom:contributor Laurence E. Day (norsefire.eth)
           * @custom:contributor vectorized.eth (vectorized.eth)
           * @custom:contributor karmacoma (karmacoma.eth)
           * @custom:contributor horsefacts (horsefacts.eth)
           * @custom:contributor UncarvedBlock (uncarvedblock.eth)
           * @custom:contributor Zoraiz Mahmood (zorz.eth)
           * @custom:contributor William Poulin (wpoulin.eth)
           * @custom:contributor Rajiv Patel-O'Connor (rajivpoc.eth)
           * @custom:contributor tserg (tserg.eth)
           * @custom:contributor cygaar (cygaar.eth)
           * @custom:contributor Meta0xNull (meta0xnull.eth)
           * @custom:contributor gpersoon (gpersoon.eth)
           * @custom:contributor Matt Solomon (msolomon.eth)
           * @custom:contributor Weikang Song (weikangs.eth)
           * @custom:contributor zer0dot (zer0dot.eth)
           * @custom:contributor Mudit Gupta (mudit.eth)
           * @custom:contributor leonardoalt (leoalt.eth)
           * @custom:contributor cmichel (cmichel.eth)
           * @custom:contributor PraneshASP (pranesh.eth)
           * @custom:contributor JasperAlexander (jasperalexander.eth)
           * @custom:contributor Ellahi (ellahi.eth)
           * @custom:contributor zaz (1zaz1.eth)
           * @custom:contributor berndartmueller (berndartmueller.eth)
           * @custom:contributor dmfxyz (dmfxyz.eth)
           * @custom:contributor daltoncoder (dontkillrobots.eth)
           * @custom:contributor 0xf4ce (0xf4ce.eth)
           * @custom:contributor phaze (phaze.eth)
           * @custom:contributor hrkrshnn (hrkrshnn.eth)
           * @custom:contributor axic (axic.eth)
           * @custom:contributor leastwood (leastwood.eth)
           * @custom:contributor 0xsanson (sanson.eth)
           * @custom:contributor blockdev (blockd3v.eth)
           * @custom:contributor fiveoutofnine (fiveoutofnine.eth)
           * @custom:contributor shuklaayush (shuklaayush.eth)
           * @custom:contributor dravee (dravee.eth)
           * @custom:contributor 0xPatissier
           * @custom:contributor pcaversaccio
           * @custom:contributor David Eiber
           * @custom:contributor csanuragjain
           * @custom:contributor sach1r0
           * @custom:contributor twojoy0
           * @custom:contributor ori_dabush
           * @custom:contributor Daniel Gelfand
           * @custom:contributor okkothejawa
           * @custom:contributor FlameHorizon
           * @custom:contributor vdrg
           * @custom:contributor dmitriia
           * @custom:contributor bokeh-eth
           * @custom:contributor asutorufos
           * @custom:contributor rfart(rfa)
           * @custom:contributor Riley Holterhus
           * @custom:contributor big-tech-sux
           * @notice Seaport is a generalized native token/ERC20/ERC721/ERC1155
           *         marketplace with lightweight methods for common routes as well as
           *         more flexible methods for composing advanced orders or groups of
           *         orders. Each order contains an arbitrary number of items that may be
           *         spent (the "offer") along with an arbitrary number of items that must
           *         be received back by the indicated recipients (the "consideration").
           */
          contract Seaport is Consideration {
              /**
               * @notice Derive and set hashes, reference chainId, and associated domain
               *         separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) Consideration(conduitController) {}
              /**
               * @dev Internal pure function to retrieve and return the name of this
               *      contract.
               *
               * @return The name of this contract.
               */
              function _name() internal pure override returns (string memory) {
                  // Return the name of the contract.
                  assembly {
                      mstore(0x20, 0x20)
                      mstore(0x47, 0x07536561706f7274)
                      return(0x20, 0x60)
                  }
              }
              /**
               * @dev Internal pure function to retrieve the name of this contract as a
               *      string that will be used to derive the name hash in the constructor.
               *
               * @return The name of this contract as a string.
               */
              function _nameString() internal pure override returns (string memory) {
                  // Return the name of the contract.
                  return "Seaport";
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ConsiderationInterface
          } from "../interfaces/ConsiderationInterface.sol";
          import {
              AdvancedOrder,
              BasicOrderParameters,
              CriteriaResolver,
              Execution,
              Fulfillment,
              FulfillmentComponent,
              Order,
              OrderComponents
          } from "./ConsiderationStructs.sol";
          import { OrderCombiner } from "./OrderCombiner.sol";
          import {
              CalldataStart,
              CalldataPointer
          } from "../helpers/PointerLibraries.sol";
          import {
              Offset_fulfillAdvancedOrder_criteriaResolvers,
              Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts,
              Offset_fulfillAvailableAdvancedOrders_criteriaResolvers,
              Offset_fulfillAvailableAdvancedOrders_offerFulfillments,
              Offset_fulfillAvailableOrders_considerationFulfillments,
              Offset_fulfillAvailableOrders_offerFulfillments,
              Offset_matchAdvancedOrders_criteriaResolvers,
              Offset_matchAdvancedOrders_fulfillments,
              Offset_matchOrders_fulfillments,
              OrderParameters_counter_offset
          } from "./ConsiderationConstants.sol";
          /**
           * @title Consideration
           * @author 0age (0age.eth)
           * @custom:coauthor d1ll0n (d1ll0n.eth)
           * @custom:coauthor transmissions11 (t11s.eth)
           * @custom:coauthor James Wenzel (emo.eth)
           * @custom:version 1.5
           * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
           *         marketplace that provides lightweight methods for common routes as
           *         well as more flexible methods for composing advanced orders or groups
           *         of orders. Each order contains an arbitrary number of items that may
           *         be spent (the "offer") along with an arbitrary number of items that
           *         must be received back by the indicated recipients (the
           *         "consideration").
           */
          contract Consideration is ConsiderationInterface, OrderCombiner {
              /**
               * @notice Derive and set hashes, reference chainId, and associated domain
               *         separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) OrderCombiner(conduitController) {}
              /**
               * @notice Accept native token transfers during execution that may then be
               *         used to facilitate native token transfers, where any tokens that
               *         remain will be transferred to the caller. Native tokens are only
               *         acceptable mid-fulfillment (and not during basic fulfillment).
               */
              receive() external payable {
                  // Ensure the reentrancy guard is currently set to accept native tokens.
                  _assertAcceptingNativeTokens();
              }
              /**
               * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by
               *         supplying Ether (or other native tokens), ERC20 tokens, an ERC721
               *         item, or an ERC1155 item as consideration. Six permutations are
               *         supported: Native token to ERC721, Native token to ERC1155, ERC20
               *         to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and ERC1155 to
               *         ERC20 (with native tokens supplied as msg.value). For an order to
               *         be eligible for fulfillment via this method, it must contain a
               *         single offer item (though that item may have a greater amount if
               *         the item is not an ERC721). An arbitrary number of "additional
               *         recipients" may also be supplied which will each receive native
               *         tokens or ERC20 items from the fulfiller as consideration. Refer
               *         to the documentation for a more comprehensive summary of how to
               *         utilize this method and what orders are compatible with it.
               *
               * @param parameters Additional information on the fulfilled order. Note
               *                   that the offerer and the fulfiller must first approve
               *                   this contract (or their chosen conduit if indicated)
               *                   before any tokens can be transferred. Also note that
               *                   contract recipients of ERC1155 consideration items must
               *                   implement `onERC1155Received` to receive those items.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillBasicOrder(
                  BasicOrderParameters calldata parameters
              ) external payable override returns (bool fulfilled) {
                  // Validate and fulfill the basic order.
                  fulfilled = _validateAndFulfillBasicOrder(parameters);
              }
              /**
               * @notice Fulfill an order offering an ERC20, ERC721, or ERC1155 item by
               *         supplying Ether (or other native tokens), ERC20 tokens, an ERC721
               *         item, or an ERC1155 item as consideration. Six permutations are
               *         supported: Native token to ERC721, Native token to ERC1155, ERC20
               *         to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and ERC1155 to
               *         ERC20 (with native tokens supplied as msg.value). For an order to
               *         be eligible for fulfillment via this method, it must contain a
               *         single offer item (though that item may have a greater amount if
               *         the item is not an ERC721). An arbitrary number of "additional
               *         recipients" may also be supplied which will each receive native
               *         tokens or ERC20 items from the fulfiller as consideration. Refer
               *         to the documentation for a more comprehensive summary of how to
               *         utilize this method and what orders are compatible with it. Note
               *         that this function costs less gas than `fulfillBasicOrder` due to
               *         the zero bytes in the function selector (0x00000000) which also
               *         results in earlier function dispatch.
               *
               * @param parameters Additional information on the fulfilled order. Note
               *                   that the offerer and the fulfiller must first approve
               *                   this contract (or their chosen conduit if indicated)
               *                   before any tokens can be transferred. Also note that
               *                   contract recipients of ERC1155 consideration items must
               *                   implement `onERC1155Received` to receive those items.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillBasicOrder_efficient_6GL6yc(
                  BasicOrderParameters calldata parameters
              ) external payable override returns (bool fulfilled) {
                  // Validate and fulfill the basic order.
                  fulfilled = _validateAndFulfillBasicOrder(parameters);
              }
              /**
               * @notice Fulfill an order with an arbitrary number of items for offer and
               *         consideration. Note that this function does not support
               *         criteria-based orders or partial filling of orders (though
               *         filling the remainder of a partially-filled order is supported).
               *
               * @custom:param order        The order to fulfill. Note that both the
               *                            offerer and the fulfiller must first approve
               *                            this contract (or the corresponding conduit if
               *                            indicated) to transfer any relevant tokens on
               *                            their behalf and that contracts must implement
               *                            `onERC1155Received` to receive ERC1155 tokens
               *                            as consideration.
               * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
               *                            any, to source the fulfiller's token approvals
               *                            from. The zero hash signifies that no conduit
               *                            should be used (and direct approvals set on
               *                            this contract).
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillOrder(
                  /**
                   * @custom:name order
                   */
                  Order calldata,
                  bytes32 fulfillerConduitKey
              ) external payable override returns (bool fulfilled) {
                  // Convert order to "advanced" order, then validate and fulfill it.
                  fulfilled = _validateAndFulfillAdvancedOrder(
                      _toAdvancedOrderReturnType(_decodeOrderAsAdvancedOrder)(
                          CalldataStart.pptr()
                      ),
                      new CriteriaResolver[](0), // No criteria resolvers supplied.
                      fulfillerConduitKey,
                      msg.sender
                  );
              }
              /**
               * @notice Fill an order, fully or partially, with an arbitrary number of
               *         items for offer and consideration alongside criteria resolvers
               *         containing specific token identifiers and associated proofs.
               *
               * @custom:param advancedOrder     The order to fulfill along with the
               *                                 fraction of the order to attempt to fill.
               *                                 Note that both the offerer and the
               *                                 fulfiller must first approve this
               *                                 contract (or their conduit if indicated
               *                                 by the order) to transfer any relevant
               *                                 tokens on their behalf and that contracts
               *                                 must implement `onERC1155Received` to
               *                                 receive ERC1155 tokens as consideration.
               *                                 Also note that all offer and
               *                                 consideration components must have no
               *                                 remainder after multiplication of the
               *                                 respective amount with the supplied
               *                                 fraction for the partial fill to be
               *                                 considered valid.
               * @custom:param criteriaResolvers An array where each element contains a
               *                                 reference to a specific offer or
               *                                 consideration, a token identifier, and a
               *                                 proof that the supplied token identifier
               *                                 is contained in the merkle root held by
               *                                 the item in question's criteria element.
               *                                 Note that an empty criteria indicates
               *                                 that any (transferable) token identifier
               *                                 on the token in question is valid and
               *                                 that no associated proof needs to be
               *                                 supplied.
               * @param fulfillerConduitKey      A bytes32 value indicating what conduit,
               *                                 if any, to source the fulfiller's token
               *                                 approvals from. The zero hash signifies
               *                                 that no conduit should be used (and
               *                                 direct approvals set on this contract).
               * @param recipient                The intended recipient for all received
               *                                 items, with `address(0)` indicating that
               *                                 the caller should receive the items.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillAdvancedOrder(
                  /**
                   * @custom:name advancedOrder
                   */
                  AdvancedOrder calldata,
                  /**
                   * @custom:name criteriaResolvers
                   */
                  CriteriaResolver[] calldata,
                  bytes32 fulfillerConduitKey,
                  address recipient
              ) external payable override returns (bool fulfilled) {
                  // Validate and fulfill the order.
                  fulfilled = _validateAndFulfillAdvancedOrder(
                      _toAdvancedOrderReturnType(_decodeAdvancedOrder)(
                          CalldataStart.pptr()
                      ),
                      _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
                          CalldataStart.pptr(
                              Offset_fulfillAdvancedOrder_criteriaResolvers
                          )
                      ),
                      fulfillerConduitKey,
                      _substituteCallerForEmptyRecipient(recipient)
                  );
              }
              /**
               * @notice Attempt to fill a group of orders, each with an arbitrary number
               *         of items for offer and consideration. Any order that is not
               *         currently active, has already been fully filled, or has been
               *         cancelled will be omitted. Remaining offer and consideration
               *         items will then be aggregated where possible as indicated by the
               *         supplied offer and consideration component arrays and aggregated
               *         items will be transferred to the fulfiller or to each intended
               *         recipient, respectively. Note that a failing item transfer or an
               *         issue with order formatting will cause the entire batch to fail.
               *         Note that this function does not support criteria-based orders or
               *         partial filling of orders (though filling the remainder of a
               *         partially-filled order is supported).
               *
               * @custom:param orders                    The orders to fulfill. Note that
               *                                         both the offerer and the
               *                                         fulfiller must first approve this
               *                                         contract (or the corresponding
               *                                         conduit if indicated) to transfer
               *                                         any relevant tokens on their
               *                                         behalf and that contracts must
               *                                         implement `onERC1155Received` to
               *                                         receive ERC1155 tokens as
               *                                         consideration.
               * @custom:param offerFulfillments         An array of FulfillmentComponent
               *                                         arrays indicating which offer
               *                                         items to attempt to aggregate
               *                                         when preparing executions. Note
               *                                         that any offer items not included
               *                                         as part of a fulfillment will be
               *                                         sent unaggregated to the caller.
               * @custom:param considerationFulfillments An array of FulfillmentComponent
               *                                         arrays indicating which
               *                                         consideration items to attempt to
               *                                         aggregate when preparing
               *                                         executions.
               * @param fulfillerConduitKey              A bytes32 value indicating what
               *                                         conduit, if any, to source the
               *                                         fulfiller's token approvals from.
               *                                         The zero hash signifies that no
               *                                         conduit should be used (and
               *                                         direct approvals set on this
               *                                         contract).
               * @param maximumFulfilled                 The maximum number of orders to
               *                                         fulfill.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders.
               */
              function fulfillAvailableOrders(
                  /**
                   * @custom:name orders
                   */
                  Order[] calldata,
                  /**
                   * @custom:name offerFulfillments
                   */
                  FulfillmentComponent[][] calldata,
                  /**
                   * @custom:name considerationFulfillments
                   */
                  FulfillmentComponent[][] calldata,
                  bytes32 fulfillerConduitKey,
                  uint256 maximumFulfilled
              )
                  external
                  payable
                  override
                  returns (
                      bool[] memory /* availableOrders */,
                      Execution[] memory /* executions */
                  )
              {
                  // Convert orders to "advanced" orders and fulfill all available orders.
                  return
                      _fulfillAvailableAdvancedOrders(
                          _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)(
                              CalldataStart.pptr()
                          ), // Convert to advanced orders.
                          new CriteriaResolver[](0), // No criteria resolvers supplied.
                          _toNestedFulfillmentComponentsReturnType(
                              _decodeNestedFulfillmentComponents
                          )(
                              CalldataStart.pptr(
                                  Offset_fulfillAvailableOrders_offerFulfillments
                              )
                          ),
                          _toNestedFulfillmentComponentsReturnType(
                              _decodeNestedFulfillmentComponents
                          )(
                              CalldataStart.pptr(
                                  Offset_fulfillAvailableOrders_considerationFulfillments
                              )
                          ),
                          fulfillerConduitKey,
                          msg.sender,
                          maximumFulfilled
                      );
              }
              /**
               * @notice Attempt to fill a group of orders, fully or partially, with an
               *         arbitrary number of items for offer and consideration per order
               *         alongside criteria resolvers containing specific token
               *         identifiers and associated proofs. Any order that is not
               *         currently active, has already been fully filled, or has been
               *         cancelled will be omitted. Remaining offer and consideration
               *         items will then be aggregated where possible as indicated by the
               *         supplied offer and consideration component arrays and aggregated
               *         items will be transferred to the fulfiller or to each intended
               *         recipient, respectively. Note that a failing item transfer or an
               *         issue with order formatting will cause the entire batch to fail.
               *
               * @custom:param advancedOrders            The orders to fulfill along with
               *                                         the fraction of those orders to
               *                                         attempt to fill. Note that both
               *                                         the offerer and the fulfiller
               *                                         must first approve this contract
               *                                         (or their conduit if indicated by
               *                                         the order) to transfer any
               *                                         relevant tokens on their behalf
               *                                         and that contracts must implement
               *                                         `onERC1155Received` to receive
               *                                         ERC1155 tokens as consideration.
               *                                         Also note that all offer and
               *                                         consideration components must
               *                                         have no remainder after
               *                                         multiplication of the respective
               *                                         amount with the supplied fraction
               *                                         for an order's partial fill
               *                                         amount to be considered valid.
               * @custom:param criteriaResolvers         An array where each element
               *                                         contains a reference to a
               *                                         specific offer or consideration,
               *                                         a token identifier, and a proof
               *                                         that the supplied token
               *                                         identifier is contained in the
               *                                         merkle root held by the item in
               *                                         question's criteria element. Note
               *                                         that an empty criteria indicates
               *                                         that any (transferable) token
               *                                         identifier on the token in
               *                                         question is valid and that no
               *                                         associated proof needs to be
               *                                         supplied.
               * @custom:param offerFulfillments         An array of FulfillmentComponent
               *                                         arrays indicating which offer
               *                                         items to attempt to aggregate
               *                                         when preparing executions. Note
               *                                         that any offer items not included
               *                                         as part of a fulfillment will be
               *                                         sent unaggregated to the caller.
               * @custom:param considerationFulfillments An array of FulfillmentComponent
               *                                         arrays indicating which
               *                                         consideration items to attempt to
               *                                         aggregate when preparing
               *                                         executions.
               * @param fulfillerConduitKey              A bytes32 value indicating what
               *                                         conduit, if any, to source the
               *                                         fulfiller's token approvals from.
               *                                         The zero hash signifies that no
               *                                         conduit should be used (and
               *                                         direct approvals set on this
               *                                         contract).
               * @param recipient                        The intended recipient for all
               *                                         received items, with `address(0)`
               *                                         indicating that the caller should
               *                                         receive the offer items.
               * @param maximumFulfilled                 The maximum number of orders to
               *                                         fulfill.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders.
               */
              function fulfillAvailableAdvancedOrders(
                  /**
                   * @custom:name advancedOrders
                   */
                  AdvancedOrder[] calldata,
                  /**
                   * @custom:name criteriaResolvers
                   */
                  CriteriaResolver[] calldata,
                  /**
                   * @custom:name offerFulfillments
                   */
                  FulfillmentComponent[][] calldata,
                  /**
                   * @custom:name considerationFulfillments
                   */
                  FulfillmentComponent[][] calldata,
                  bytes32 fulfillerConduitKey,
                  address recipient,
                  uint256 maximumFulfilled
              )
                  external
                  payable
                  override
                  returns (
                      bool[] memory /* availableOrders */,
                      Execution[] memory /* executions */
                  )
              {
                  // Fulfill all available orders.
                  return
                      _fulfillAvailableAdvancedOrders(
                          _toAdvancedOrdersReturnType(_decodeAdvancedOrders)(
                              CalldataStart.pptr()
                          ),
                          _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
                              CalldataStart.pptr(
                                  Offset_fulfillAvailableAdvancedOrders_criteriaResolvers
                              )
                          ),
                          _toNestedFulfillmentComponentsReturnType(
                              _decodeNestedFulfillmentComponents
                          )(
                              CalldataStart.pptr(
                                  Offset_fulfillAvailableAdvancedOrders_offerFulfillments
                              )
                          ),
                          _toNestedFulfillmentComponentsReturnType(
                              _decodeNestedFulfillmentComponents
                          )(
                              CalldataStart.pptr(
                                  Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts
                              )
                          ),
                          fulfillerConduitKey,
                          _substituteCallerForEmptyRecipient(recipient),
                          maximumFulfilled
                      );
              }
              /**
               * @notice Match an arbitrary number of orders, each with an arbitrary
               *         number of items for offer and consideration along with a set of
               *         fulfillments allocating offer components to consideration
               *         components. Note that this function does not support
               *         criteria-based or partial filling of orders (though filling the
               *         remainder of a partially-filled order is supported). Any unspent
               *         offer item amounts or native tokens will be transferred to the
               *         caller.
               *
               * @custom:param orders       The orders to match. Note that both the
               *                            offerer and fulfiller on each order must first
               *                            approve this contract (or their conduit if
               *                            indicated by the order) to transfer any
               *                            relevant tokens on their behalf and each
               *                            consideration recipient must implement
               *                            `onERC1155Received` to receive ERC1155 tokens.
               * @custom:param fulfillments An array of elements allocating offer
               *                            components to consideration components. Note
               *                            that each consideration component must be
               *                            fully met for the match operation to be valid,
               *                            and that any unspent offer items will be sent
               *                            unaggregated to the caller.
               *
               * @return executions An array of elements indicating the sequence of
               *                    transfers performed as part of matching the given
               *                    orders. Note that unspent offer item amounts or native
               *                    tokens will not be reflected as part of this array.
               */
              function matchOrders(
                  /**
                   * @custom:name orders
                   */
                  Order[] calldata,
                  /**
                   * @custom:name fulfillments
                   */
                  Fulfillment[] calldata
              ) external payable override returns (Execution[] memory /* executions */) {
                  // Convert to advanced, validate, and match orders using fulfillments.
                  return
                      _matchAdvancedOrders(
                          _toAdvancedOrdersReturnType(_decodeOrdersAsAdvancedOrders)(
                              CalldataStart.pptr()
                          ),
                          new CriteriaResolver[](0), // No criteria resolvers supplied.
                          _toFulfillmentsReturnType(_decodeFulfillments)(
                              CalldataStart.pptr(Offset_matchOrders_fulfillments)
                          ),
                          msg.sender
                      );
              }
              /**
               * @notice Match an arbitrary number of full, partial, or contract orders,
               *         each with an arbitrary number of items for offer and
               *         consideration, supplying criteria resolvers containing specific
               *         token identifiers and associated proofs as well as fulfillments
               *         allocating offer components to consideration components. Any
               *         unspent offer item amounts will be transferred to the designated
               *         recipient (with the null address signifying to use the caller)
               *         and any unspent native tokens will be returned to the caller.
               *
               * @custom:param advancedOrders    The advanced orders to match. Note that
               *                                 both the offerer and fulfiller on each
               *                                 order must first approve this contract
               *                                 (or their conduit if indicated by the
               *                                 order) to transfer any relevant tokens on
               *                                 their behalf and each consideration
               *                                 recipient must implement
               *                                 `onERC1155Received` to receive ERC1155
               *                                 tokens. Also note that the offer and
               *                                 consideration components for each order
               *                                 must have no remainder after multiplying
               *                                 the respective amount with the supplied
               *                                 fraction for the group of partial fills
               *                                 to be considered valid.
               * @custom:param criteriaResolvers An array where each element contains a
               *                                 reference to a specific offer or
               *                                 consideration, a token identifier, and a
               *                                 proof that the supplied token identifier
               *                                 is contained in the merkle root held by
               *                                 the item in question's criteria element.
               *                                 Note that an empty criteria indicates
               *                                 that any (transferable) token identifier
               *                                 on the token in question is valid and
               *                                 that no associated proof needs to be
               *                                 supplied.
               * @custom:param fulfillments      An array of elements allocating offer
               *                                 components to consideration components.
               *                                 Note that each consideration component
               *                                 must be fully met for the match operation
               *                                 to be valid, and that any unspent offer
               *                                 items will be sent unaggregated to the
               *                                 designated recipient.
               * @param recipient                The intended recipient for all unspent
               *                                 offer item amounts, or the caller if the
               *                                 null address is supplied.
               *
               * @return executions An array of elements indicating the sequence of
               *                     transfers performed as part of matching the given
               *                     orders. Note that unspent offer item amounts or
               *                     native tokens will not be reflected as part of this
               *                     array.
               */
              function matchAdvancedOrders(
                  /**
                   * @custom:name advancedOrders
                   */
                  AdvancedOrder[] calldata,
                  /**
                   * @custom:name criteriaResolvers
                   */
                  CriteriaResolver[] calldata,
                  /**
                   * @custom:name fulfillments
                   */
                  Fulfillment[] calldata,
                  address recipient
              ) external payable override returns (Execution[] memory /* executions */) {
                  // Validate and match the advanced orders using supplied fulfillments.
                  return
                      _matchAdvancedOrders(
                          _toAdvancedOrdersReturnType(_decodeAdvancedOrders)(
                              CalldataStart.pptr()
                          ),
                          _toCriteriaResolversReturnType(_decodeCriteriaResolvers)(
                              CalldataStart.pptr(
                                  Offset_matchAdvancedOrders_criteriaResolvers
                              )
                          ),
                          _toFulfillmentsReturnType(_decodeFulfillments)(
                              CalldataStart.pptr(Offset_matchAdvancedOrders_fulfillments)
                          ),
                          _substituteCallerForEmptyRecipient(recipient)
                      );
              }
              /**
               * @notice Cancel an arbitrary number of orders. Note that only the offerer
               *         or the zone of a given order may cancel it. Callers should ensure
               *         that the intended order was cancelled by calling `getOrderStatus`
               *         and confirming that `isCancelled` returns `true`.
               *
               * @param orders The orders to cancel.
               *
               * @return cancelled A boolean indicating whether the supplied orders have
               *                   been successfully cancelled.
               */
              function cancel(
                  OrderComponents[] calldata orders
              ) external override returns (bool cancelled) {
                  // Cancel the orders.
                  cancelled = _cancel(orders);
              }
              /**
               * @notice Validate an arbitrary number of orders, thereby registering their
               *         signatures as valid and allowing the fulfiller to skip signature
               *         verification on fulfillment. Note that validated orders may still
               *         be unfulfillable due to invalid item amounts or other factors;
               *         callers should determine whether validated orders are fulfillable
               *         by simulating the fulfillment call prior to execution. Also note
               *         that anyone can validate a signed order, but only the offerer can
               *         validate an order without supplying a signature.
               *
               * @custom:param orders The orders to validate.
               *
               * @return validated A boolean indicating whether the supplied orders have
               *                   been successfully validated.
               */
              function validate(
                  /**
                   * @custom:name orders
                   */
                  Order[] calldata
              ) external override returns (bool /* validated */) {
                  return
                      _validate(_toOrdersReturnType(_decodeOrders)(CalldataStart.pptr()));
              }
              /**
               * @notice Cancel all orders from a given offerer with a given zone in bulk
               *         by incrementing a counter. Note that only the offerer may
               *         increment the counter.
               *
               * @return newCounter The new counter.
               */
              function incrementCounter() external override returns (uint256 newCounter) {
                  // Increment current counter for the supplied offerer.  Note that the
                  // counter is incremented by a large, quasi-random interval.
                  newCounter = _incrementCounter();
              }
              /**
               * @notice Retrieve the order hash for a given order.
               *
               * @custom:param order The components of the order.
               *
               * @return orderHash The order hash.
               */
              function getOrderHash(
                  /**
                   * @custom:name order
                   */
                  OrderComponents calldata
              ) external view override returns (bytes32 orderHash) {
                  CalldataPointer orderPointer = CalldataStart.pptr();
                  // Derive order hash by supplying order parameters along with counter.
                  orderHash = _deriveOrderHash(
                      _toOrderParametersReturnType(
                          _decodeOrderComponentsAsOrderParameters
                      )(orderPointer),
                      // Read order counter
                      orderPointer.offset(OrderParameters_counter_offset).readUint256()
                  );
              }
              /**
               * @notice Retrieve the status of a given order by hash, including whether
               *         the order has been cancelled or validated and the fraction of the
               *         order that has been filled. Since the _orderStatus[orderHash]
               *         does not get set for contract orders, getOrderStatus will always
               *         return (false, false, 0, 0) for those hashes. Note that this
               *         function is susceptible to view reentrancy and so should be used
               *         with care when calling from other contracts.
               *
               * @param orderHash The order hash in question.
               *
               * @return isValidated A boolean indicating whether the order in question
               *                     has been validated (i.e. previously approved or
               *                     partially filled).
               * @return isCancelled A boolean indicating whether the order in question
               *                     has been cancelled.
               * @return totalFilled The total portion of the order that has been filled
               *                     (i.e. the "numerator").
               * @return totalSize   The total size of the order that is either filled or
               *                     unfilled (i.e. the "denominator").
               */
              function getOrderStatus(
                  bytes32 orderHash
              )
                  external
                  view
                  override
                  returns (
                      bool isValidated,
                      bool isCancelled,
                      uint256 totalFilled,
                      uint256 totalSize
                  )
              {
                  // Retrieve the order status using the order hash.
                  return _getOrderStatus(orderHash);
              }
              /**
               * @notice Retrieve the current counter for a given offerer.
               *
               * @param offerer The offerer in question.
               *
               * @return counter The current counter.
               */
              function getCounter(
                  address offerer
              ) external view override returns (uint256 counter) {
                  // Return the counter for the supplied offerer.
                  counter = _getCounter(offerer);
              }
              /**
               * @notice Retrieve configuration information for this contract.
               *
               * @return version           The contract version.
               * @return domainSeparator   The domain separator for this contract.
               * @return conduitController The conduit Controller set for this contract.
               */
              function information()
                  external
                  view
                  override
                  returns (
                      string memory version,
                      bytes32 domainSeparator,
                      address conduitController
                  )
              {
                  // Return the information for this contract.
                  return _information();
              }
              /**
               * @dev Gets the contract offerer nonce for the specified contract offerer.
               *      Note that this function is susceptible to view reentrancy and so
               *      should be used with care when calling from other contracts.
               *
               * @param contractOfferer The contract offerer for which to get the nonce.
               *
               * @return nonce The contract offerer nonce.
               */
              function getContractOffererNonce(
                  address contractOfferer
              ) external view override returns (uint256 nonce) {
                  nonce = _contractNonces[contractOfferer];
              }
              /**
               * @notice Retrieve the name of this contract.
               *
               * @return contractName The name of this contract.
               */
              function name()
                  external
                  pure
                  override
                  returns (string memory /* contractName */)
              {
                  // Return the name of the contract.
                  return _name();
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { Side, ItemType, OrderType } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              ConsiderationItem,
              CriteriaResolver,
              Execution,
              Fulfillment,
              FulfillmentComponent,
              OfferItem,
              OrderParameters,
              ReceivedItem
          } from "./ConsiderationStructs.sol";
          import { OrderFulfiller } from "./OrderFulfiller.sol";
          import { FulfillmentApplier } from "./FulfillmentApplier.sol";
          import {
              _revertConsiderationNotMet,
              _revertInsufficientNativeTokensSupplied,
              _revertInvalidNativeOfferItem,
              _revertNoSpecifiedOrdersAvailable
          } from "./ConsiderationErrors.sol";
          import {
              AccumulatorDisarmed,
              ConsiderationItem_recipient_offset,
              Execution_offerer_offset,
              NonMatchSelector_InvalidErrorValue,
              NonMatchSelector_MagicMask,
              OneWord,
              OneWordShift,
              OrdersMatchedTopic0,
              ReceivedItem_amount_offset,
              ReceivedItem_recipient_offset,
              TwoWords
          } from "./ConsiderationConstants.sol";
          /**
           * @title OrderCombiner
           * @author 0age
           * @notice OrderCombiner contains logic for fulfilling combinations of orders,
           *         either by matching offer items to consideration items or by
           *         fulfilling orders where available.
           */
          contract OrderCombiner is OrderFulfiller, FulfillmentApplier {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) OrderFulfiller(conduitController) {}
              /**
               * @notice Internal function to attempt to fill a group of orders, fully or
               *         partially, with an arbitrary number of items for offer and
               *         consideration per order alongside criteria resolvers containing
               *         specific token identifiers and associated proofs. Any order that
               *         is not currently active, has already been fully filled, or has
               *         been cancelled will be omitted. Remaining offer and consideration
               *         items will then be aggregated where possible as indicated by the
               *         supplied offer and consideration component arrays and aggregated
               *         items will be transferred to the fulfiller or to each intended
               *         recipient, respectively. Note that a failing item transfer or an
               *         issue with order formatting will cause the entire batch to fail.
               *
               * @param advancedOrders            The orders to fulfill along with the
               *                                  fraction of those orders to attempt to
               *                                  fill. Note that both the offerer and the
               *                                  fulfiller must first approve this
               *                                  contract (or a conduit if indicated by
               *                                  the order) to transfer any relevant
               *                                  tokens on their behalf and that
               *                                  contracts must implement
               *                                  `onERC1155Received` in order to receive
               *                                  ERC1155 tokens as consideration. Also
               *                                  note that all offer and consideration
               *                                  components must have no remainder after
               *                                  multiplication of the respective amount
               *                                  with the supplied fraction for an
               *                                  order's partial fill amount to be
               *                                  considered valid.
               * @param criteriaResolvers         An array where each element contains a
               *                                  reference to a specific offer or
               *                                  consideration, a token identifier, and a
               *                                  proof that the supplied token identifier
               *                                  is contained in the merkle root held by
               *                                  the item in question's criteria element.
               *                                  Note that an empty criteria indicates
               *                                  that any (transferable) token
               *                                  identifier on the token in question is
               *                                  valid and that no associated proof needs
               *                                  to be supplied.
               * @param offerFulfillments         An array of FulfillmentComponent arrays
               *                                  indicating which offer items to attempt
               *                                  to aggregate when preparing executions.
               * @param considerationFulfillments An array of FulfillmentComponent arrays
               *                                  indicating which consideration items to
               *                                  attempt to aggregate when preparing
               *                                  executions.
               * @param fulfillerConduitKey       A bytes32 value indicating what conduit,
               *                                  if any, to source the fulfiller's token
               *                                  approvals from. The zero hash signifies
               *                                  that no conduit should be used (and
               *                                  direct approvals set on Consideration).
               * @param recipient                 The intended recipient for all received
               *                                  items.
               * @param maximumFulfilled          The maximum number of orders to fulfill.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders.
               */
              function _fulfillAvailableAdvancedOrders(
                  AdvancedOrder[] memory advancedOrders,
                  CriteriaResolver[] memory criteriaResolvers,
                  FulfillmentComponent[][] memory offerFulfillments,
                  FulfillmentComponent[][] memory considerationFulfillments,
                  bytes32 fulfillerConduitKey,
                  address recipient,
                  uint256 maximumFulfilled
              )
                  internal
                  returns (
                      bool[] memory /* availableOrders */,
                      Execution[] memory /* executions */
                  )
              {
                  // Validate orders, apply amounts, & determine if they use conduits.
                  (
                      bytes32[] memory orderHashes,
                      bool containsNonOpen
                  ) = _validateOrdersAndPrepareToFulfill(
                          advancedOrders,
                          criteriaResolvers,
                          false, // Signifies that invalid orders should NOT revert.
                          maximumFulfilled,
                          recipient
                      );
                  // Aggregate used offer and consideration items and execute transfers.
                  return
                      _executeAvailableFulfillments(
                          advancedOrders,
                          offerFulfillments,
                          considerationFulfillments,
                          fulfillerConduitKey,
                          recipient,
                          orderHashes,
                          containsNonOpen
                      );
              }
              /**
               * @dev Internal function to validate a group of orders, update their
               *      statuses, reduce amounts by their previously filled fractions, apply
               *      criteria resolvers, and emit OrderFulfilled events. Note that this
               *      function needs to be called before
               *      _aggregateValidFulfillmentConsiderationItems to set the memory
               *      layout that _aggregateValidFulfillmentConsiderationItems depends on.
               *
               * @param advancedOrders    The advanced orders to validate and reduce by
               *                          their previously filled amounts.
               * @param criteriaResolvers An array where each element contains a reference
               *                          to a specific order as well as that order's
               *                          offer or consideration, a token identifier, and
               *                          a proof that the supplied token identifier is
               *                          contained in the order's merkle root. Note that
               *                          a root of zero indicates that any transferable
               *                          token identifier is valid and that no proof
               *                          needs to be supplied.
               * @param revertOnInvalid   A boolean indicating whether to revert on any
               *                          order being invalid; setting this to false will
               *                          instead cause the invalid order to be skipped.
               * @param maximumFulfilled  The maximum number of orders to fulfill.
               * @param recipient         The intended recipient for all items that do not
               *                          already have a designated recipient and are not
               *                          already used as part of a provided fulfillment.
               *
               * @return orderHashes     The hashes of the orders being fulfilled.
               * @return containsNonOpen A boolean indicating whether any restricted or
               *                         contract orders are present within the provided
               *                         array of advanced orders.
               */
              function _validateOrdersAndPrepareToFulfill(
                  AdvancedOrder[] memory advancedOrders,
                  CriteriaResolver[] memory criteriaResolvers,
                  bool revertOnInvalid,
                  uint256 maximumFulfilled,
                  address recipient
              ) internal returns (bytes32[] memory orderHashes, bool containsNonOpen) {
                  // Ensure this function cannot be triggered during a reentrant call.
                  _setReentrancyGuard(true); // Native tokens accepted during execution.
                  // Declare an error buffer indicating status of any native offer items.
                  // Native tokens may only be provided as part of contract orders or when
                  // fulfilling via matchOrders or matchAdvancedOrders; if bits indicating
                  // these conditions are not met have been set, throw.
                  uint256 invalidNativeOfferItemErrorBuffer;
                  // Use assembly to set the value for the second bit of the error buffer.
                  assembly {
                      /**
                       * Use the 231st bit of the error buffer to indicate whether the
                       * current function is not matchAdvancedOrders or matchOrders.
                       *
                       * sig                                func
                       * -----------------------------------------------------------------
                       * 1010100000010111010001000 0 000100 matchOrders
                       * 1111001011010001001010110 0 010010 matchAdvancedOrders
                       * 1110110110011000101001010 1 110100 fulfillAvailableOrders
                       * 1000011100100000000110110 1 000001 fulfillAvailableAdvancedOrders
                       *                           ^ 7th bit
                       */
                      invalidNativeOfferItemErrorBuffer := and(
                          NonMatchSelector_MagicMask,
                          calldataload(0)
                      )
                  }
                  // Declare variables for later use.
                  AdvancedOrder memory advancedOrder;
                  uint256 terminalMemoryOffset;
                  unchecked {
                      // Read length of orders array and place on the stack.
                      uint256 totalOrders = advancedOrders.length;
                      // Track the order hash for each order being fulfilled.
                      orderHashes = new bytes32[](totalOrders);
                      // Determine the memory offset to terminate on during loops.
                      terminalMemoryOffset = (totalOrders + 1) << OneWordShift;
                  }
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      // Declare inner variables.
                      OfferItem[] memory offer;
                      ConsiderationItem[] memory consideration;
                      // Iterate over each order.
                      for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) {
                          // Retrieve order using assembly to bypass out-of-range check.
                          assembly {
                              advancedOrder := mload(add(advancedOrders, i))
                          }
                          // Determine if max number orders have already been fulfilled.
                          if (maximumFulfilled == 0) {
                              // Mark fill fraction as zero as the order will not be used.
                              advancedOrder.numerator = 0;
                              // Continue iterating through the remaining orders.
                              continue;
                          }
                          // Validate it, update status, and determine fraction to fill.
                          (
                              bytes32 orderHash,
                              uint256 numerator,
                              uint256 denominator
                          ) = _validateOrderAndUpdateStatus(
                                  advancedOrder,
                                  revertOnInvalid
                              );
                          // Do not track hash or adjust prices if order is not fulfilled.
                          if (numerator == 0) {
                              // Mark fill fraction as zero if the order is not fulfilled.
                              advancedOrder.numerator = 0;
                              // Continue iterating through the remaining orders.
                              continue;
                          }
                          // Otherwise, track the order hash in question.
                          assembly {
                              mstore(add(orderHashes, i), orderHash)
                          }
                          // Decrement the number of fulfilled orders.
                          // Skip underflow check as the condition before
                          // implies that maximumFulfilled > 0.
                          --maximumFulfilled;
                          // Place the start time for the order on the stack.
                          uint256 startTime = advancedOrder.parameters.startTime;
                          // Place the end time for the order on the stack.
                          uint256 endTime = advancedOrder.parameters.endTime;
                          // Retrieve array of offer items for the order in question.
                          offer = advancedOrder.parameters.offer;
                          // Read length of offer array and place on the stack.
                          uint256 totalOfferItems = offer.length;
                          {
                              // Determine the order type, used to check for eligibility
                              // for native token offer items as well as for the presence
                              // of restricted and contract orders (or non-open orders).
                              OrderType orderType = advancedOrder.parameters.orderType;
                              // Utilize assembly to efficiently check for order types.
                              // Note that these checks expect that there are no order
                              // types beyond the current set (0-4) and will need to be
                              // modified if more order types are added.
                              assembly {
                                  // Declare a variable indicating if the order is not a
                                  // contract order. Cache in scratch space to avoid stack
                                  // depth errors.
                                  let isNonContract := lt(orderType, 4)
                                  mstore(0, isNonContract)
                                  // Update the variable indicating if the order is not an
                                  // open order, remaining set if it has been set already.
                                  containsNonOpen := or(containsNonOpen, gt(orderType, 1))
                              }
                          }
                          // Iterate over each offer item on the order.
                          for (uint256 j = 0; j < totalOfferItems; ++j) {
                              // Retrieve the offer item.
                              OfferItem memory offerItem = offer[j];
                              // If the offer item is for the native token and the order
                              // type is not a contract order type, set the first bit of
                              // the error buffer to true.
                              assembly {
                                  invalidNativeOfferItemErrorBuffer := or(
                                      invalidNativeOfferItemErrorBuffer,
                                      lt(mload(offerItem), mload(0))
                                  )
                              }
                              // Apply order fill fraction to offer item end amount.
                              uint256 endAmount = _getFraction(
                                  numerator,
                                  denominator,
                                  offerItem.endAmount
                              );
                              // Reuse same fraction if start and end amounts are equal.
                              if (offerItem.startAmount == offerItem.endAmount) {
                                  // Apply derived amount to both start and end amount.
                                  offerItem.startAmount = endAmount;
                              } else {
                                  // Apply order fill fraction to offer item start amount.
                                  offerItem.startAmount = _getFraction(
                                      numerator,
                                      denominator,
                                      offerItem.startAmount
                                  );
                              }
                              // Adjust offer amount using current time; round down.
                              uint256 currentAmount = _locateCurrentAmount(
                                  offerItem.startAmount,
                                  endAmount,
                                  startTime,
                                  endTime,
                                  false // round down
                              );
                              // Update amounts in memory to match the current amount.
                              // Note that the end amount is used to track spent amounts.
                              offerItem.startAmount = currentAmount;
                              offerItem.endAmount = currentAmount;
                          }
                          // Retrieve array of consideration items for order in question.
                          consideration = (advancedOrder.parameters.consideration);
                          // Read length of consideration array and place on the stack.
                          uint256 totalConsiderationItems = consideration.length;
                          // Iterate over each consideration item on the order.
                          for (uint256 j = 0; j < totalConsiderationItems; ++j) {
                              // Retrieve the consideration item.
                              ConsiderationItem memory considerationItem = (
                                  consideration[j]
                              );
                              // Apply fraction to consideration item end amount.
                              uint256 endAmount = _getFraction(
                                  numerator,
                                  denominator,
                                  considerationItem.endAmount
                              );
                              // Reuse same fraction if start and end amounts are equal.
                              if (
                                  considerationItem.startAmount ==
                                  considerationItem.endAmount
                              ) {
                                  // Apply derived amount to both start and end amount.
                                  considerationItem.startAmount = endAmount;
                              } else {
                                  // Apply fraction to consideration item start amount.
                                  considerationItem.startAmount = _getFraction(
                                      numerator,
                                      denominator,
                                      considerationItem.startAmount
                                  );
                              }
                              // Adjust consideration amount using current time; round up.
                              uint256 currentAmount = (
                                  _locateCurrentAmount(
                                      considerationItem.startAmount,
                                      endAmount,
                                      startTime,
                                      endTime,
                                      true // round up
                                  )
                              );
                              considerationItem.startAmount = currentAmount;
                              // Utilize assembly to manually "shift" the recipient value,
                              // then to copy the start amount to the recipient.
                              // Note that this sets up the memory layout that is
                              // subsequently relied upon by
                              // _aggregateValidFulfillmentConsiderationItems.
                              assembly {
                                  // Derive the pointer to the recipient using the item
                                  // pointer along with the offset to the recipient.
                                  let considerationItemRecipientPtr := add(
                                      considerationItem,
                                      ConsiderationItem_recipient_offset // recipient
                                  )
                                  // Write recipient to endAmount, as endAmount is not
                                  // used from this point on and can be repurposed to fit
                                  // the layout of a ReceivedItem.
                                  mstore(
                                      add(
                                          considerationItem,
                                          ReceivedItem_recipient_offset // old endAmount
                                      ),
                                      mload(considerationItemRecipientPtr)
                                  )
                                  // Write startAmount to recipient, as recipient is not
                                  // used from this point on and can be repurposed to
                                  // track received amounts.
                                  mstore(considerationItemRecipientPtr, currentAmount)
                              }
                          }
                      }
                  }
                  // If the first bit is set, a native offer item was encountered on an
                  // order that is not a contract order. If the 231st bit is set in the
                  // error buffer, the current function is not matchOrders or
                  // matchAdvancedOrders. If the value is 1 + (1 << 230), then both the
                  // 1st and 231st bits were set; in that case, revert with an error.
                  if (
                      invalidNativeOfferItemErrorBuffer ==
                      NonMatchSelector_InvalidErrorValue
                  ) {
                      _revertInvalidNativeOfferItem();
                  }
                  // Apply criteria resolvers to each order as applicable.
                  _applyCriteriaResolvers(advancedOrders, criteriaResolvers);
                  // Emit an event for each order signifying that it has been fulfilled.
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      bytes32 orderHash;
                      // Iterate over each order.
                      for (uint256 i = OneWord; i < terminalMemoryOffset; i += OneWord) {
                          assembly {
                              orderHash := mload(add(orderHashes, i))
                          }
                          // Do not emit an event if no order hash is present.
                          if (orderHash == bytes32(0)) {
                              continue;
                          }
                          // Retrieve order using assembly to bypass out-of-range check.
                          assembly {
                              advancedOrder := mload(add(advancedOrders, i))
                          }
                          // Retrieve parameters for the order in question.
                          OrderParameters memory orderParameters = (
                              advancedOrder.parameters
                          );
                          // Emit an OrderFulfilled event.
                          _emitOrderFulfilledEvent(
                              orderHash,
                              orderParameters.offerer,
                              orderParameters.zone,
                              recipient,
                              orderParameters.offer,
                              orderParameters.consideration
                          );
                      }
                  }
              }
              /**
               * @dev Internal function to fulfill a group of validated orders, fully or
               *      partially, with an arbitrary number of items for offer and
               *      consideration per order and to execute transfers. Any order that is
               *      not currently active, has already been fully filled, or has been
               *      cancelled will be omitted. Remaining offer and consideration items
               *      will then be aggregated where possible as indicated by the supplied
               *      offer and consideration component arrays and aggregated items will
               *      be transferred to the fulfiller or to each intended recipient,
               *      respectively. Note that a failing item transfer or an issue with
               *      order formatting will cause the entire batch to fail.
               *
               * @param advancedOrders            The orders to fulfill along with the
               *                                  fraction of those orders to attempt to
               *                                  fill. Note that both the offerer and the
               *                                  fulfiller must first approve this
               *                                  contract (or the conduit if indicated by
               *                                  the order) to transfer any relevant
               *                                  tokens on their behalf and that
               *                                  contracts must implement
               *                                  `onERC1155Received` in order to receive
               *                                  ERC1155 tokens as consideration. Also
               *                                  note that all offer and consideration
               *                                  components must have no remainder after
               *                                  multiplication of the respective amount
               *                                  with the supplied fraction for an
               *                                  order's partial fill amount to be
               *                                  considered valid.
               * @param offerFulfillments         An array of FulfillmentComponent arrays
               *                                  indicating which offer items to attempt
               *                                  to aggregate when preparing executions.
               * @param considerationFulfillments An array of FulfillmentComponent arrays
               *                                  indicating which consideration items to
               *                                  attempt to aggregate when preparing
               *                                  executions.
               * @param fulfillerConduitKey       A bytes32 value indicating what conduit,
               *                                  if any, to source the fulfiller's token
               *                                  approvals from. The zero hash signifies
               *                                  that no conduit should be used, with
               *                                  direct approvals set on Consideration.
               * @param recipient                 The intended recipient for all items
               *                                  that do not already have a designated
               *                                  recipient and are not already used as
               *                                  part of a provided fulfillment.
               * @param orderHashes               An array of order hashes for each order.
               * @param containsNonOpen           A boolean indicating whether any
               *                                  restricted or contract orders are
               *                                  present within the provided array of
               *                                  advanced orders.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders.
               */
              function _executeAvailableFulfillments(
                  AdvancedOrder[] memory advancedOrders,
                  FulfillmentComponent[][] memory offerFulfillments,
                  FulfillmentComponent[][] memory considerationFulfillments,
                  bytes32 fulfillerConduitKey,
                  address recipient,
                  bytes32[] memory orderHashes,
                  bool containsNonOpen
              )
                  internal
                  returns (bool[] memory availableOrders, Execution[] memory executions)
              {
                  // Retrieve length of offer fulfillments array and place on the stack.
                  uint256 totalOfferFulfillments = offerFulfillments.length;
                  // Retrieve length of consideration fulfillments array & place on stack.
                  uint256 totalConsiderationFulfillments = (
                      considerationFulfillments.length
                  );
                  // Allocate an execution for each offer and consideration fulfillment.
                  executions = new Execution[](
                      totalOfferFulfillments + totalConsiderationFulfillments
                  );
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      // Track number of filtered executions.
                      uint256 totalFilteredExecutions = 0;
                      // Iterate over each offer fulfillment.
                      for (uint256 i = 0; i < totalOfferFulfillments; ) {
                          // Derive aggregated execution corresponding with fulfillment.
                          Execution memory execution = _aggregateAvailable(
                              advancedOrders,
                              Side.OFFER,
                              offerFulfillments[i],
                              fulfillerConduitKey,
                              recipient
                          );
                          // If the execution is filterable...
                          if (_isFilterableExecution(execution)) {
                              // Increment total filtered executions.
                              ++totalFilteredExecutions;
                          } else {
                              // Otherwise, assign the execution to the executions array.
                              executions[i - totalFilteredExecutions] = execution;
                          }
                          // Increment iterator.
                          ++i;
                      }
                      // Iterate over each consideration fulfillment.
                      for (uint256 i = 0; i < totalConsiderationFulfillments; ) {
                          // Derive aggregated execution corresponding with fulfillment.
                          Execution memory execution = _aggregateAvailable(
                              advancedOrders,
                              Side.CONSIDERATION,
                              considerationFulfillments[i],
                              fulfillerConduitKey,
                              address(0) // unused
                          );
                          // If the execution is filterable...
                          if (_isFilterableExecution(execution)) {
                              // Increment total filtered executions.
                              ++totalFilteredExecutions;
                          } else {
                              // Otherwise, assign the execution to the executions array.
                              executions[
                                  i + totalOfferFulfillments - totalFilteredExecutions
                              ] = execution;
                          }
                          // Increment iterator.
                          ++i;
                      }
                      // If some number of executions have been filtered...
                      if (totalFilteredExecutions != 0) {
                          // reduce the total length of the executions array.
                          assembly {
                              mstore(
                                  executions,
                                  sub(mload(executions), totalFilteredExecutions)
                              )
                          }
                      }
                  }
                  // Revert if no orders are available.
                  if (executions.length == 0) {
                      _revertNoSpecifiedOrdersAvailable();
                  }
                  // Perform final checks and return.
                  availableOrders = _performFinalChecksAndExecuteOrders(
                      advancedOrders,
                      executions,
                      orderHashes,
                      recipient,
                      containsNonOpen
                  );
                  return (availableOrders, executions);
              }
              /**
               * @dev Internal function to perform a final check that each consideration
               *      item for an arbitrary number of fulfilled orders has been met and to
               *      trigger associated executions, transferring the respective items.
               *
               * @param advancedOrders  The orders to check and perform executions for.
               * @param executions      An array of elements indicating the sequence of
               *                        transfers to perform when fulfilling the given
               *                        orders.
               * @param orderHashes     An array of order hashes for each order.
               * @param recipient       The intended recipient for all items that do not
               *                        already have a designated recipient and are not
               *                        used as part of a provided fulfillment.
               * @param containsNonOpen A boolean indicating whether any restricted or
               *                        contract orders are present within the provided
               *                        array of advanced orders.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               */
              function _performFinalChecksAndExecuteOrders(
                  AdvancedOrder[] memory advancedOrders,
                  Execution[] memory executions,
                  bytes32[] memory orderHashes,
                  address recipient,
                  bool containsNonOpen
              ) internal returns (bool[] memory /* availableOrders */) {
                  // Retrieve the length of the advanced orders array and place on stack.
                  uint256 totalOrders = advancedOrders.length;
                  // Initialize array for tracking available orders.
                  bool[] memory availableOrders = new bool[](totalOrders);
                  // Initialize an accumulator array. From this point forward, no new
                  // memory regions can be safely allocated until the accumulator is no
                  // longer being utilized, as the accumulator operates in an open-ended
                  // fashion from this memory pointer; existing memory may still be
                  // accessed and modified, however.
                  bytes memory accumulator = new bytes(AccumulatorDisarmed);
                  {
                      // Declare a variable for the available native token balance.
                      uint256 nativeTokenBalance;
                      // Retrieve the length of the executions array and place on stack.
                      uint256 totalExecutions = executions.length;
                      // Iterate over each execution.
                      for (uint256 i = 0; i < totalExecutions; ) {
                          // Retrieve the execution and the associated received item.
                          Execution memory execution = executions[i];
                          ReceivedItem memory item = execution.item;
                          // If execution transfers native tokens, reduce value available.
                          if (item.itemType == ItemType.NATIVE) {
                              // Get the current available balance of native tokens.
                              assembly {
                                  nativeTokenBalance := selfbalance()
                              }
                              // Ensure that sufficient native tokens are still available.
                              if (item.amount > nativeTokenBalance) {
                                  _revertInsufficientNativeTokensSupplied();
                              }
                          }
                          // Transfer the item specified by the execution.
                          _transfer(
                              item,
                              execution.offerer,
                              execution.conduitKey,
                              accumulator
                          );
                          // Skip overflow check as for loop is indexed starting at zero.
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      // Iterate over each order.
                      for (uint256 i = 0; i < totalOrders; ++i) {
                          // Retrieve the order in question.
                          AdvancedOrder memory advancedOrder = advancedOrders[i];
                          // Skip the order in question if not being not fulfilled.
                          if (advancedOrder.numerator == 0) {
                              // Explicitly set availableOrders at the given index to
                              // guard against the possibility of dirtied memory.
                              availableOrders[i] = false;
                              continue;
                          }
                          // Mark the order as available.
                          availableOrders[i] = true;
                          // Retrieve the order parameters.
                          OrderParameters memory parameters = advancedOrder.parameters;
                          {
                              // Retrieve offer items.
                              OfferItem[] memory offer = parameters.offer;
                              // Read length of offer array & place on the stack.
                              uint256 totalOfferItems = offer.length;
                              // Iterate over each offer item to restore it.
                              for (uint256 j = 0; j < totalOfferItems; ++j) {
                                  // Retrieve the offer item in question.
                                  OfferItem memory offerItem = offer[j];
                                  // Transfer to recipient if unspent amount is not zero.
                                  // Note that the transfer will not be reflected in the
                                  // executions array.
                                  if (offerItem.startAmount != 0) {
                                      // Replace the endAmount parameter with the recipient to
                                      // make offerItem compatible with the ReceivedItem input
                                      // to _transfer and cache the original endAmount so it
                                      // can be restored after the transfer.
                                      uint256 originalEndAmount = _replaceEndAmountWithRecipient(
                                              offerItem,
                                              recipient
                                          );
                                      // Transfer excess offer item amount to recipient.
                                      _toOfferItemInput(_transfer)(
                                          offerItem,
                                          parameters.offerer,
                                          parameters.conduitKey,
                                          accumulator
                                      );
                                      // Restore the original endAmount in offerItem.
                                      assembly {
                                          mstore(
                                              add(
                                                  offerItem,
                                                  ReceivedItem_recipient_offset
                                              ),
                                              originalEndAmount
                                          )
                                      }
                                  }
                                  // Restore original amount on the offer item.
                                  offerItem.startAmount = offerItem.endAmount;
                              }
                          }
                          {
                              // Read consideration items & ensure they are fulfilled.
                              ConsiderationItem[] memory consideration = (
                                  parameters.consideration
                              );
                              // Read length of consideration array & place on stack.
                              uint256 totalConsiderationItems = consideration.length;
                              // Iterate over each consideration item.
                              for (uint256 j = 0; j < totalConsiderationItems; ++j) {
                                  ConsiderationItem memory considerationItem = (
                                      consideration[j]
                                  );
                                  // Retrieve remaining amount on consideration item.
                                  uint256 unmetAmount = considerationItem.startAmount;
                                  // Revert if the remaining amount is not zero.
                                  if (unmetAmount != 0) {
                                      _revertConsiderationNotMet(i, j, unmetAmount);
                                  }
                                  // Utilize assembly to restore the original value.
                                  assembly {
                                      // Write recipient to startAmount.
                                      mstore(
                                          add(
                                              considerationItem,
                                              ReceivedItem_amount_offset
                                          ),
                                          mload(
                                              add(
                                                  considerationItem,
                                                  ConsiderationItem_recipient_offset
                                              )
                                          )
                                      )
                                  }
                              }
                          }
                      }
                  }
                  // Trigger any accumulated transfers via call to the conduit.
                  _triggerIfArmed(accumulator);
                  // Determine whether any native token balance remains.
                  uint256 remainingNativeTokenBalance;
                  assembly {
                      remainingNativeTokenBalance := selfbalance()
                  }
                  // Return any remaining native token balance to the caller.
                  if (remainingNativeTokenBalance != 0) {
                      _transferNativeTokens(
                          payable(msg.sender),
                          remainingNativeTokenBalance
                      );
                  }
                  // If any restricted or contract orders are present in the group of
                  // orders being fulfilled, perform any validateOrder or ratifyOrder
                  // calls after all executions and related transfers are complete.
                  if (containsNonOpen) {
                      // Iterate over each order a second time.
                      for (uint256 i = 0; i < totalOrders; ) {
                          // Ensure the order in question is being fulfilled.
                          if (availableOrders[i]) {
                              // Check restricted orders and contract orders.
                              _assertRestrictedAdvancedOrderValidity(
                                  advancedOrders[i],
                                  orderHashes,
                                  orderHashes[i]
                              );
                          }
                          // Skip overflow checks as for loop is indexed starting at zero.
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  // Clear the reentrancy guard.
                  _clearReentrancyGuard();
                  // Return the array containing available orders.
                  return availableOrders;
              }
              /**
               * @dev Internal function to emit an OrdersMatched event using the same
               *      memory region as the existing order hash array.
               *
               * @param orderHashes An array of order hashes to include as an argument for
               *                    the OrdersMatched event.
               */
              function _emitOrdersMatched(bytes32[] memory orderHashes) internal {
                  assembly {
                      // Load the array length from memory.
                      let length := mload(orderHashes)
                      // Get the full size of the event data - one word for the offset,
                      // one for the array length and one per hash.
                      let dataSize := add(TwoWords, shl(OneWordShift, length))
                      // Get pointer to start of data, reusing word before array length
                      // for the offset.
                      let dataPointer := sub(orderHashes, OneWord)
                      // Cache the existing word in memory at the offset pointer.
                      let cache := mload(dataPointer)
                      // Write an offset of 32.
                      mstore(dataPointer, OneWord)
                      // Emit the OrdersMatched event.
                      log1(dataPointer, dataSize, OrdersMatchedTopic0)
                      // Restore the cached word.
                      mstore(dataPointer, cache)
                  }
              }
              /**
               * @dev Internal function to match an arbitrary number of full or partial
               *      orders, each with an arbitrary number of items for offer and
               *      consideration, supplying criteria resolvers containing specific
               *      token identifiers and associated proofs as well as fulfillments
               *      allocating offer components to consideration components.
               *
               * @param advancedOrders    The advanced orders to match. Note that both the
               *                          offerer and fulfiller on each order must first
               *                          approve this contract (or their conduit if
               *                          indicated by the order) to transfer any relevant
               *                          tokens on their behalf and each consideration
               *                          recipient must implement `onERC1155Received` in
               *                          order to receive ERC1155 tokens. Also note that
               *                          the offer and consideration components for each
               *                          order must have no remainder after multiplying
               *                          the respective amount with the supplied fraction
               *                          in order for the group of partial fills to be
               *                          considered valid.
               * @param criteriaResolvers An array where each element contains a reference
               *                          to a specific order as well as that order's
               *                          offer or consideration, a token identifier, and
               *                          a proof that the supplied token identifier is
               *                          contained in the order's merkle root. Note that
               *                          an empty root indicates that any (transferable)
               *                          token identifier is valid and that no associated
               *                          proof needs to be supplied.
               * @param fulfillments      An array of elements allocating offer components
               *                          to consideration components. Note that each
               *                          consideration component must be fully met in
               *                          order for the match operation to be valid.
               * @param recipient         The intended recipient for all unspent offer
               *                          item amounts.
               *
               * @return executions An array of elements indicating the sequence of
               *                    transfers performed as part of matching the given
               *                    orders.
               */
              function _matchAdvancedOrders(
                  AdvancedOrder[] memory advancedOrders,
                  CriteriaResolver[] memory criteriaResolvers,
                  Fulfillment[] memory fulfillments,
                  address recipient
              ) internal returns (Execution[] memory /* executions */) {
                  // Validate orders, update order status, and determine item amounts.
                  (
                      bytes32[] memory orderHashes,
                      bool containsNonOpen
                  ) = _validateOrdersAndPrepareToFulfill(
                          advancedOrders,
                          criteriaResolvers,
                          true, // Signifies that invalid orders should revert.
                          advancedOrders.length,
                          recipient
                      );
                  // Emit OrdersMatched event, providing an array of matched order hashes.
                  _emitOrdersMatched(orderHashes);
                  // Fulfill the orders using the supplied fulfillments and recipient.
                  return
                      _fulfillAdvancedOrders(
                          advancedOrders,
                          fulfillments,
                          orderHashes,
                          recipient,
                          containsNonOpen
                      );
              }
              /**
               * @dev Internal function to fulfill an arbitrary number of orders, either
               *      full or partial, after validating, adjusting amounts, and applying
               *      criteria resolvers.
               *
               * @param advancedOrders  The orders to match, including a fraction to
               *                        attempt to fill for each order.
               * @param fulfillments    An array of elements allocating offer components
               *                        to consideration components. Note that the final
               *                        amount of each consideration component must be
               *                        zero for a match operation to be considered valid.
               * @param orderHashes     An array of order hashes for each order.
               * @param recipient       The intended recipient for all items that do not
               *                        already have a designated recipient and are not
               *                        used as part of a provided fulfillment.
               * @param containsNonOpen A boolean indicating whether any restricted or
               *                        contract orders are present within the provided
               *                        array of advanced orders.
               *
               * @return executions An array of elements indicating the sequence of
               *                    transfers performed as part of matching the given
               *                    orders.
               */
              function _fulfillAdvancedOrders(
                  AdvancedOrder[] memory advancedOrders,
                  Fulfillment[] memory fulfillments,
                  bytes32[] memory orderHashes,
                  address recipient,
                  bool containsNonOpen
              ) internal returns (Execution[] memory executions) {
                  // Retrieve fulfillments array length and place on the stack.
                  uint256 totalFulfillments = fulfillments.length;
                  // Allocate executions by fulfillment and apply them to each execution.
                  executions = new Execution[](totalFulfillments);
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      // Track number of filtered executions.
                      uint256 totalFilteredExecutions = 0;
                      // Iterate over each fulfillment.
                      for (uint256 i = 0; i < totalFulfillments; ++i) {
                          /// Retrieve the fulfillment in question.
                          Fulfillment memory fulfillment = fulfillments[i];
                          // Derive the execution corresponding with the fulfillment.
                          Execution memory execution = _applyFulfillment(
                              advancedOrders,
                              fulfillment.offerComponents,
                              fulfillment.considerationComponents,
                              i
                          );
                          // If the execution is filterable...
                          if (_isFilterableExecution(execution)) {
                              // Increment total filtered executions.
                              ++totalFilteredExecutions;
                          } else {
                              // Otherwise, assign the execution to the executions array.
                              executions[i - totalFilteredExecutions] = execution;
                          }
                      }
                      // If some number of executions have been filtered...
                      if (totalFilteredExecutions != 0) {
                          // reduce the total length of the executions array.
                          assembly {
                              mstore(
                                  executions,
                                  sub(mload(executions), totalFilteredExecutions)
                              )
                          }
                      }
                  }
                  // Perform final checks and execute orders.
                  _performFinalChecksAndExecuteOrders(
                      advancedOrders,
                      executions,
                      orderHashes,
                      recipient,
                      containsNonOpen
                  );
                  // Return the executions array.
                  return executions;
              }
              /**
               * @dev Internal pure function to determine whether a given execution is
               *      filterable and may be removed from the executions array. The offerer
               *      and the recipient must be the same address and the item type cannot
               *      indicate a native token transfer.
               *
               * @param execution The execution to check for filterability.
               *
               * @return filterable A boolean indicating whether the execution in question
               *                    can be filtered from the executions array.
               */
              function _isFilterableExecution(
                  Execution memory execution
              ) internal pure returns (bool filterable) {
                  // Utilize assembly to efficiently determine if execution is filterable.
                  assembly {
                      // Retrieve the received item referenced by the execution.
                      let item := mload(execution)
                      // Determine whether the execution is filterable.
                      filterable := and(
                          // Determine if offerer and recipient are the same address.
                          eq(
                              // Retrieve the recipient's address from the received item.
                              mload(add(item, ReceivedItem_recipient_offset)),
                              // Retrieve the offerer's address from the execution.
                              mload(add(execution, Execution_offerer_offset))
                          ),
                          // Determine if received item's item type is non-zero, thereby
                          // indicating that the execution does not involve native tokens.
                          iszero(iszero(mload(item)))
                      )
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /*
           * -------------------------- Disambiguation & Other Notes ---------------------
           *    - The term "head" is used as it is in the documentation for ABI encoding,
           *      but only in reference to dynamic types, i.e. it always refers to the
           *      offset or pointer to the body of a dynamic type. In calldata, the head
           *      is always an offset (relative to the parent object), while in memory,
           *      the head is always the pointer to the body. More information found here:
           *      https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding
           *        - Note that the length of an array is separate from and precedes the
           *          head of the array.
           *
           *    - The term "body" is used in place of the term "head" used in the ABI
           *      documentation. It refers to the start of the data for a dynamic type,
           *      e.g. the first word of a struct or the first word of the first element
           *      in an array.
           *
           *    - The term "pointer" is used to describe the absolute position of a value
           *      and never an offset relative to another value.
           *        - The suffix "_ptr" refers to a memory pointer.
           *        - The suffix "_cdPtr" refers to a calldata pointer.
           *
           *    - The term "offset" is used to describe the position of a value relative
           *      to some parent value. For example, OrderParameters_conduit_offset is the
           *      offset to the "conduit" value in the OrderParameters struct relative to
           *      the start of the body.
           *        - Note: Offsets are used to derive pointers.
           *
           *    - Some structs have pointers defined for all of their fields in this file.
           *      Lines which are commented out are fields that are not used in the
           *      codebase but have been left in for readability.
           */
          // Declare constants for name, version, and reentrancy sentinel values.
          // Name is right padded, so it touches the length which is left padded. This
          // enables writing both values at once. Length goes at byte 95 in memory, and
          // name fills bytes 96-109, so both values can be written left-padded to 77.
          uint256 constant NameLengthPtr = 0x4D;
          uint256 constant NameWithLength = 0x0d436F6E73696465726174696F6E;
          uint256 constant information_version_offset = 0;
          uint256 constant information_version_cd_offset = 0x60;
          uint256 constant information_domainSeparator_offset = 0x20;
          uint256 constant information_conduitController_offset = 0x40;
          uint256 constant information_versionLengthPtr = 0x63;
          uint256 constant information_versionWithLength = 0x03312e35; // 1.5
          uint256 constant information_length = 0xa0;
          uint256 constant _NOT_ENTERED = 1;
          uint256 constant _ENTERED = 2;
          uint256 constant _ENTERED_AND_ACCEPTING_NATIVE_TOKENS = 3;
          uint256 constant Offset_fulfillAdvancedOrder_criteriaResolvers = 0x20;
          uint256 constant Offset_fulfillAvailableOrders_offerFulfillments = 0x20;
          uint256 constant Offset_fulfillAvailableOrders_considerationFulfillments = 0x40;
          uint256 constant Offset_fulfillAvailableAdvancedOrders_criteriaResolvers = 0x20;
          uint256 constant Offset_fulfillAvailableAdvancedOrders_offerFulfillments = 0x40;
          uint256 constant Offset_fulfillAvailableAdvancedOrders_cnsdrationFlflmnts = (
              0x60
          );
          uint256 constant Offset_matchOrders_fulfillments = 0x20;
          uint256 constant Offset_matchAdvancedOrders_criteriaResolvers = 0x20;
          uint256 constant Offset_matchAdvancedOrders_fulfillments = 0x40;
          // Common Offsets
          // Offsets for identically positioned fields shared by:
          // OfferItem, ConsiderationItem, SpentItem, ReceivedItem
          uint256 constant Selector_length = 0x4;
          uint256 constant Common_token_offset = 0x20;
          uint256 constant Common_identifier_offset = 0x40;
          uint256 constant Common_amount_offset = 0x60;
          uint256 constant Common_endAmount_offset = 0x80;
          uint256 constant SpentItem_size = 0x80;
          uint256 constant SpentItem_size_shift = 0x7;
          uint256 constant OfferItem_size = 0xa0;
          uint256 constant OfferItem_size_with_length = 0xc0;
          uint256 constant ReceivedItem_size_excluding_recipient = 0x80;
          uint256 constant ReceivedItem_size = 0xa0;
          uint256 constant ReceivedItem_amount_offset = 0x60;
          uint256 constant ReceivedItem_recipient_offset = 0x80;
          uint256 constant ReceivedItem_CommonParams_size = 0x60;
          uint256 constant ConsiderationItem_size = 0xc0;
          uint256 constant ConsiderationItem_size_with_length = 0xe0;
          uint256 constant ConsiderationItem_recipient_offset = 0xa0;
          // Store the same constant in an abbreviated format for a line length fix.
          uint256 constant ConsiderItem_recipient_offset = 0xa0;
          uint256 constant Execution_offerer_offset = 0x20;
          uint256 constant Execution_conduit_offset = 0x40;
          // uint256 constant OrderParameters_offerer_offset = 0x00;
          uint256 constant OrderParameters_zone_offset = 0x20;
          uint256 constant OrderParameters_offer_head_offset = 0x40;
          uint256 constant OrderParameters_consideration_head_offset = 0x60;
          // uint256 constant OrderParameters_orderType_offset = 0x80;
          uint256 constant OrderParameters_startTime_offset = 0xa0;
          uint256 constant OrderParameters_endTime_offset = 0xc0;
          uint256 constant OrderParameters_zoneHash_offset = 0xe0;
          // uint256 constant OrderParameters_salt_offset = 0x100;
          uint256 constant OrderParameters_conduit_offset = 0x120;
          uint256 constant OrderParameters_counter_offset = 0x140;
          uint256 constant Fulfillment_itemIndex_offset = 0x20;
          uint256 constant AdvancedOrder_head_size = 0xa0;
          uint256 constant AdvancedOrder_numerator_offset = 0x20;
          uint256 constant AdvancedOrder_denominator_offset = 0x40;
          uint256 constant AdvancedOrder_signature_offset = 0x60;
          uint256 constant AdvancedOrder_extraData_offset = 0x80;
          uint256 constant OrderStatus_ValidatedAndNotCancelled = 1;
          uint256 constant OrderStatus_filledNumerator_offset = 0x10;
          uint256 constant OrderStatus_filledDenominator_offset = 0x88;
          uint256 constant ThirtyOneBytes = 0x1f;
          uint256 constant OneWord = 0x20;
          uint256 constant TwoWords = 0x40;
          uint256 constant ThreeWords = 0x60;
          uint256 constant FourWords = 0x80;
          uint256 constant FiveWords = 0xa0;
          uint256 constant OneWordShift = 0x5;
          uint256 constant TwoWordsShift = 0x6;
          uint256 constant SixtyThreeBytes = 0x3f;
          uint256 constant OnlyFullWordMask = 0xffffffe0;
          uint256 constant FreeMemoryPointerSlot = 0x40;
          uint256 constant ZeroSlot = 0x60;
          uint256 constant DefaultFreeMemoryPointer = 0x80;
          uint256 constant Slot0x80 = 0x80;
          uint256 constant Slot0xA0 = 0xa0;
          // uint256 constant BasicOrder_endAmount_cdPtr = 0x104;
          uint256 constant BasicOrder_common_params_size = 0xa0;
          uint256 constant BasicOrder_considerationHashesArray_ptr = 0x160;
          uint256 constant BasicOrder_receivedItemByteMap = (
              0x0000010102030000000000000000000000000000000000000000000000000000
          );
          uint256 constant BasicOrder_offeredItemByteMap = (
              0x0203020301010000000000000000000000000000000000000000000000000000
          );
          bytes32 constant OrdersMatchedTopic0 = (
              0x4b9f2d36e1b4c93de62cc077b00b1a91d84b6c31b4a14e012718dcca230689e7
          );
          uint256 constant EIP712_Order_size = 0x180;
          uint256 constant EIP712_OfferItem_size = 0xc0;
          uint256 constant EIP712_ConsiderationItem_size = 0xe0;
          uint256 constant AdditionalRecipient_size = 0x40;
          uint256 constant AdditionalRecipient_size_shift = 0x6;
          uint256 constant EIP712_DomainSeparator_offset = 0x02;
          uint256 constant EIP712_OrderHash_offset = 0x22;
          uint256 constant EIP712_DigestPayload_size = 0x42;
          uint256 constant EIP712_domainData_nameHash_offset = 0x20;
          uint256 constant EIP712_domainData_versionHash_offset = 0x40;
          uint256 constant EIP712_domainData_chainId_offset = 0x60;
          uint256 constant EIP712_domainData_verifyingContract_offset = 0x80;
          uint256 constant EIP712_domainData_size = 0xa0;
          // Minimum BulkOrder proof size: 64 bytes for signature + 3 for key + 32 for 1
          // sibling. Maximum BulkOrder proof size: 65 bytes for signature + 3 for key +
          // 768 for 24 siblings.
          uint256 constant BulkOrderProof_minSize = 0x63;
          uint256 constant BulkOrderProof_rangeSize = 0x2e2;
          uint256 constant BulkOrderProof_lengthAdjustmentBeforeMask = 0x1d;
          uint256 constant BulkOrderProof_lengthRangeAfterMask = 0x2;
          uint256 constant BulkOrderProof_keyShift = 0xe8;
          uint256 constant BulkOrderProof_keySize = 0x3;
          uint256 constant BulkOrder_Typehash_Height_One = (
              0x3ca2711d29384747a8f61d60aad3c450405f7aaff5613541dee28df2d6986d32
          );
          uint256 constant BulkOrder_Typehash_Height_Two = (
              0xbf8e29b89f29ed9b529c154a63038ffca562f8d7cd1e2545dda53a1b582dde30
          );
          uint256 constant BulkOrder_Typehash_Height_Three = (
              0x53c6f6856e13104584dd0797ca2b2779202dc2597c6066a42e0d8fe990b0024d
          );
          uint256 constant BulkOrder_Typehash_Height_Four = (
              0xa02eb7ff164c884e5e2c336dc85f81c6a93329d8e9adf214b32729b894de2af1
          );
          uint256 constant BulkOrder_Typehash_Height_Five = (
              0x39c9d33c18e050dda0aeb9a8086fb16fc12d5d64536780e1da7405a800b0b9f6
          );
          uint256 constant BulkOrder_Typehash_Height_Six = (
              0x1c19f71958cdd8f081b4c31f7caf5c010b29d12950be2fa1c95070dc47e30b55
          );
          uint256 constant BulkOrder_Typehash_Height_Seven = (
              0xca74fab2fece9a1d58234a274220ad05ca096a92ef6a1ca1750b9d90c948955c
          );
          uint256 constant BulkOrder_Typehash_Height_Eight = (
              0x7ff98d9d4e55d876c5cfac10b43c04039522f3ddfb0ea9bfe70c68cfb5c7cc14
          );
          uint256 constant BulkOrder_Typehash_Height_Nine = (
              0xbed7be92d41c56f9e59ac7a6272185299b815ddfabc3f25deb51fe55fe2f9e8a
          );
          uint256 constant BulkOrder_Typehash_Height_Ten = (
              0xd1d97d1ef5eaa37a4ee5fbf234e6f6d64eb511eb562221cd7edfbdde0848da05
          );
          uint256 constant BulkOrder_Typehash_Height_Eleven = (
              0x896c3f349c4da741c19b37fec49ed2e44d738e775a21d9c9860a69d67a3dae53
          );
          uint256 constant BulkOrder_Typehash_Height_Twelve = (
              0xbb98d87cc12922b83759626c5f07d72266da9702d19ffad6a514c73a89002f5f
          );
          uint256 constant BulkOrder_Typehash_Height_Thirteen = (
              0xe6ae19322608dd1f8a8d56aab48ed9c28be489b689f4b6c91268563efc85f20e
          );
          uint256 constant BulkOrder_Typehash_Height_Fourteen = (
              0x6b5b04cbae4fcb1a9d78e7b2dfc51a36933d023cf6e347e03d517b472a852590
          );
          uint256 constant BulkOrder_Typehash_Height_Fifteen = (
              0xd1eb68309202b7106b891e109739dbbd334a1817fe5d6202c939e75cf5e35ca9
          );
          uint256 constant BulkOrder_Typehash_Height_Sixteen = (
              0x1da3eed3ecef6ebaa6e5023c057ec2c75150693fd0dac5c90f4a142f9879fde8
          );
          uint256 constant BulkOrder_Typehash_Height_Seventeen = (
              0xeee9a1392aa395c7002308119a58f2582777a75e54e0c1d5d5437bd2e8bf6222
          );
          uint256 constant BulkOrder_Typehash_Height_Eighteen = (
              0xc3939feff011e53ab8c35ca3370aad54c5df1fc2938cd62543174fa6e7d85877
          );
          uint256 constant BulkOrder_Typehash_Height_Nineteen = (
              0x0efca7572ac20f5ae84db0e2940674f7eca0a4726fa1060ffc2d18cef54b203d
          );
          uint256 constant BulkOrder_Typehash_Height_Twenty = (
              0x5a4f867d3d458dabecad65f6201ceeaba0096df2d0c491cc32e6ea4e64350017
          );
          uint256 constant BulkOrder_Typehash_Height_TwentyOne = (
              0x80987079d291feebf21c2230e69add0f283cee0b8be492ca8050b4185a2ff719
          );
          uint256 constant BulkOrder_Typehash_Height_TwentyTwo = (
              0x3bd8cff538aba49a9c374c806d277181e9651624b3e31111bc0624574f8bca1d
          );
          uint256 constant BulkOrder_Typehash_Height_TwentyThree = (
              0x5d6a3f098a0bc373f808c619b1bb4028208721b3c4f8d6bc8a874d659814eb76
          );
          uint256 constant BulkOrder_Typehash_Height_TwentyFour = (
              0x1d51df90cba8de7637ca3e8fe1e3511d1dc2f23487d05dbdecb781860c21ac1c
          );
          uint256 constant receivedItemsHash_ptr = 0x60;
          /*
           *  Memory layout in _prepareBasicFulfillmentFromCalldata of
           *  data for OrderFulfilled
           *
           *   event OrderFulfilled(
           *     bytes32 orderHash,
           *     address indexed offerer,
           *     address indexed zone,
           *     address fulfiller,
           *     SpentItem[] offer,
           *       > (itemType, token, id, amount)
           *     ReceivedItem[] consideration
           *       > (itemType, token, id, amount, recipient)
           *   )
           *
           *  - 0x00: orderHash
           *  - 0x20: fulfiller
           *  - 0x40: offer offset (0x80)
           *  - 0x60: consideration offset (0x120)
           *  - 0x80: offer.length (1)
           *  - 0xa0: offerItemType
           *  - 0xc0: offerToken
           *  - 0xe0: offerIdentifier
           *  - 0x100: offerAmount
           *  - 0x120: consideration.length (1 + additionalRecipients.length)
           *  - 0x140: considerationItemType
           *  - 0x160: considerationToken
           *  - 0x180: considerationIdentifier
           *  - 0x1a0: considerationAmount
           *  - 0x1c0: considerationRecipient
           *  - ...
           */
          // Minimum length of the OrderFulfilled event data.
          // Must be added to the size of the ReceivedItem array for additionalRecipients
          // (0xa0 * additionalRecipients.length) to calculate full size of the buffer.
          uint256 constant OrderFulfilled_baseSize = 0x1e0;
          uint256 constant OrderFulfilled_selector = (
              0x9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31
          );
          // Minimum offset in memory to OrderFulfilled event data.
          // Must be added to the size of the EIP712 hash array for additionalRecipients
          // (32 * additionalRecipients.length) to calculate the pointer to event data.
          uint256 constant OrderFulfilled_baseOffset = 0x180;
          uint256 constant OrderFulfilled_consideration_length_baseOffset = 0x2a0;
          uint256 constant OrderFulfilled_offer_length_baseOffset = 0x200;
          // Related constants used for restricted order checks on basic orders.
          uint256 constant OrderFulfilled_baseDataSize = 0x160;
          // uint256 constant ValidateOrder_offerDataOffset = 0x184;
          // uint256 constant RatifyOrder_offerDataOffset = 0xc4;
          // uint256 constant OrderFulfilled_orderHash_offset = 0x00;
          uint256 constant OrderFulfilled_fulfiller_offset = 0x20;
          uint256 constant OrderFulfilled_offer_head_offset = 0x40;
          uint256 constant OrderFulfilled_offer_body_offset = 0x80;
          uint256 constant OrderFulfilled_consideration_head_offset = 0x60;
          uint256 constant OrderFulfilled_consideration_body_offset = 0x120;
          // BasicOrderParameters
          uint256 constant BasicOrder_parameters_cdPtr = 0x04;
          uint256 constant BasicOrder_considerationToken_cdPtr = 0x24;
          uint256 constant BasicOrder_considerationIdentifier_cdPtr = 0x44;
          uint256 constant BasicOrder_considerationAmount_cdPtr = 0x64;
          uint256 constant BasicOrder_offerer_cdPtr = 0x84;
          uint256 constant BasicOrder_zone_cdPtr = 0xa4;
          uint256 constant BasicOrder_offerToken_cdPtr = 0xc4;
          uint256 constant BasicOrder_offerIdentifier_cdPtr = 0xe4;
          uint256 constant BasicOrder_offerAmount_cdPtr = 0x104;
          uint256 constant BasicOrder_basicOrderType_cdPtr = 0x124;
          uint256 constant BasicOrder_startTime_cdPtr = 0x144;
          uint256 constant BasicOrder_endTime_cdPtr = 0x164;
          // uint256 constant BasicOrder_zoneHash_cdPtr = 0x184;
          // uint256 constant BasicOrder_salt_cdPtr = 0x1a4;
          uint256 constant BasicOrder_offererConduit_cdPtr = 0x1c4;
          uint256 constant BasicOrder_fulfillerConduit_cdPtr = 0x1e4;
          uint256 constant BasicOrder_totalOriginalAdditionalRecipients_cdPtr = 0x204;
          uint256 constant BasicOrder_additionalRecipients_head_cdPtr = 0x224;
          uint256 constant BasicOrder_signature_cdPtr = 0x244;
          uint256 constant BasicOrder_additionalRecipients_length_cdPtr = 0x264;
          uint256 constant BasicOrder_additionalRecipients_data_cdPtr = 0x284;
          uint256 constant BasicOrder_parameters_ptr = 0x20;
          uint256 constant BasicOrder_basicOrderType_range = 0x18; // 24 values
          /*
           *  Memory layout in _prepareBasicFulfillmentFromCalldata of
           *  EIP712 data for ConsiderationItem
           *   - 0x80: ConsiderationItem EIP-712 typehash (constant)
           *   - 0xa0: itemType
           *   - 0xc0: token
           *   - 0xe0: identifier
           *   - 0x100: startAmount
           *   - 0x120: endAmount
           *   - 0x140: recipient
           */
          uint256 constant BasicOrder_considerationItem_typeHash_ptr = 0x80; // memoryPtr
          uint256 constant BasicOrder_considerationItem_itemType_ptr = 0xa0;
          uint256 constant BasicOrder_considerationItem_token_ptr = 0xc0;
          uint256 constant BasicOrder_considerationItem_identifier_ptr = 0xe0;
          uint256 constant BasicOrder_considerationItem_startAmount_ptr = 0x100;
          uint256 constant BasicOrder_considerationItem_endAmount_ptr = 0x120;
          // uint256 constant BasicOrder_considerationItem_recipient_ptr = 0x140;
          /*
           *  Memory layout in _prepareBasicFulfillmentFromCalldata of
           *  EIP712 data for OfferItem
           *   - 0x80:  OfferItem EIP-712 typehash (constant)
           *   - 0xa0:  itemType
           *   - 0xc0:  token
           *   - 0xe0:  identifier (reused for offeredItemsHash)
           *   - 0x100: startAmount
           *   - 0x120: endAmount
           */
          uint256 constant BasicOrder_offerItem_typeHash_ptr = 0x80;
          uint256 constant BasicOrder_offerItem_itemType_ptr = 0xa0;
          uint256 constant BasicOrder_offerItem_token_ptr = 0xc0;
          // uint256 constant BasicOrder_offerItem_identifier_ptr = 0xe0;
          // uint256 constant BasicOrder_offerItem_startAmount_ptr = 0x100;
          uint256 constant BasicOrder_offerItem_endAmount_ptr = 0x120;
          /*
           *  Memory layout in _prepareBasicFulfillmentFromCalldata of
           *  EIP712 data for Order
           *   - 0x80:   Order EIP-712 typehash (constant)
           *   - 0xa0:   orderParameters.offerer
           *   - 0xc0:   orderParameters.zone
           *   - 0xe0:   keccak256(abi.encodePacked(offerHashes))
           *   - 0x100:  keccak256(abi.encodePacked(considerationHashes))
           *   - 0x120:  orderType
           *   - 0x140:  startTime
           *   - 0x160:  endTime
           *   - 0x180:  zoneHash
           *   - 0x1a0:  salt
           *   - 0x1c0:  conduit
           *   - 0x1e0:  _counters[orderParameters.offerer] (from storage)
           */
          uint256 constant BasicOrder_order_typeHash_ptr = 0x80;
          uint256 constant BasicOrder_order_offerer_ptr = 0xa0;
          // uint256 constant BasicOrder_order_zone_ptr = 0xc0;
          uint256 constant BasicOrder_order_offerHashes_ptr = 0xe0;
          uint256 constant BasicOrder_order_considerationHashes_ptr = 0x100;
          uint256 constant BasicOrder_order_orderType_ptr = 0x120;
          uint256 constant BasicOrder_order_startTime_ptr = 0x140;
          // uint256 constant BasicOrder_order_endTime_ptr = 0x160;
          // uint256 constant BasicOrder_order_zoneHash_ptr = 0x180;
          // uint256 constant BasicOrder_order_salt_ptr = 0x1a0;
          // uint256 constant BasicOrder_order_conduitKey_ptr = 0x1c0;
          uint256 constant BasicOrder_order_counter_ptr = 0x1e0;
          uint256 constant BasicOrder_additionalRecipients_head_ptr = 0x240;
          uint256 constant BasicOrder_signature_ptr = 0x260;
          uint256 constant BasicOrder_startTimeThroughZoneHash_size = 0x60;
          uint256 constant ContractOrder_orderHash_offerer_shift = 0x60;
          uint256 constant Counter_blockhash_shift = 0x80;
          // Signature-related
          bytes32 constant EIP2098_allButHighestBitMask = (
              0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
          );
          bytes32 constant ECDSA_twentySeventhAndTwentyEighthBytesSet = (
              0x0000000000000000000000000000000000000000000000000000000101000000
          );
          uint256 constant ECDSA_MaxLength = 65;
          uint256 constant ECDSA_signature_s_offset = 0x40;
          uint256 constant ECDSA_signature_v_offset = 0x60;
          bytes32 constant EIP1271_isValidSignature_selector = (
              0x1626ba7e00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant EIP1271_isValidSignature_digest_negativeOffset = 0x40;
          uint256 constant EIP1271_isValidSignature_selector_negativeOffset = 0x44;
          uint256 constant EIP1271_isValidSignature_calldata_baseLength = 0x64;
          uint256 constant EIP1271_isValidSignature_signature_head_offset = 0x40;
          uint256 constant EIP_712_PREFIX = (
              0x1901000000000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ExtraGasBuffer = 0x20;
          uint256 constant CostPerWord = 0x3;
          uint256 constant MemoryExpansionCoefficientShift = 0x9;
          uint256 constant Create2AddressDerivation_ptr = 0x0b;
          uint256 constant Create2AddressDerivation_length = 0x55;
          uint256 constant MaskOverByteTwelve = (
              0x0000000000000000000000ff0000000000000000000000000000000000000000
          );
          uint256 constant MaskOverLastTwentyBytes = (
              0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
          );
          uint256 constant AddressDirtyUpperBitThreshold = (
              0x0000000000000000000000010000000000000000000000000000000000000000
          );
          uint256 constant MaskOverFirstFourBytes = (
              0xffffffff00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant Conduit_execute_signature = (
              0x4ce34aa200000000000000000000000000000000000000000000000000000000
          );
          uint256 constant MaxUint8 = 0xff;
          uint256 constant MaxUint120 = 0xffffffffffffffffffffffffffffff;
          uint256 constant Conduit_execute_ConduitTransfer_ptr = 0x20;
          uint256 constant Conduit_execute_ConduitTransfer_length = 0x01;
          uint256 constant Conduit_execute_ConduitTransfer_offset_ptr = 0x04;
          uint256 constant Conduit_execute_ConduitTransfer_length_ptr = 0x24;
          uint256 constant Conduit_execute_transferItemType_ptr = 0x44;
          uint256 constant Conduit_execute_transferToken_ptr = 0x64;
          uint256 constant Conduit_execute_transferFrom_ptr = 0x84;
          uint256 constant Conduit_execute_transferTo_ptr = 0xa4;
          uint256 constant Conduit_execute_transferIdentifier_ptr = 0xc4;
          uint256 constant Conduit_execute_transferAmount_ptr = 0xe4;
          uint256 constant OneConduitExecute_size = 0x104;
          // Sentinel value to indicate that the conduit accumulator is not armed.
          uint256 constant AccumulatorDisarmed = 0x20;
          uint256 constant AccumulatorArmed = 0x40;
          uint256 constant Accumulator_conduitKey_ptr = 0x20;
          uint256 constant Accumulator_selector_ptr = 0x40;
          uint256 constant Accumulator_array_offset_ptr = 0x44;
          uint256 constant Accumulator_array_length_ptr = 0x64;
          uint256 constant Accumulator_itemSizeOffsetDifference = 0x3c;
          uint256 constant Accumulator_array_offset = 0x20;
          uint256 constant Conduit_transferItem_size = 0xc0;
          uint256 constant Conduit_transferItem_token_ptr = 0x20;
          uint256 constant Conduit_transferItem_from_ptr = 0x40;
          uint256 constant Conduit_transferItem_to_ptr = 0x60;
          uint256 constant Conduit_transferItem_identifier_ptr = 0x80;
          uint256 constant Conduit_transferItem_amount_ptr = 0xa0;
          uint256 constant Ecrecover_precompile = 0x1;
          uint256 constant Ecrecover_args_size = 0x80;
          uint256 constant Signature_lower_v = 27;
          // Bitmask that only gives a non-zero value if masked with a non-match selector.
          uint256 constant NonMatchSelector_MagicMask = (
              0x4000000000000000000000000000000000000000000000000000000000
          );
          // First bit indicates that a NATIVE offer items has been used and the 231st bit
          // indicates that a non match selector has been called.
          uint256 constant NonMatchSelector_InvalidErrorValue = (
              0x4000000000000000000000000000000000000000000000000000000001
          );
          /**
           * @dev Selector and offsets for generateOrder
           *
           * function generateOrder(
           *   address fulfiller,
           *   SpentItem[] calldata minimumReceived,
           *   SpentItem[] calldata maximumSpent,
           *   bytes calldata context
           * )
           */
          uint256 constant generateOrder_selector = 0x98919765;
          uint256 constant generateOrder_selector_offset = 0x1c;
          uint256 constant generateOrder_head_offset = 0x04;
          uint256 constant generateOrder_minimumReceived_head_offset = 0x20;
          uint256 constant generateOrder_maximumSpent_head_offset = 0x40;
          uint256 constant generateOrder_context_head_offset = 0x60;
          uint256 constant generateOrder_base_tail_offset = 0x80;
          uint256 constant generateOrder_maximum_returndatasize = 0xffff;
          uint256 constant ratifyOrder_selector = 0xf4dd92ce;
          uint256 constant ratifyOrder_selector_offset = 0x1c;
          uint256 constant ratifyOrder_head_offset = 0x04;
          // uint256 constant ratifyOrder_offer_head_offset = 0x00;
          uint256 constant ratifyOrder_consideration_head_offset = 0x20;
          uint256 constant ratifyOrder_context_head_offset = 0x40;
          uint256 constant ratifyOrder_orderHashes_head_offset = 0x60;
          uint256 constant ratifyOrder_contractNonce_offset = 0x80;
          uint256 constant ratifyOrder_base_tail_offset = 0xa0;
          uint256 constant validateOrder_selector = 0x17b1f942;
          uint256 constant validateOrder_selector_offset = 0x1c;
          uint256 constant validateOrder_head_offset = 0x04;
          uint256 constant validateOrder_zoneParameters_offset = 0x20;
          // uint256 constant ZoneParameters_orderHash_offset = 0x00;
          uint256 constant ZoneParameters_fulfiller_offset = 0x20;
          uint256 constant ZoneParameters_offerer_offset = 0x40;
          uint256 constant ZoneParameters_offer_head_offset = 0x60;
          uint256 constant ZoneParameters_consideration_head_offset = 0x80;
          uint256 constant ZoneParameters_extraData_head_offset = 0xa0;
          uint256 constant ZoneParameters_orderHashes_head_offset = 0xc0;
          uint256 constant ZoneParameters_startTime_offset = 0xe0;
          uint256 constant ZoneParameters_endTime_offset = 0x100;
          uint256 constant ZoneParameters_zoneHash_offset = 0x120;
          uint256 constant ZoneParameters_base_tail_offset = 0x140;
          uint256 constant ZoneParameters_selectorAndPointer_length = 0x24;
          uint256 constant ZoneParameters_basicOrderFixedElements_length = 0x64;
          // ConsiderationDecoder Constants
          uint256 constant OrderParameters_head_size = 0x0160;
          uint256 constant OrderParameters_totalOriginalConsiderationItems_offset = (
              0x0140
          );
          uint256 constant AdvancedOrderPlusOrderParameters_head_size = 0x0200;
          uint256 constant Order_signature_offset = 0x20;
          uint256 constant Order_head_size = 0x40;
          uint256 constant AdvancedOrder_fixed_segment_0 = 0x40;
          uint256 constant CriteriaResolver_head_size = 0xa0;
          uint256 constant CriteriaResolver_fixed_segment_0 = 0x80;
          uint256 constant CriteriaResolver_criteriaProof_offset = 0x80;
          uint256 constant FulfillmentComponent_mem_tail_size = 0x40;
          uint256 constant FulfillmentComponent_mem_tail_size_shift = 0x6;
          uint256 constant Fulfillment_head_size = 0x40;
          uint256 constant Fulfillment_considerationComponents_offset = 0x20;
          uint256 constant OrderComponents_OrderParameters_common_head_size = 0x0140;
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {
              AdvancedOrder,
              BasicOrderParameters,
              CriteriaResolver,
              Execution,
              Fulfillment,
              FulfillmentComponent,
              Order,
              OrderComponents
          } from "../lib/ConsiderationStructs.sol";
          /**
           * @title ConsiderationInterface
           * @author 0age
           * @custom:version 1.5
           * @notice Consideration is a generalized native token/ERC20/ERC721/ERC1155
           *         marketplace. It minimizes external calls to the greatest extent
           *         possible and provides lightweight methods for common routes as well
           *         as more flexible methods for composing advanced orders.
           *
           * @dev ConsiderationInterface contains all external function interfaces for
           *      Consideration.
           */
          interface ConsiderationInterface {
              /**
               * @notice Fulfill an order offering an ERC721 token by supplying Ether (or
               *         the native token for the given chain) as consideration for the
               *         order. An arbitrary number of "additional recipients" may also be
               *         supplied which will each receive native tokens from the fulfiller
               *         as consideration.
               *
               * @param parameters Additional information on the fulfilled order. Note
               *                   that the offerer must first approve this contract (or
               *                   their preferred conduit if indicated by the order) for
               *                   their offered ERC721 token to be transferred.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillBasicOrder(
                  BasicOrderParameters calldata parameters
              ) external payable returns (bool fulfilled);
              /**
               * @notice Fulfill an order with an arbitrary number of items for offer and
               *         consideration. Note that this function does not support
               *         criteria-based orders or partial filling of orders (though
               *         filling the remainder of a partially-filled order is supported).
               *
               * @param order               The order to fulfill. Note that both the
               *                            offerer and the fulfiller must first approve
               *                            this contract (or the corresponding conduit if
               *                            indicated) to transfer any relevant tokens on
               *                            their behalf and that contracts must implement
               *                            `onERC1155Received` to receive ERC1155 tokens
               *                            as consideration.
               * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
               *                            any, to source the fulfiller's token approvals
               *                            from. The zero hash signifies that no conduit
               *                            should be used, with direct approvals set on
               *                            Consideration.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillOrder(
                  Order calldata order,
                  bytes32 fulfillerConduitKey
              ) external payable returns (bool fulfilled);
              /**
               * @notice Fill an order, fully or partially, with an arbitrary number of
               *         items for offer and consideration alongside criteria resolvers
               *         containing specific token identifiers and associated proofs.
               *
               * @param advancedOrder       The order to fulfill along with the fraction
               *                            of the order to attempt to fill. Note that
               *                            both the offerer and the fulfiller must first
               *                            approve this contract (or their preferred
               *                            conduit if indicated by the order) to transfer
               *                            any relevant tokens on their behalf and that
               *                            contracts must implement `onERC1155Received`
               *                            to receive ERC1155 tokens as consideration.
               *                            Also note that all offer and consideration
               *                            components must have no remainder after
               *                            multiplication of the respective amount with
               *                            the supplied fraction for the partial fill to
               *                            be considered valid.
               * @param criteriaResolvers   An array where each element contains a
               *                            reference to a specific offer or
               *                            consideration, a token identifier, and a proof
               *                            that the supplied token identifier is
               *                            contained in the merkle root held by the item
               *                            in question's criteria element. Note that an
               *                            empty criteria indicates that any
               *                            (transferable) token identifier on the token
               *                            in question is valid and that no associated
               *                            proof needs to be supplied.
               * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
               *                            any, to source the fulfiller's token approvals
               *                            from. The zero hash signifies that no conduit
               *                            should be used, with direct approvals set on
               *                            Consideration.
               * @param recipient           The intended recipient for all received items,
               *                            with `address(0)` indicating that the caller
               *                            should receive the items.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillAdvancedOrder(
                  AdvancedOrder calldata advancedOrder,
                  CriteriaResolver[] calldata criteriaResolvers,
                  bytes32 fulfillerConduitKey,
                  address recipient
              ) external payable returns (bool fulfilled);
              /**
               * @notice Attempt to fill a group of orders, each with an arbitrary number
               *         of items for offer and consideration. Any order that is not
               *         currently active, has already been fully filled, or has been
               *         cancelled will be omitted. Remaining offer and consideration
               *         items will then be aggregated where possible as indicated by the
               *         supplied offer and consideration component arrays and aggregated
               *         items will be transferred to the fulfiller or to each intended
               *         recipient, respectively. Note that a failing item transfer or an
               *         issue with order formatting will cause the entire batch to fail.
               *         Note that this function does not support criteria-based orders or
               *         partial filling of orders (though filling the remainder of a
               *         partially-filled order is supported).
               *
               * @param orders                    The orders to fulfill. Note that both
               *                                  the offerer and the fulfiller must first
               *                                  approve this contract (or the
               *                                  corresponding conduit if indicated) to
               *                                  transfer any relevant tokens on their
               *                                  behalf and that contracts must implement
               *                                  `onERC1155Received` to receive ERC1155
               *                                  tokens as consideration.
               * @param offerFulfillments         An array of FulfillmentComponent arrays
               *                                  indicating which offer items to attempt
               *                                  to aggregate when preparing executions.
               * @param considerationFulfillments An array of FulfillmentComponent arrays
               *                                  indicating which consideration items to
               *                                  attempt to aggregate when preparing
               *                                  executions.
               * @param fulfillerConduitKey       A bytes32 value indicating what conduit,
               *                                  if any, to source the fulfiller's token
               *                                  approvals from. The zero hash signifies
               *                                  that no conduit should be used, with
               *                                  direct approvals set on this contract.
               * @param maximumFulfilled          The maximum number of orders to fulfill.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders. Note that unspent offer item amounts or
               *                         native tokens will not be reflected as part of
               *                         this array.
               */
              function fulfillAvailableOrders(
                  Order[] calldata orders,
                  FulfillmentComponent[][] calldata offerFulfillments,
                  FulfillmentComponent[][] calldata considerationFulfillments,
                  bytes32 fulfillerConduitKey,
                  uint256 maximumFulfilled
              )
                  external
                  payable
                  returns (bool[] memory availableOrders, Execution[] memory executions);
              /**
               * @notice Attempt to fill a group of orders, fully or partially, with an
               *         arbitrary number of items for offer and consideration per order
               *         alongside criteria resolvers containing specific token
               *         identifiers and associated proofs. Any order that is not
               *         currently active, has already been fully filled, or has been
               *         cancelled will be omitted. Remaining offer and consideration
               *         items will then be aggregated where possible as indicated by the
               *         supplied offer and consideration component arrays and aggregated
               *         items will be transferred to the fulfiller or to each intended
               *         recipient, respectively. Note that a failing item transfer or an
               *         issue with order formatting will cause the entire batch to fail.
               *
               * @param advancedOrders            The orders to fulfill along with the
               *                                  fraction of those orders to attempt to
               *                                  fill. Note that both the offerer and the
               *                                  fulfiller must first approve this
               *                                  contract (or their preferred conduit if
               *                                  indicated by the order) to transfer any
               *                                  relevant tokens on their behalf and that
               *                                  contracts must implement
               *                                  `onERC1155Received` to enable receipt of
               *                                  ERC1155 tokens as consideration. Also
               *                                  note that all offer and consideration
               *                                  components must have no remainder after
               *                                  multiplication of the respective amount
               *                                  with the supplied fraction for an
               *                                  order's partial fill amount to be
               *                                  considered valid.
               * @param criteriaResolvers         An array where each element contains a
               *                                  reference to a specific offer or
               *                                  consideration, a token identifier, and a
               *                                  proof that the supplied token identifier
               *                                  is contained in the merkle root held by
               *                                  the item in question's criteria element.
               *                                  Note that an empty criteria indicates
               *                                  that any (transferable) token
               *                                  identifier on the token in question is
               *                                  valid and that no associated proof needs
               *                                  to be supplied.
               * @param offerFulfillments         An array of FulfillmentComponent arrays
               *                                  indicating which offer items to attempt
               *                                  to aggregate when preparing executions.
               * @param considerationFulfillments An array of FulfillmentComponent arrays
               *                                  indicating which consideration items to
               *                                  attempt to aggregate when preparing
               *                                  executions.
               * @param fulfillerConduitKey       A bytes32 value indicating what conduit,
               *                                  if any, to source the fulfiller's token
               *                                  approvals from. The zero hash signifies
               *                                  that no conduit should be used, with
               *                                  direct approvals set on this contract.
               * @param recipient                 The intended recipient for all received
               *                                  items, with `address(0)` indicating that
               *                                  the caller should receive the items.
               * @param maximumFulfilled          The maximum number of orders to fulfill.
               *
               * @return availableOrders An array of booleans indicating if each order
               *                         with an index corresponding to the index of the
               *                         returned boolean was fulfillable or not.
               * @return executions      An array of elements indicating the sequence of
               *                         transfers performed as part of matching the given
               *                         orders. Note that unspent offer item amounts or
               *                         native tokens will not be reflected as part of
               *                         this array.
               */
              function fulfillAvailableAdvancedOrders(
                  AdvancedOrder[] calldata advancedOrders,
                  CriteriaResolver[] calldata criteriaResolvers,
                  FulfillmentComponent[][] calldata offerFulfillments,
                  FulfillmentComponent[][] calldata considerationFulfillments,
                  bytes32 fulfillerConduitKey,
                  address recipient,
                  uint256 maximumFulfilled
              )
                  external
                  payable
                  returns (bool[] memory availableOrders, Execution[] memory executions);
              /**
               * @notice Match an arbitrary number of orders, each with an arbitrary
               *         number of items for offer and consideration along with a set of
               *         fulfillments allocating offer components to consideration
               *         components. Note that this function does not support
               *         criteria-based or partial filling of orders (though filling the
               *         remainder of a partially-filled order is supported). Any unspent
               *         offer item amounts or native tokens will be transferred to the
               *         caller.
               *
               * @param orders       The orders to match. Note that both the offerer and
               *                     fulfiller on each order must first approve this
               *                     contract (or their conduit if indicated by the order)
               *                     to transfer any relevant tokens on their behalf and
               *                     each consideration recipient must implement
               *                     `onERC1155Received` to enable ERC1155 token receipt.
               * @param fulfillments An array of elements allocating offer components to
               *                     consideration components. Note that each
               *                     consideration component must be fully met for the
               *                     match operation to be valid.
               *
               * @return executions An array of elements indicating the sequence of
               *                    transfers performed as part of matching the given
               *                    orders. Note that unspent offer item amounts or
               *                    native tokens will not be reflected as part of this
               *                    array.
               */
              function matchOrders(
                  Order[] calldata orders,
                  Fulfillment[] calldata fulfillments
              ) external payable returns (Execution[] memory executions);
              /**
               * @notice Match an arbitrary number of full or partial orders, each with an
               *         arbitrary number of items for offer and consideration, supplying
               *         criteria resolvers containing specific token identifiers and
               *         associated proofs as well as fulfillments allocating offer
               *         components to consideration components. Any unspent offer item
               *         amounts will be transferred to the designated recipient (with the
               *         null address signifying to use the caller) and any unspent native
               *         tokens will be returned to the caller.
               *
               * @param orders            The advanced orders to match. Note that both the
               *                          offerer and fulfiller on each order must first
               *                          approve this contract (or a preferred conduit if
               *                          indicated by the order) to transfer any relevant
               *                          tokens on their behalf and each consideration
               *                          recipient must implement `onERC1155Received` in
               *                          order to receive ERC1155 tokens. Also note that
               *                          the offer and consideration components for each
               *                          order must have no remainder after multiplying
               *                          the respective amount with the supplied fraction
               *                          in order for the group of partial fills to be
               *                          considered valid.
               * @param criteriaResolvers An array where each element contains a reference
               *                          to a specific order as well as that order's
               *                          offer or consideration, a token identifier, and
               *                          a proof that the supplied token identifier is
               *                          contained in the order's merkle root. Note that
               *                          an empty root indicates that any (transferable)
               *                          token identifier is valid and that no associated
               *                          proof needs to be supplied.
               * @param fulfillments      An array of elements allocating offer components
               *                          to consideration components. Note that each
               *                          consideration component must be fully met in
               *                          order for the match operation to be valid.
               * @param recipient         The intended recipient for all unspent offer
               *                          item amounts, or the caller if the null address
               *                          is supplied.
               *
               * @return executions An array of elements indicating the sequence of
               *                    transfers performed as part of matching the given
               *                    orders. Note that unspent offer item amounts or native
               *                    tokens will not be reflected as part of this array.
               */
              function matchAdvancedOrders(
                  AdvancedOrder[] calldata orders,
                  CriteriaResolver[] calldata criteriaResolvers,
                  Fulfillment[] calldata fulfillments,
                  address recipient
              ) external payable returns (Execution[] memory executions);
              /**
               * @notice Cancel an arbitrary number of orders. Note that only the offerer
               *         or the zone of a given order may cancel it. Callers should ensure
               *         that the intended order was cancelled by calling `getOrderStatus`
               *         and confirming that `isCancelled` returns `true`.
               *
               * @param orders The orders to cancel.
               *
               * @return cancelled A boolean indicating whether the supplied orders have
               *                   been successfully cancelled.
               */
              function cancel(
                  OrderComponents[] calldata orders
              ) external returns (bool cancelled);
              /**
               * @notice Validate an arbitrary number of orders, thereby registering their
               *         signatures as valid and allowing the fulfiller to skip signature
               *         verification on fulfillment. Note that validated orders may still
               *         be unfulfillable due to invalid item amounts or other factors;
               *         callers should determine whether validated orders are fulfillable
               *         by simulating the fulfillment call prior to execution. Also note
               *         that anyone can validate a signed order, but only the offerer can
               *         validate an order without supplying a signature.
               *
               * @param orders The orders to validate.
               *
               * @return validated A boolean indicating whether the supplied orders have
               *                   been successfully validated.
               */
              function validate(
                  Order[] calldata orders
              ) external returns (bool validated);
              /**
               * @notice Cancel all orders from a given offerer with a given zone in bulk
               *         by incrementing a counter. Note that only the offerer may
               *         increment the counter.
               *
               * @return newCounter The new counter.
               */
              function incrementCounter() external returns (uint256 newCounter);
              /**
               * @notice Fulfill an order offering an ERC721 token by supplying Ether (or
               *         the native token for the given chain) as consideration for the
               *         order. An arbitrary number of "additional recipients" may also be
               *         supplied which will each receive native tokens from the fulfiller
               *         as consideration. Note that this function costs less gas than
               *         `fulfillBasicOrder` due to the zero bytes in the function
               *         selector (0x00000000) which also results in earlier function
               *         dispatch.
               *
               * @param parameters Additional information on the fulfilled order. Note
               *                   that the offerer must first approve this contract (or
               *                   their preferred conduit if indicated by the order) for
               *                   their offered ERC721 token to be transferred.
               *
               * @return fulfilled A boolean indicating whether the order has been
               *                   successfully fulfilled.
               */
              function fulfillBasicOrder_efficient_6GL6yc(
                  BasicOrderParameters calldata parameters
              ) external payable returns (bool fulfilled);
              /**
               * @notice Retrieve the order hash for a given order.
               *
               * @param order The components of the order.
               *
               * @return orderHash The order hash.
               */
              function getOrderHash(
                  OrderComponents calldata order
              ) external view returns (bytes32 orderHash);
              /**
               * @notice Retrieve the status of a given order by hash, including whether
               *         the order has been cancelled or validated and the fraction of the
               *         order that has been filled.
               *
               * @param orderHash The order hash in question.
               *
               * @return isValidated A boolean indicating whether the order in question
               *                     has been validated (i.e. previously approved or
               *                     partially filled).
               * @return isCancelled A boolean indicating whether the order in question
               *                     has been cancelled.
               * @return totalFilled The total portion of the order that has been filled
               *                     (i.e. the "numerator").
               * @return totalSize   The total size of the order that is either filled or
               *                     unfilled (i.e. the "denominator").
               */
              function getOrderStatus(
                  bytes32 orderHash
              )
                  external
                  view
                  returns (
                      bool isValidated,
                      bool isCancelled,
                      uint256 totalFilled,
                      uint256 totalSize
                  );
              /**
               * @notice Retrieve the current counter for a given offerer.
               *
               * @param offerer The offerer in question.
               *
               * @return counter The current counter.
               */
              function getCounter(
                  address offerer
              ) external view returns (uint256 counter);
              /**
               * @notice Retrieve configuration information for this contract.
               *
               * @return version           The contract version.
               * @return domainSeparator   The domain separator for this contract.
               * @return conduitController The conduit Controller set for this contract.
               */
              function information()
                  external
                  view
                  returns (
                      string memory version,
                      bytes32 domainSeparator,
                      address conduitController
                  );
              function getContractOffererNonce(
                  address contractOfferer
              ) external view returns (uint256 nonce);
              /**
               * @notice Retrieve the name of this contract.
               *
               * @return contractName The name of this contract.
               */
              function name() external view returns (string memory contractName);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          type CalldataPointer is uint256;
          type ReturndataPointer is uint256;
          type MemoryPointer is uint256;
          using CalldataPointerLib for CalldataPointer global;
          using MemoryPointerLib for MemoryPointer global;
          using ReturndataPointerLib for ReturndataPointer global;
          using CalldataReaders for CalldataPointer global;
          using ReturndataReaders for ReturndataPointer global;
          using MemoryReaders for MemoryPointer global;
          using MemoryWriters for MemoryPointer global;
          CalldataPointer constant CalldataStart = CalldataPointer.wrap(0x04);
          MemoryPointer constant FreeMemoryPPtr = MemoryPointer.wrap(0x40);
          uint256 constant IdentityPrecompileAddress = 0x4;
          uint256 constant OffsetOrLengthMask = 0xffffffff;
          uint256 constant _OneWord = 0x20;
          uint256 constant _FreeMemoryPointerSlot = 0x40;
          /// @dev Allocates `size` bytes in memory by increasing the free memory pointer
          ///    and returns the memory pointer to the first byte of the allocated region.
          // (Free functions cannot have visibility.)
          // solhint-disable-next-line func-visibility
          function malloc(uint256 size) pure returns (MemoryPointer mPtr) {
              assembly {
                  mPtr := mload(_FreeMemoryPointerSlot)
                  mstore(_FreeMemoryPointerSlot, add(mPtr, size))
              }
          }
          // (Free functions cannot have visibility.)
          // solhint-disable-next-line func-visibility
          function getFreeMemoryPointer() pure returns (MemoryPointer mPtr) {
              mPtr = FreeMemoryPPtr.readMemoryPointer();
          }
          // (Free functions cannot have visibility.)
          // solhint-disable-next-line func-visibility
          function setFreeMemoryPointer(MemoryPointer mPtr) pure {
              FreeMemoryPPtr.write(mPtr);
          }
          library CalldataPointerLib {
              function lt(
                  CalldataPointer a,
                  CalldataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := lt(a, b)
                  }
              }
              function gt(
                  CalldataPointer a,
                  CalldataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := gt(a, b)
                  }
              }
              function eq(
                  CalldataPointer a,
                  CalldataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := eq(a, b)
                  }
              }
              function isNull(CalldataPointer a) internal pure returns (bool b) {
                  assembly {
                      b := iszero(a)
                  }
              }
              /// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata.
              ///      pointer `cdPtr` must point to some parent object with a dynamic
              ///      type's head stored at `cdPtr + headOffset`.
              function pptr(
                  CalldataPointer cdPtr,
                  uint256 headOffset
              ) internal pure returns (CalldataPointer cdPtrChild) {
                  cdPtrChild = cdPtr.offset(
                      cdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
                  );
              }
              /// @dev Resolves an offset stored at `cdPtr` to a calldata pointer.
              ///      `cdPtr` must point to some parent object with a dynamic type as its
              ///      first member, e.g. `struct { bytes data; }`
              function pptr(
                  CalldataPointer cdPtr
              ) internal pure returns (CalldataPointer cdPtrChild) {
                  cdPtrChild = cdPtr.offset(cdPtr.readUint256() & OffsetOrLengthMask);
              }
              /// @dev Returns the calldata pointer one word after `cdPtr`.
              function next(
                  CalldataPointer cdPtr
              ) internal pure returns (CalldataPointer cdPtrNext) {
                  assembly {
                      cdPtrNext := add(cdPtr, _OneWord)
                  }
              }
              /// @dev Returns the calldata pointer `_offset` bytes after `cdPtr`.
              function offset(
                  CalldataPointer cdPtr,
                  uint256 _offset
              ) internal pure returns (CalldataPointer cdPtrNext) {
                  assembly {
                      cdPtrNext := add(cdPtr, _offset)
                  }
              }
              /// @dev Copies `size` bytes from calldata starting at `src` to memory at
              ///      `dst`.
              function copy(
                  CalldataPointer src,
                  MemoryPointer dst,
                  uint256 size
              ) internal pure {
                  assembly {
                      calldatacopy(dst, src, size)
                  }
              }
          }
          library ReturndataPointerLib {
              function lt(
                  ReturndataPointer a,
                  ReturndataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := lt(a, b)
                  }
              }
              function gt(
                  ReturndataPointer a,
                  ReturndataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := gt(a, b)
                  }
              }
              function eq(
                  ReturndataPointer a,
                  ReturndataPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := eq(a, b)
                  }
              }
              function isNull(ReturndataPointer a) internal pure returns (bool b) {
                  assembly {
                      b := iszero(a)
                  }
              }
              /// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata
              ///      pointer. `rdPtr` must point to some parent object with a dynamic
              ///      type's head stored at `rdPtr + headOffset`.
              function pptr(
                  ReturndataPointer rdPtr,
                  uint256 headOffset
              ) internal pure returns (ReturndataPointer rdPtrChild) {
                  rdPtrChild = rdPtr.offset(
                      rdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
                  );
              }
              /// @dev Resolves an offset stored at `rdPtr` to a returndata pointer.
              ///    `rdPtr` must point to some parent object with a dynamic type as its
              ///    first member, e.g. `struct { bytes data; }`
              function pptr(
                  ReturndataPointer rdPtr
              ) internal pure returns (ReturndataPointer rdPtrChild) {
                  rdPtrChild = rdPtr.offset(rdPtr.readUint256() & OffsetOrLengthMask);
              }
              /// @dev Returns the returndata pointer one word after `cdPtr`.
              function next(
                  ReturndataPointer rdPtr
              ) internal pure returns (ReturndataPointer rdPtrNext) {
                  assembly {
                      rdPtrNext := add(rdPtr, _OneWord)
                  }
              }
              /// @dev Returns the returndata pointer `_offset` bytes after `cdPtr`.
              function offset(
                  ReturndataPointer rdPtr,
                  uint256 _offset
              ) internal pure returns (ReturndataPointer rdPtrNext) {
                  assembly {
                      rdPtrNext := add(rdPtr, _offset)
                  }
              }
              /// @dev Copies `size` bytes from returndata starting at `src` to memory at
              /// `dst`.
              function copy(
                  ReturndataPointer src,
                  MemoryPointer dst,
                  uint256 size
              ) internal pure {
                  assembly {
                      returndatacopy(dst, src, size)
                  }
              }
          }
          library MemoryPointerLib {
              function copy(
                  MemoryPointer src,
                  MemoryPointer dst,
                  uint256 size
              ) internal view {
                  assembly {
                      let success := staticcall(
                          gas(),
                          IdentityPrecompileAddress,
                          src,
                          size,
                          dst,
                          size
                      )
                      if or(iszero(returndatasize()), iszero(success)) {
                          revert(0, 0)
                      }
                  }
              }
              function lt(
                  MemoryPointer a,
                  MemoryPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := lt(a, b)
                  }
              }
              function gt(
                  MemoryPointer a,
                  MemoryPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := gt(a, b)
                  }
              }
              function eq(
                  MemoryPointer a,
                  MemoryPointer b
              ) internal pure returns (bool c) {
                  assembly {
                      c := eq(a, b)
                  }
              }
              function isNull(MemoryPointer a) internal pure returns (bool b) {
                  assembly {
                      b := iszero(a)
                  }
              }
              function hash(
                  MemoryPointer ptr,
                  uint256 length
              ) internal pure returns (bytes32 _hash) {
                  assembly {
                      _hash := keccak256(ptr, length)
                  }
              }
              /// @dev Returns the memory pointer one word after `mPtr`.
              function next(
                  MemoryPointer mPtr
              ) internal pure returns (MemoryPointer mPtrNext) {
                  assembly {
                      mPtrNext := add(mPtr, _OneWord)
                  }
              }
              /// @dev Returns the memory pointer `_offset` bytes after `mPtr`.
              function offset(
                  MemoryPointer mPtr,
                  uint256 _offset
              ) internal pure returns (MemoryPointer mPtrNext) {
                  assembly {
                      mPtrNext := add(mPtr, _offset)
                  }
              }
              /// @dev Resolves a pointer at `mPtr + headOffset` to a memory
              ///    pointer. `mPtr` must point to some parent object with a dynamic
              ///    type's pointer stored at `mPtr + headOffset`.
              function pptr(
                  MemoryPointer mPtr,
                  uint256 headOffset
              ) internal pure returns (MemoryPointer mPtrChild) {
                  mPtrChild = mPtr.offset(headOffset).readMemoryPointer();
              }
              /// @dev Resolves a pointer stored at `mPtr` to a memory pointer.
              ///    `mPtr` must point to some parent object with a dynamic type as its
              ///    first member, e.g. `struct { bytes data; }`
              function pptr(
                  MemoryPointer mPtr
              ) internal pure returns (MemoryPointer mPtrChild) {
                  mPtrChild = mPtr.readMemoryPointer();
              }
          }
          library CalldataReaders {
              /// @dev Reads the value at `cdPtr` and applies a mask to return only the
              ///    last 4 bytes.
              function readMaskedUint256(
                  CalldataPointer cdPtr
              ) internal pure returns (uint256 value) {
                  value = cdPtr.readUint256() & OffsetOrLengthMask;
              }
              /// @dev Reads the bool at `cdPtr` in calldata.
              function readBool(
                  CalldataPointer cdPtr
              ) internal pure returns (bool value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the address at `cdPtr` in calldata.
              function readAddress(
                  CalldataPointer cdPtr
              ) internal pure returns (address value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes1 at `cdPtr` in calldata.
              function readBytes1(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes1 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes2 at `cdPtr` in calldata.
              function readBytes2(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes2 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes3 at `cdPtr` in calldata.
              function readBytes3(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes3 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes4 at `cdPtr` in calldata.
              function readBytes4(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes4 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes5 at `cdPtr` in calldata.
              function readBytes5(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes5 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes6 at `cdPtr` in calldata.
              function readBytes6(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes6 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes7 at `cdPtr` in calldata.
              function readBytes7(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes7 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes8 at `cdPtr` in calldata.
              function readBytes8(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes8 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes9 at `cdPtr` in calldata.
              function readBytes9(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes9 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes10 at `cdPtr` in calldata.
              function readBytes10(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes10 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes11 at `cdPtr` in calldata.
              function readBytes11(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes11 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes12 at `cdPtr` in calldata.
              function readBytes12(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes12 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes13 at `cdPtr` in calldata.
              function readBytes13(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes13 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes14 at `cdPtr` in calldata.
              function readBytes14(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes14 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes15 at `cdPtr` in calldata.
              function readBytes15(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes15 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes16 at `cdPtr` in calldata.
              function readBytes16(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes16 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes17 at `cdPtr` in calldata.
              function readBytes17(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes17 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes18 at `cdPtr` in calldata.
              function readBytes18(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes18 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes19 at `cdPtr` in calldata.
              function readBytes19(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes19 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes20 at `cdPtr` in calldata.
              function readBytes20(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes20 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes21 at `cdPtr` in calldata.
              function readBytes21(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes21 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes22 at `cdPtr` in calldata.
              function readBytes22(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes22 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes23 at `cdPtr` in calldata.
              function readBytes23(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes23 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes24 at `cdPtr` in calldata.
              function readBytes24(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes24 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes25 at `cdPtr` in calldata.
              function readBytes25(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes25 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes26 at `cdPtr` in calldata.
              function readBytes26(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes26 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes27 at `cdPtr` in calldata.
              function readBytes27(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes27 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes28 at `cdPtr` in calldata.
              function readBytes28(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes28 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes29 at `cdPtr` in calldata.
              function readBytes29(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes29 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes30 at `cdPtr` in calldata.
              function readBytes30(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes30 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes31 at `cdPtr` in calldata.
              function readBytes31(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes31 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the bytes32 at `cdPtr` in calldata.
              function readBytes32(
                  CalldataPointer cdPtr
              ) internal pure returns (bytes32 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint8 at `cdPtr` in calldata.
              function readUint8(
                  CalldataPointer cdPtr
              ) internal pure returns (uint8 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint16 at `cdPtr` in calldata.
              function readUint16(
                  CalldataPointer cdPtr
              ) internal pure returns (uint16 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint24 at `cdPtr` in calldata.
              function readUint24(
                  CalldataPointer cdPtr
              ) internal pure returns (uint24 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint32 at `cdPtr` in calldata.
              function readUint32(
                  CalldataPointer cdPtr
              ) internal pure returns (uint32 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint40 at `cdPtr` in calldata.
              function readUint40(
                  CalldataPointer cdPtr
              ) internal pure returns (uint40 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint48 at `cdPtr` in calldata.
              function readUint48(
                  CalldataPointer cdPtr
              ) internal pure returns (uint48 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint56 at `cdPtr` in calldata.
              function readUint56(
                  CalldataPointer cdPtr
              ) internal pure returns (uint56 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint64 at `cdPtr` in calldata.
              function readUint64(
                  CalldataPointer cdPtr
              ) internal pure returns (uint64 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint72 at `cdPtr` in calldata.
              function readUint72(
                  CalldataPointer cdPtr
              ) internal pure returns (uint72 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint80 at `cdPtr` in calldata.
              function readUint80(
                  CalldataPointer cdPtr
              ) internal pure returns (uint80 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint88 at `cdPtr` in calldata.
              function readUint88(
                  CalldataPointer cdPtr
              ) internal pure returns (uint88 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint96 at `cdPtr` in calldata.
              function readUint96(
                  CalldataPointer cdPtr
              ) internal pure returns (uint96 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint104 at `cdPtr` in calldata.
              function readUint104(
                  CalldataPointer cdPtr
              ) internal pure returns (uint104 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint112 at `cdPtr` in calldata.
              function readUint112(
                  CalldataPointer cdPtr
              ) internal pure returns (uint112 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint120 at `cdPtr` in calldata.
              function readUint120(
                  CalldataPointer cdPtr
              ) internal pure returns (uint120 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint128 at `cdPtr` in calldata.
              function readUint128(
                  CalldataPointer cdPtr
              ) internal pure returns (uint128 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint136 at `cdPtr` in calldata.
              function readUint136(
                  CalldataPointer cdPtr
              ) internal pure returns (uint136 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint144 at `cdPtr` in calldata.
              function readUint144(
                  CalldataPointer cdPtr
              ) internal pure returns (uint144 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint152 at `cdPtr` in calldata.
              function readUint152(
                  CalldataPointer cdPtr
              ) internal pure returns (uint152 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint160 at `cdPtr` in calldata.
              function readUint160(
                  CalldataPointer cdPtr
              ) internal pure returns (uint160 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint168 at `cdPtr` in calldata.
              function readUint168(
                  CalldataPointer cdPtr
              ) internal pure returns (uint168 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint176 at `cdPtr` in calldata.
              function readUint176(
                  CalldataPointer cdPtr
              ) internal pure returns (uint176 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint184 at `cdPtr` in calldata.
              function readUint184(
                  CalldataPointer cdPtr
              ) internal pure returns (uint184 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint192 at `cdPtr` in calldata.
              function readUint192(
                  CalldataPointer cdPtr
              ) internal pure returns (uint192 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint200 at `cdPtr` in calldata.
              function readUint200(
                  CalldataPointer cdPtr
              ) internal pure returns (uint200 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint208 at `cdPtr` in calldata.
              function readUint208(
                  CalldataPointer cdPtr
              ) internal pure returns (uint208 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint216 at `cdPtr` in calldata.
              function readUint216(
                  CalldataPointer cdPtr
              ) internal pure returns (uint216 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint224 at `cdPtr` in calldata.
              function readUint224(
                  CalldataPointer cdPtr
              ) internal pure returns (uint224 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint232 at `cdPtr` in calldata.
              function readUint232(
                  CalldataPointer cdPtr
              ) internal pure returns (uint232 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint240 at `cdPtr` in calldata.
              function readUint240(
                  CalldataPointer cdPtr
              ) internal pure returns (uint240 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint248 at `cdPtr` in calldata.
              function readUint248(
                  CalldataPointer cdPtr
              ) internal pure returns (uint248 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the uint256 at `cdPtr` in calldata.
              function readUint256(
                  CalldataPointer cdPtr
              ) internal pure returns (uint256 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int8 at `cdPtr` in calldata.
              function readInt8(
                  CalldataPointer cdPtr
              ) internal pure returns (int8 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int16 at `cdPtr` in calldata.
              function readInt16(
                  CalldataPointer cdPtr
              ) internal pure returns (int16 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int24 at `cdPtr` in calldata.
              function readInt24(
                  CalldataPointer cdPtr
              ) internal pure returns (int24 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int32 at `cdPtr` in calldata.
              function readInt32(
                  CalldataPointer cdPtr
              ) internal pure returns (int32 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int40 at `cdPtr` in calldata.
              function readInt40(
                  CalldataPointer cdPtr
              ) internal pure returns (int40 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int48 at `cdPtr` in calldata.
              function readInt48(
                  CalldataPointer cdPtr
              ) internal pure returns (int48 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int56 at `cdPtr` in calldata.
              function readInt56(
                  CalldataPointer cdPtr
              ) internal pure returns (int56 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int64 at `cdPtr` in calldata.
              function readInt64(
                  CalldataPointer cdPtr
              ) internal pure returns (int64 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int72 at `cdPtr` in calldata.
              function readInt72(
                  CalldataPointer cdPtr
              ) internal pure returns (int72 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int80 at `cdPtr` in calldata.
              function readInt80(
                  CalldataPointer cdPtr
              ) internal pure returns (int80 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int88 at `cdPtr` in calldata.
              function readInt88(
                  CalldataPointer cdPtr
              ) internal pure returns (int88 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int96 at `cdPtr` in calldata.
              function readInt96(
                  CalldataPointer cdPtr
              ) internal pure returns (int96 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int104 at `cdPtr` in calldata.
              function readInt104(
                  CalldataPointer cdPtr
              ) internal pure returns (int104 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int112 at `cdPtr` in calldata.
              function readInt112(
                  CalldataPointer cdPtr
              ) internal pure returns (int112 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int120 at `cdPtr` in calldata.
              function readInt120(
                  CalldataPointer cdPtr
              ) internal pure returns (int120 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int128 at `cdPtr` in calldata.
              function readInt128(
                  CalldataPointer cdPtr
              ) internal pure returns (int128 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int136 at `cdPtr` in calldata.
              function readInt136(
                  CalldataPointer cdPtr
              ) internal pure returns (int136 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int144 at `cdPtr` in calldata.
              function readInt144(
                  CalldataPointer cdPtr
              ) internal pure returns (int144 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int152 at `cdPtr` in calldata.
              function readInt152(
                  CalldataPointer cdPtr
              ) internal pure returns (int152 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int160 at `cdPtr` in calldata.
              function readInt160(
                  CalldataPointer cdPtr
              ) internal pure returns (int160 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int168 at `cdPtr` in calldata.
              function readInt168(
                  CalldataPointer cdPtr
              ) internal pure returns (int168 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int176 at `cdPtr` in calldata.
              function readInt176(
                  CalldataPointer cdPtr
              ) internal pure returns (int176 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int184 at `cdPtr` in calldata.
              function readInt184(
                  CalldataPointer cdPtr
              ) internal pure returns (int184 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int192 at `cdPtr` in calldata.
              function readInt192(
                  CalldataPointer cdPtr
              ) internal pure returns (int192 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int200 at `cdPtr` in calldata.
              function readInt200(
                  CalldataPointer cdPtr
              ) internal pure returns (int200 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int208 at `cdPtr` in calldata.
              function readInt208(
                  CalldataPointer cdPtr
              ) internal pure returns (int208 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int216 at `cdPtr` in calldata.
              function readInt216(
                  CalldataPointer cdPtr
              ) internal pure returns (int216 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int224 at `cdPtr` in calldata.
              function readInt224(
                  CalldataPointer cdPtr
              ) internal pure returns (int224 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int232 at `cdPtr` in calldata.
              function readInt232(
                  CalldataPointer cdPtr
              ) internal pure returns (int232 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int240 at `cdPtr` in calldata.
              function readInt240(
                  CalldataPointer cdPtr
              ) internal pure returns (int240 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int248 at `cdPtr` in calldata.
              function readInt248(
                  CalldataPointer cdPtr
              ) internal pure returns (int248 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
              /// @dev Reads the int256 at `cdPtr` in calldata.
              function readInt256(
                  CalldataPointer cdPtr
              ) internal pure returns (int256 value) {
                  assembly {
                      value := calldataload(cdPtr)
                  }
              }
          }
          library ReturndataReaders {
              /// @dev Reads value at `rdPtr` & applies a mask to return only last 4 bytes
              function readMaskedUint256(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint256 value) {
                  value = rdPtr.readUint256() & OffsetOrLengthMask;
              }
              /// @dev Reads the bool at `rdPtr` in returndata.
              function readBool(
                  ReturndataPointer rdPtr
              ) internal pure returns (bool value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the address at `rdPtr` in returndata.
              function readAddress(
                  ReturndataPointer rdPtr
              ) internal pure returns (address value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes1 at `rdPtr` in returndata.
              function readBytes1(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes1 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes2 at `rdPtr` in returndata.
              function readBytes2(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes2 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes3 at `rdPtr` in returndata.
              function readBytes3(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes3 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes4 at `rdPtr` in returndata.
              function readBytes4(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes4 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes5 at `rdPtr` in returndata.
              function readBytes5(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes5 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes6 at `rdPtr` in returndata.
              function readBytes6(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes6 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes7 at `rdPtr` in returndata.
              function readBytes7(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes7 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes8 at `rdPtr` in returndata.
              function readBytes8(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes8 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes9 at `rdPtr` in returndata.
              function readBytes9(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes9 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes10 at `rdPtr` in returndata.
              function readBytes10(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes10 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes11 at `rdPtr` in returndata.
              function readBytes11(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes11 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes12 at `rdPtr` in returndata.
              function readBytes12(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes12 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes13 at `rdPtr` in returndata.
              function readBytes13(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes13 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes14 at `rdPtr` in returndata.
              function readBytes14(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes14 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes15 at `rdPtr` in returndata.
              function readBytes15(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes15 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes16 at `rdPtr` in returndata.
              function readBytes16(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes16 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes17 at `rdPtr` in returndata.
              function readBytes17(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes17 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes18 at `rdPtr` in returndata.
              function readBytes18(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes18 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes19 at `rdPtr` in returndata.
              function readBytes19(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes19 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes20 at `rdPtr` in returndata.
              function readBytes20(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes20 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes21 at `rdPtr` in returndata.
              function readBytes21(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes21 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes22 at `rdPtr` in returndata.
              function readBytes22(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes22 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes23 at `rdPtr` in returndata.
              function readBytes23(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes23 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes24 at `rdPtr` in returndata.
              function readBytes24(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes24 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes25 at `rdPtr` in returndata.
              function readBytes25(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes25 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes26 at `rdPtr` in returndata.
              function readBytes26(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes26 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes27 at `rdPtr` in returndata.
              function readBytes27(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes27 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes28 at `rdPtr` in returndata.
              function readBytes28(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes28 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes29 at `rdPtr` in returndata.
              function readBytes29(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes29 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes30 at `rdPtr` in returndata.
              function readBytes30(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes30 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes31 at `rdPtr` in returndata.
              function readBytes31(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes31 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the bytes32 at `rdPtr` in returndata.
              function readBytes32(
                  ReturndataPointer rdPtr
              ) internal pure returns (bytes32 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint8 at `rdPtr` in returndata.
              function readUint8(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint8 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint16 at `rdPtr` in returndata.
              function readUint16(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint16 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint24 at `rdPtr` in returndata.
              function readUint24(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint24 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint32 at `rdPtr` in returndata.
              function readUint32(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint32 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint40 at `rdPtr` in returndata.
              function readUint40(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint40 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint48 at `rdPtr` in returndata.
              function readUint48(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint48 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint56 at `rdPtr` in returndata.
              function readUint56(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint56 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint64 at `rdPtr` in returndata.
              function readUint64(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint64 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint72 at `rdPtr` in returndata.
              function readUint72(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint72 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint80 at `rdPtr` in returndata.
              function readUint80(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint80 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint88 at `rdPtr` in returndata.
              function readUint88(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint88 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint96 at `rdPtr` in returndata.
              function readUint96(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint96 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint104 at `rdPtr` in returndata.
              function readUint104(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint104 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint112 at `rdPtr` in returndata.
              function readUint112(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint112 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint120 at `rdPtr` in returndata.
              function readUint120(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint120 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint128 at `rdPtr` in returndata.
              function readUint128(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint128 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint136 at `rdPtr` in returndata.
              function readUint136(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint136 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint144 at `rdPtr` in returndata.
              function readUint144(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint144 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint152 at `rdPtr` in returndata.
              function readUint152(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint152 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint160 at `rdPtr` in returndata.
              function readUint160(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint160 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint168 at `rdPtr` in returndata.
              function readUint168(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint168 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint176 at `rdPtr` in returndata.
              function readUint176(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint176 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint184 at `rdPtr` in returndata.
              function readUint184(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint184 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint192 at `rdPtr` in returndata.
              function readUint192(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint192 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint200 at `rdPtr` in returndata.
              function readUint200(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint200 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint208 at `rdPtr` in returndata.
              function readUint208(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint208 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint216 at `rdPtr` in returndata.
              function readUint216(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint216 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint224 at `rdPtr` in returndata.
              function readUint224(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint224 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint232 at `rdPtr` in returndata.
              function readUint232(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint232 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint240 at `rdPtr` in returndata.
              function readUint240(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint240 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint248 at `rdPtr` in returndata.
              function readUint248(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint248 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the uint256 at `rdPtr` in returndata.
              function readUint256(
                  ReturndataPointer rdPtr
              ) internal pure returns (uint256 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int8 at `rdPtr` in returndata.
              function readInt8(
                  ReturndataPointer rdPtr
              ) internal pure returns (int8 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int16 at `rdPtr` in returndata.
              function readInt16(
                  ReturndataPointer rdPtr
              ) internal pure returns (int16 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int24 at `rdPtr` in returndata.
              function readInt24(
                  ReturndataPointer rdPtr
              ) internal pure returns (int24 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int32 at `rdPtr` in returndata.
              function readInt32(
                  ReturndataPointer rdPtr
              ) internal pure returns (int32 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int40 at `rdPtr` in returndata.
              function readInt40(
                  ReturndataPointer rdPtr
              ) internal pure returns (int40 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int48 at `rdPtr` in returndata.
              function readInt48(
                  ReturndataPointer rdPtr
              ) internal pure returns (int48 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int56 at `rdPtr` in returndata.
              function readInt56(
                  ReturndataPointer rdPtr
              ) internal pure returns (int56 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int64 at `rdPtr` in returndata.
              function readInt64(
                  ReturndataPointer rdPtr
              ) internal pure returns (int64 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int72 at `rdPtr` in returndata.
              function readInt72(
                  ReturndataPointer rdPtr
              ) internal pure returns (int72 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int80 at `rdPtr` in returndata.
              function readInt80(
                  ReturndataPointer rdPtr
              ) internal pure returns (int80 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int88 at `rdPtr` in returndata.
              function readInt88(
                  ReturndataPointer rdPtr
              ) internal pure returns (int88 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int96 at `rdPtr` in returndata.
              function readInt96(
                  ReturndataPointer rdPtr
              ) internal pure returns (int96 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int104 at `rdPtr` in returndata.
              function readInt104(
                  ReturndataPointer rdPtr
              ) internal pure returns (int104 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int112 at `rdPtr` in returndata.
              function readInt112(
                  ReturndataPointer rdPtr
              ) internal pure returns (int112 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int120 at `rdPtr` in returndata.
              function readInt120(
                  ReturndataPointer rdPtr
              ) internal pure returns (int120 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int128 at `rdPtr` in returndata.
              function readInt128(
                  ReturndataPointer rdPtr
              ) internal pure returns (int128 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int136 at `rdPtr` in returndata.
              function readInt136(
                  ReturndataPointer rdPtr
              ) internal pure returns (int136 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int144 at `rdPtr` in returndata.
              function readInt144(
                  ReturndataPointer rdPtr
              ) internal pure returns (int144 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int152 at `rdPtr` in returndata.
              function readInt152(
                  ReturndataPointer rdPtr
              ) internal pure returns (int152 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int160 at `rdPtr` in returndata.
              function readInt160(
                  ReturndataPointer rdPtr
              ) internal pure returns (int160 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int168 at `rdPtr` in returndata.
              function readInt168(
                  ReturndataPointer rdPtr
              ) internal pure returns (int168 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int176 at `rdPtr` in returndata.
              function readInt176(
                  ReturndataPointer rdPtr
              ) internal pure returns (int176 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int184 at `rdPtr` in returndata.
              function readInt184(
                  ReturndataPointer rdPtr
              ) internal pure returns (int184 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int192 at `rdPtr` in returndata.
              function readInt192(
                  ReturndataPointer rdPtr
              ) internal pure returns (int192 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int200 at `rdPtr` in returndata.
              function readInt200(
                  ReturndataPointer rdPtr
              ) internal pure returns (int200 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int208 at `rdPtr` in returndata.
              function readInt208(
                  ReturndataPointer rdPtr
              ) internal pure returns (int208 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int216 at `rdPtr` in returndata.
              function readInt216(
                  ReturndataPointer rdPtr
              ) internal pure returns (int216 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int224 at `rdPtr` in returndata.
              function readInt224(
                  ReturndataPointer rdPtr
              ) internal pure returns (int224 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int232 at `rdPtr` in returndata.
              function readInt232(
                  ReturndataPointer rdPtr
              ) internal pure returns (int232 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int240 at `rdPtr` in returndata.
              function readInt240(
                  ReturndataPointer rdPtr
              ) internal pure returns (int240 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int248 at `rdPtr` in returndata.
              function readInt248(
                  ReturndataPointer rdPtr
              ) internal pure returns (int248 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
              /// @dev Reads the int256 at `rdPtr` in returndata.
              function readInt256(
                  ReturndataPointer rdPtr
              ) internal pure returns (int256 value) {
                  assembly {
                      returndatacopy(0, rdPtr, _OneWord)
                      value := mload(0)
                  }
              }
          }
          library MemoryReaders {
              /// @dev Reads the memory pointer at `mPtr` in memory.
              function readMemoryPointer(
                  MemoryPointer mPtr
              ) internal pure returns (MemoryPointer value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads value at `mPtr` & applies a mask to return only last 4 bytes
              function readMaskedUint256(
                  MemoryPointer mPtr
              ) internal pure returns (uint256 value) {
                  value = mPtr.readUint256() & OffsetOrLengthMask;
              }
              /// @dev Reads the bool at `mPtr` in memory.
              function readBool(MemoryPointer mPtr) internal pure returns (bool value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the address at `mPtr` in memory.
              function readAddress(
                  MemoryPointer mPtr
              ) internal pure returns (address value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes1 at `mPtr` in memory.
              function readBytes1(
                  MemoryPointer mPtr
              ) internal pure returns (bytes1 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes2 at `mPtr` in memory.
              function readBytes2(
                  MemoryPointer mPtr
              ) internal pure returns (bytes2 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes3 at `mPtr` in memory.
              function readBytes3(
                  MemoryPointer mPtr
              ) internal pure returns (bytes3 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes4 at `mPtr` in memory.
              function readBytes4(
                  MemoryPointer mPtr
              ) internal pure returns (bytes4 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes5 at `mPtr` in memory.
              function readBytes5(
                  MemoryPointer mPtr
              ) internal pure returns (bytes5 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes6 at `mPtr` in memory.
              function readBytes6(
                  MemoryPointer mPtr
              ) internal pure returns (bytes6 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes7 at `mPtr` in memory.
              function readBytes7(
                  MemoryPointer mPtr
              ) internal pure returns (bytes7 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes8 at `mPtr` in memory.
              function readBytes8(
                  MemoryPointer mPtr
              ) internal pure returns (bytes8 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes9 at `mPtr` in memory.
              function readBytes9(
                  MemoryPointer mPtr
              ) internal pure returns (bytes9 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes10 at `mPtr` in memory.
              function readBytes10(
                  MemoryPointer mPtr
              ) internal pure returns (bytes10 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes11 at `mPtr` in memory.
              function readBytes11(
                  MemoryPointer mPtr
              ) internal pure returns (bytes11 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes12 at `mPtr` in memory.
              function readBytes12(
                  MemoryPointer mPtr
              ) internal pure returns (bytes12 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes13 at `mPtr` in memory.
              function readBytes13(
                  MemoryPointer mPtr
              ) internal pure returns (bytes13 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes14 at `mPtr` in memory.
              function readBytes14(
                  MemoryPointer mPtr
              ) internal pure returns (bytes14 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes15 at `mPtr` in memory.
              function readBytes15(
                  MemoryPointer mPtr
              ) internal pure returns (bytes15 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes16 at `mPtr` in memory.
              function readBytes16(
                  MemoryPointer mPtr
              ) internal pure returns (bytes16 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes17 at `mPtr` in memory.
              function readBytes17(
                  MemoryPointer mPtr
              ) internal pure returns (bytes17 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes18 at `mPtr` in memory.
              function readBytes18(
                  MemoryPointer mPtr
              ) internal pure returns (bytes18 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes19 at `mPtr` in memory.
              function readBytes19(
                  MemoryPointer mPtr
              ) internal pure returns (bytes19 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes20 at `mPtr` in memory.
              function readBytes20(
                  MemoryPointer mPtr
              ) internal pure returns (bytes20 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes21 at `mPtr` in memory.
              function readBytes21(
                  MemoryPointer mPtr
              ) internal pure returns (bytes21 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes22 at `mPtr` in memory.
              function readBytes22(
                  MemoryPointer mPtr
              ) internal pure returns (bytes22 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes23 at `mPtr` in memory.
              function readBytes23(
                  MemoryPointer mPtr
              ) internal pure returns (bytes23 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes24 at `mPtr` in memory.
              function readBytes24(
                  MemoryPointer mPtr
              ) internal pure returns (bytes24 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes25 at `mPtr` in memory.
              function readBytes25(
                  MemoryPointer mPtr
              ) internal pure returns (bytes25 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes26 at `mPtr` in memory.
              function readBytes26(
                  MemoryPointer mPtr
              ) internal pure returns (bytes26 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes27 at `mPtr` in memory.
              function readBytes27(
                  MemoryPointer mPtr
              ) internal pure returns (bytes27 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes28 at `mPtr` in memory.
              function readBytes28(
                  MemoryPointer mPtr
              ) internal pure returns (bytes28 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes29 at `mPtr` in memory.
              function readBytes29(
                  MemoryPointer mPtr
              ) internal pure returns (bytes29 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes30 at `mPtr` in memory.
              function readBytes30(
                  MemoryPointer mPtr
              ) internal pure returns (bytes30 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes31 at `mPtr` in memory.
              function readBytes31(
                  MemoryPointer mPtr
              ) internal pure returns (bytes31 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the bytes32 at `mPtr` in memory.
              function readBytes32(
                  MemoryPointer mPtr
              ) internal pure returns (bytes32 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint8 at `mPtr` in memory.
              function readUint8(MemoryPointer mPtr) internal pure returns (uint8 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint16 at `mPtr` in memory.
              function readUint16(
                  MemoryPointer mPtr
              ) internal pure returns (uint16 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint24 at `mPtr` in memory.
              function readUint24(
                  MemoryPointer mPtr
              ) internal pure returns (uint24 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint32 at `mPtr` in memory.
              function readUint32(
                  MemoryPointer mPtr
              ) internal pure returns (uint32 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint40 at `mPtr` in memory.
              function readUint40(
                  MemoryPointer mPtr
              ) internal pure returns (uint40 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint48 at `mPtr` in memory.
              function readUint48(
                  MemoryPointer mPtr
              ) internal pure returns (uint48 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint56 at `mPtr` in memory.
              function readUint56(
                  MemoryPointer mPtr
              ) internal pure returns (uint56 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint64 at `mPtr` in memory.
              function readUint64(
                  MemoryPointer mPtr
              ) internal pure returns (uint64 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint72 at `mPtr` in memory.
              function readUint72(
                  MemoryPointer mPtr
              ) internal pure returns (uint72 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint80 at `mPtr` in memory.
              function readUint80(
                  MemoryPointer mPtr
              ) internal pure returns (uint80 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint88 at `mPtr` in memory.
              function readUint88(
                  MemoryPointer mPtr
              ) internal pure returns (uint88 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint96 at `mPtr` in memory.
              function readUint96(
                  MemoryPointer mPtr
              ) internal pure returns (uint96 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint104 at `mPtr` in memory.
              function readUint104(
                  MemoryPointer mPtr
              ) internal pure returns (uint104 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint112 at `mPtr` in memory.
              function readUint112(
                  MemoryPointer mPtr
              ) internal pure returns (uint112 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint120 at `mPtr` in memory.
              function readUint120(
                  MemoryPointer mPtr
              ) internal pure returns (uint120 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint128 at `mPtr` in memory.
              function readUint128(
                  MemoryPointer mPtr
              ) internal pure returns (uint128 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint136 at `mPtr` in memory.
              function readUint136(
                  MemoryPointer mPtr
              ) internal pure returns (uint136 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint144 at `mPtr` in memory.
              function readUint144(
                  MemoryPointer mPtr
              ) internal pure returns (uint144 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint152 at `mPtr` in memory.
              function readUint152(
                  MemoryPointer mPtr
              ) internal pure returns (uint152 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint160 at `mPtr` in memory.
              function readUint160(
                  MemoryPointer mPtr
              ) internal pure returns (uint160 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint168 at `mPtr` in memory.
              function readUint168(
                  MemoryPointer mPtr
              ) internal pure returns (uint168 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint176 at `mPtr` in memory.
              function readUint176(
                  MemoryPointer mPtr
              ) internal pure returns (uint176 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint184 at `mPtr` in memory.
              function readUint184(
                  MemoryPointer mPtr
              ) internal pure returns (uint184 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint192 at `mPtr` in memory.
              function readUint192(
                  MemoryPointer mPtr
              ) internal pure returns (uint192 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint200 at `mPtr` in memory.
              function readUint200(
                  MemoryPointer mPtr
              ) internal pure returns (uint200 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint208 at `mPtr` in memory.
              function readUint208(
                  MemoryPointer mPtr
              ) internal pure returns (uint208 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint216 at `mPtr` in memory.
              function readUint216(
                  MemoryPointer mPtr
              ) internal pure returns (uint216 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint224 at `mPtr` in memory.
              function readUint224(
                  MemoryPointer mPtr
              ) internal pure returns (uint224 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint232 at `mPtr` in memory.
              function readUint232(
                  MemoryPointer mPtr
              ) internal pure returns (uint232 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint240 at `mPtr` in memory.
              function readUint240(
                  MemoryPointer mPtr
              ) internal pure returns (uint240 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint248 at `mPtr` in memory.
              function readUint248(
                  MemoryPointer mPtr
              ) internal pure returns (uint248 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the uint256 at `mPtr` in memory.
              function readUint256(
                  MemoryPointer mPtr
              ) internal pure returns (uint256 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int8 at `mPtr` in memory.
              function readInt8(MemoryPointer mPtr) internal pure returns (int8 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int16 at `mPtr` in memory.
              function readInt16(MemoryPointer mPtr) internal pure returns (int16 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int24 at `mPtr` in memory.
              function readInt24(MemoryPointer mPtr) internal pure returns (int24 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int32 at `mPtr` in memory.
              function readInt32(MemoryPointer mPtr) internal pure returns (int32 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int40 at `mPtr` in memory.
              function readInt40(MemoryPointer mPtr) internal pure returns (int40 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int48 at `mPtr` in memory.
              function readInt48(MemoryPointer mPtr) internal pure returns (int48 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int56 at `mPtr` in memory.
              function readInt56(MemoryPointer mPtr) internal pure returns (int56 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int64 at `mPtr` in memory.
              function readInt64(MemoryPointer mPtr) internal pure returns (int64 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int72 at `mPtr` in memory.
              function readInt72(MemoryPointer mPtr) internal pure returns (int72 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int80 at `mPtr` in memory.
              function readInt80(MemoryPointer mPtr) internal pure returns (int80 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int88 at `mPtr` in memory.
              function readInt88(MemoryPointer mPtr) internal pure returns (int88 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int96 at `mPtr` in memory.
              function readInt96(MemoryPointer mPtr) internal pure returns (int96 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int104 at `mPtr` in memory.
              function readInt104(
                  MemoryPointer mPtr
              ) internal pure returns (int104 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int112 at `mPtr` in memory.
              function readInt112(
                  MemoryPointer mPtr
              ) internal pure returns (int112 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int120 at `mPtr` in memory.
              function readInt120(
                  MemoryPointer mPtr
              ) internal pure returns (int120 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int128 at `mPtr` in memory.
              function readInt128(
                  MemoryPointer mPtr
              ) internal pure returns (int128 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int136 at `mPtr` in memory.
              function readInt136(
                  MemoryPointer mPtr
              ) internal pure returns (int136 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int144 at `mPtr` in memory.
              function readInt144(
                  MemoryPointer mPtr
              ) internal pure returns (int144 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int152 at `mPtr` in memory.
              function readInt152(
                  MemoryPointer mPtr
              ) internal pure returns (int152 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int160 at `mPtr` in memory.
              function readInt160(
                  MemoryPointer mPtr
              ) internal pure returns (int160 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int168 at `mPtr` in memory.
              function readInt168(
                  MemoryPointer mPtr
              ) internal pure returns (int168 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int176 at `mPtr` in memory.
              function readInt176(
                  MemoryPointer mPtr
              ) internal pure returns (int176 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int184 at `mPtr` in memory.
              function readInt184(
                  MemoryPointer mPtr
              ) internal pure returns (int184 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int192 at `mPtr` in memory.
              function readInt192(
                  MemoryPointer mPtr
              ) internal pure returns (int192 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int200 at `mPtr` in memory.
              function readInt200(
                  MemoryPointer mPtr
              ) internal pure returns (int200 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int208 at `mPtr` in memory.
              function readInt208(
                  MemoryPointer mPtr
              ) internal pure returns (int208 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int216 at `mPtr` in memory.
              function readInt216(
                  MemoryPointer mPtr
              ) internal pure returns (int216 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int224 at `mPtr` in memory.
              function readInt224(
                  MemoryPointer mPtr
              ) internal pure returns (int224 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int232 at `mPtr` in memory.
              function readInt232(
                  MemoryPointer mPtr
              ) internal pure returns (int232 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int240 at `mPtr` in memory.
              function readInt240(
                  MemoryPointer mPtr
              ) internal pure returns (int240 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int248 at `mPtr` in memory.
              function readInt248(
                  MemoryPointer mPtr
              ) internal pure returns (int248 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
              /// @dev Reads the int256 at `mPtr` in memory.
              function readInt256(
                  MemoryPointer mPtr
              ) internal pure returns (int256 value) {
                  assembly {
                      value := mload(mPtr)
                  }
              }
          }
          library MemoryWriters {
              /// @dev Writes `valuePtr` to memory at `mPtr`.
              function write(MemoryPointer mPtr, MemoryPointer valuePtr) internal pure {
                  assembly {
                      mstore(mPtr, valuePtr)
                  }
              }
              /// @dev Writes a boolean `value` to `mPtr` in memory.
              function write(MemoryPointer mPtr, bool value) internal pure {
                  assembly {
                      mstore(mPtr, value)
                  }
              }
              /// @dev Writes an address `value` to `mPtr` in memory.
              function write(MemoryPointer mPtr, address value) internal pure {
                  assembly {
                      mstore(mPtr, value)
                  }
              }
              /// @dev Writes a bytes32 `value` to `mPtr` in memory.
              /// Separate name to disambiguate literal write parameters.
              function writeBytes32(MemoryPointer mPtr, bytes32 value) internal pure {
                  assembly {
                      mstore(mPtr, value)
                  }
              }
              /// @dev Writes a uint256 `value` to `mPtr` in memory.
              function write(MemoryPointer mPtr, uint256 value) internal pure {
                  assembly {
                      mstore(mPtr, value)
                  }
              }
              /// @dev Writes an int256 `value` to `mPtr` in memory.
              /// Separate name to disambiguate literal write parameters.
              function writeInt(MemoryPointer mPtr, int256 value) internal pure {
                  assembly {
                      mstore(mPtr, value)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {
              BasicOrderType,
              ItemType,
              OrderType,
              Side
          } from "./ConsiderationEnums.sol";
          import {
              CalldataPointer,
              MemoryPointer
          } from "../helpers/PointerLibraries.sol";
          /**
           * @dev An order contains eleven components: an offerer, a zone (or account that
           *      can cancel the order or restrict who can fulfill the order depending on
           *      the type), the order type (specifying partial fill support as well as
           *      restricted order status), the start and end time, a hash that will be
           *      provided to the zone when validating restricted orders, a salt, a key
           *      corresponding to a given conduit, a counter, and an arbitrary number of
           *      offer items that can be spent along with consideration items that must
           *      be received by their respective recipient.
           */
          struct OrderComponents {
              address offerer;
              address zone;
              OfferItem[] offer;
              ConsiderationItem[] consideration;
              OrderType orderType;
              uint256 startTime;
              uint256 endTime;
              bytes32 zoneHash;
              uint256 salt;
              bytes32 conduitKey;
              uint256 counter;
          }
          /**
           * @dev An offer item has five components: an item type (ETH or other native
           *      tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and
           *      ERC1155), a token address, a dual-purpose "identifierOrCriteria"
           *      component that will either represent a tokenId or a merkle root
           *      depending on the item type, and a start and end amount that support
           *      increasing or decreasing amounts over the duration of the respective
           *      order.
           */
          struct OfferItem {
              ItemType itemType;
              address token;
              uint256 identifierOrCriteria;
              uint256 startAmount;
              uint256 endAmount;
          }
          /**
           * @dev A consideration item has the same five components as an offer item and
           *      an additional sixth component designating the required recipient of the
           *      item.
           */
          struct ConsiderationItem {
              ItemType itemType;
              address token;
              uint256 identifierOrCriteria;
              uint256 startAmount;
              uint256 endAmount;
              address payable recipient;
          }
          /**
           * @dev A spent item is translated from a utilized offer item and has four
           *      components: an item type (ETH or other native tokens, ERC20, ERC721, and
           *      ERC1155), a token address, a tokenId, and an amount.
           */
          struct SpentItem {
              ItemType itemType;
              address token;
              uint256 identifier;
              uint256 amount;
          }
          /**
           * @dev A received item is translated from a utilized consideration item and has
           *      the same four components as a spent item, as well as an additional fifth
           *      component designating the required recipient of the item.
           */
          struct ReceivedItem {
              ItemType itemType;
              address token;
              uint256 identifier;
              uint256 amount;
              address payable recipient;
          }
          /**
           * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155
           *      matching, a group of six functions may be called that only requires a
           *      subset of the usual order arguments. Note the use of a "basicOrderType"
           *      enum; this represents both the usual order type as well as the "route"
           *      of the basic order (a simple derivation function for the basic order
           *      type is `basicOrderType = orderType + (4 * basicOrderRoute)`.)
           */
          struct BasicOrderParameters {
              // calldata offset
              address considerationToken; // 0x24
              uint256 considerationIdentifier; // 0x44
              uint256 considerationAmount; // 0x64
              address payable offerer; // 0x84
              address zone; // 0xa4
              address offerToken; // 0xc4
              uint256 offerIdentifier; // 0xe4
              uint256 offerAmount; // 0x104
              BasicOrderType basicOrderType; // 0x124
              uint256 startTime; // 0x144
              uint256 endTime; // 0x164
              bytes32 zoneHash; // 0x184
              uint256 salt; // 0x1a4
              bytes32 offererConduitKey; // 0x1c4
              bytes32 fulfillerConduitKey; // 0x1e4
              uint256 totalOriginalAdditionalRecipients; // 0x204
              AdditionalRecipient[] additionalRecipients; // 0x224
              bytes signature; // 0x244
              // Total length, excluding dynamic array data: 0x264 (580)
          }
          /**
           * @dev Basic orders can supply any number of additional recipients, with the
           *      implied assumption that they are supplied from the offered ETH (or other
           *      native token) or ERC20 token for the order.
           */
          struct AdditionalRecipient {
              uint256 amount;
              address payable recipient;
          }
          /**
           * @dev The full set of order components, with the exception of the counter,
           *      must be supplied when fulfilling more sophisticated orders or groups of
           *      orders. The total number of original consideration items must also be
           *      supplied, as the caller may specify additional consideration items.
           */
          struct OrderParameters {
              address offerer; // 0x00
              address zone; // 0x20
              OfferItem[] offer; // 0x40
              ConsiderationItem[] consideration; // 0x60
              OrderType orderType; // 0x80
              uint256 startTime; // 0xa0
              uint256 endTime; // 0xc0
              bytes32 zoneHash; // 0xe0
              uint256 salt; // 0x100
              bytes32 conduitKey; // 0x120
              uint256 totalOriginalConsiderationItems; // 0x140
              // offer.length                          // 0x160
          }
          /**
           * @dev Orders require a signature in addition to the other order parameters.
           */
          struct Order {
              OrderParameters parameters;
              bytes signature;
          }
          /**
           * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
           *      and a denominator (the total size of the order) in addition to the
           *      signature and other order parameters. It also supports an optional field
           *      for supplying extra data; this data will be provided to the zone if the
           *      order type is restricted and the zone is not the caller, or will be
           *      provided to the offerer as context for contract order types.
           */
          struct AdvancedOrder {
              OrderParameters parameters;
              uint120 numerator;
              uint120 denominator;
              bytes signature;
              bytes extraData;
          }
          /**
           * @dev Orders can be validated (either explicitly via `validate`, or as a
           *      consequence of a full or partial fill), specifically cancelled (they can
           *      also be cancelled in bulk via incrementing a per-zone counter), and
           *      partially or fully filled (with the fraction filled represented by a
           *      numerator and denominator).
           */
          struct OrderStatus {
              bool isValidated;
              bool isCancelled;
              uint120 numerator;
              uint120 denominator;
          }
          /**
           * @dev A criteria resolver specifies an order, side (offer vs. consideration),
           *      and item index. It then provides a chosen identifier (i.e. tokenId)
           *      alongside a merkle proof demonstrating the identifier meets the required
           *      criteria.
           */
          struct CriteriaResolver {
              uint256 orderIndex;
              Side side;
              uint256 index;
              uint256 identifier;
              bytes32[] criteriaProof;
          }
          /**
           * @dev A fulfillment is applied to a group of orders. It decrements a series of
           *      offer and consideration items, then generates a single execution
           *      element. A given fulfillment can be applied to as many offer and
           *      consideration items as desired, but must contain at least one offer and
           *      at least one consideration that match. The fulfillment must also remain
           *      consistent on all key parameters across all offer items (same offerer,
           *      token, type, tokenId, and conduit preference) as well as across all
           *      consideration items (token, type, tokenId, and recipient).
           */
          struct Fulfillment {
              FulfillmentComponent[] offerComponents;
              FulfillmentComponent[] considerationComponents;
          }
          /**
           * @dev Each fulfillment component contains one index referencing a specific
           *      order and another referencing a specific offer or consideration item.
           */
          struct FulfillmentComponent {
              uint256 orderIndex;
              uint256 itemIndex;
          }
          /**
           * @dev An execution is triggered once all consideration items have been zeroed
           *      out. It sends the item in question from the offerer to the item's
           *      recipient, optionally sourcing approvals from either this contract
           *      directly or from the offerer's chosen conduit if one is specified. An
           *      execution is not provided as an argument, but rather is derived via
           *      orders, criteria resolvers, and fulfillments (where the total number of
           *      executions will be less than or equal to the total number of indicated
           *      fulfillments) and returned as part of `matchOrders`.
           */
          struct Execution {
              ReceivedItem item;
              address offerer;
              bytes32 conduitKey;
          }
          /**
           * @dev Restricted orders are validated post-execution by calling validateOrder
           *      on the zone. This struct provides context about the order fulfillment
           *      and any supplied extraData, as well as all order hashes fulfilled in a
           *      call to a match or fulfillAvailable method.
           */
          struct ZoneParameters {
              bytes32 orderHash;
              address fulfiller;
              address offerer;
              SpentItem[] offer;
              ReceivedItem[] consideration;
              bytes extraData;
              bytes32[] orderHashes;
              uint256 startTime;
              uint256 endTime;
              bytes32 zoneHash;
          }
          /**
           * @dev Zones and contract offerers can communicate which schemas they implement
           *      along with any associated metadata related to each schema.
           */
          struct Schema {
              uint256 id;
              bytes metadata;
          }
          using StructPointers for OrderComponents global;
          using StructPointers for OfferItem global;
          using StructPointers for ConsiderationItem global;
          using StructPointers for SpentItem global;
          using StructPointers for ReceivedItem global;
          using StructPointers for BasicOrderParameters global;
          using StructPointers for AdditionalRecipient global;
          using StructPointers for OrderParameters global;
          using StructPointers for Order global;
          using StructPointers for AdvancedOrder global;
          using StructPointers for OrderStatus global;
          using StructPointers for CriteriaResolver global;
          using StructPointers for Fulfillment global;
          using StructPointers for FulfillmentComponent global;
          using StructPointers for Execution global;
          using StructPointers for ZoneParameters global;
          /**
           * @dev This library provides a set of functions for converting structs to
           *      pointers.
           */
          library StructPointers {
              /**
               * @dev Get a MemoryPointer from OrderComponents.
               *
               * @param obj The OrderComponents object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  OrderComponents memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from OrderComponents.
               *
               * @param obj The OrderComponents object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  OrderComponents calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from OfferItem.
               *
               * @param obj The OfferItem object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  OfferItem memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from OfferItem.
               *
               * @param obj The OfferItem object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  OfferItem calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from ConsiderationItem.
               *
               * @param obj The ConsiderationItem object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  ConsiderationItem memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from ConsiderationItem.
               *
               * @param obj The ConsiderationItem object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  ConsiderationItem calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from SpentItem.
               *
               * @param obj The SpentItem object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  SpentItem memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from SpentItem.
               *
               * @param obj The SpentItem object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  SpentItem calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from ReceivedItem.
               *
               * @param obj The ReceivedItem object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  ReceivedItem memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from ReceivedItem.
               *
               * @param obj The ReceivedItem object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  ReceivedItem calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from BasicOrderParameters.
               *
               * @param obj The BasicOrderParameters object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  BasicOrderParameters memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from BasicOrderParameters.
               *
               * @param obj The BasicOrderParameters object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  BasicOrderParameters calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from AdditionalRecipient.
               *
               * @param obj The AdditionalRecipient object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  AdditionalRecipient memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from AdditionalRecipient.
               *
               * @param obj The AdditionalRecipient object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  AdditionalRecipient calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from OrderParameters.
               *
               * @param obj The OrderParameters object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  OrderParameters memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from OrderParameters.
               *
               * @param obj The OrderParameters object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  OrderParameters calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from Order.
               *
               * @param obj The Order object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  Order memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from Order.
               *
               * @param obj The Order object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  Order calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from AdvancedOrder.
               *
               * @param obj The AdvancedOrder object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  AdvancedOrder memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from AdvancedOrder.
               *
               * @param obj The AdvancedOrder object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  AdvancedOrder calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from OrderStatus.
               *
               * @param obj The OrderStatus object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  OrderStatus memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from OrderStatus.
               *
               * @param obj The OrderStatus object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  OrderStatus calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from CriteriaResolver.
               *
               * @param obj The CriteriaResolver object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  CriteriaResolver memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from CriteriaResolver.
               *
               * @param obj The CriteriaResolver object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  CriteriaResolver calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from Fulfillment.
               *
               * @param obj The Fulfillment object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  Fulfillment memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from Fulfillment.
               *
               * @param obj The Fulfillment object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  Fulfillment calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from FulfillmentComponent.
               *
               * @param obj The FulfillmentComponent object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  FulfillmentComponent memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from FulfillmentComponent.
               *
               * @param obj The FulfillmentComponent object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  FulfillmentComponent calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from Execution.
               *
               * @param obj The Execution object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  Execution memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from Execution.
               *
               * @param obj The Execution object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  Execution calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a MemoryPointer from ZoneParameters.
               *
               * @param obj The ZoneParameters object.
               *
               * @return ptr The MemoryPointer.
               */
              function toMemoryPointer(
                  ZoneParameters memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Get a CalldataPointer from ZoneParameters.
               *
               * @param obj The ZoneParameters object.
               *
               * @return ptr The CalldataPointer.
               */
              function toCalldataPointer(
                  ZoneParameters calldata obj
              ) internal pure returns (CalldataPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { Side } from "./ConsiderationEnums.sol";
          import {
              BadFraction_error_length,
              BadFraction_error_selector,
              CannotCancelOrder_error_length,
              CannotCancelOrder_error_selector,
              ConsiderationLengthNotEqualToTotalOriginal_error_length,
              ConsiderationLengthNotEqualToTotalOriginal_error_selector,
              ConsiderationNotMet_error_considerationIndex_ptr,
              ConsiderationNotMet_error_length,
              ConsiderationNotMet_error_orderIndex_ptr,
              ConsiderationNotMet_error_selector,
              ConsiderationNotMet_error_shortfallAmount_ptr,
              CriteriaNotEnabledForItem_error_length,
              CriteriaNotEnabledForItem_error_selector,
              Error_selector_offset,
              InsufficientNativeTokensSupplied_error_length,
              InsufficientNativeTokensSupplied_error_selector,
              InvalidBasicOrderParameterEncoding_error_length,
              InvalidBasicOrderParameterEncoding_error_selector,
              InvalidCallToConduit_error_conduit_ptr,
              InvalidCallToConduit_error_length,
              InvalidCallToConduit_error_selector,
              InvalidConduit_error_conduit_ptr,
              InvalidConduit_error_conduitKey_ptr,
              InvalidConduit_error_length,
              InvalidConduit_error_selector,
              InvalidContractOrder_error_length,
              InvalidContractOrder_error_orderHash_ptr,
              InvalidContractOrder_error_selector,
              InvalidERC721TransferAmount_error_amount_ptr,
              InvalidERC721TransferAmount_error_length,
              InvalidERC721TransferAmount_error_selector,
              InvalidMsgValue_error_length,
              InvalidMsgValue_error_selector,
              InvalidMsgValue_error_value_ptr,
              InvalidNativeOfferItem_error_length,
              InvalidNativeOfferItem_error_selector,
              InvalidProof_error_length,
              InvalidProof_error_selector,
              InvalidTime_error_endTime_ptr,
              InvalidTime_error_length,
              InvalidTime_error_selector,
              InvalidTime_error_startTime_ptr,
              MismatchedOfferAndConsiderationComponents_error_idx_ptr,
              MismatchedOfferAndConsiderationComponents_error_length,
              MismatchedOfferAndConsiderationComponents_error_selector,
              MissingFulfillmentComponentOnAggregation_error_length,
              MissingFulfillmentComponentOnAggregation_error_selector,
              MissingFulfillmentComponentOnAggregation_error_side_ptr,
              MissingOriginalConsiderationItems_error_length,
              MissingOriginalConsiderationItems_error_selector,
              NoReentrantCalls_error_length,
              NoReentrantCalls_error_selector,
              NoSpecifiedOrdersAvailable_error_length,
              NoSpecifiedOrdersAvailable_error_selector,
              OfferAndConsiderationRequiredOnFulfillment_error_length,
              OfferAndConsiderationRequiredOnFulfillment_error_selector,
              OrderAlreadyFilled_error_length,
              OrderAlreadyFilled_error_orderHash_ptr,
              OrderAlreadyFilled_error_selector,
              OrderCriteriaResolverOutOfRange_error_length,
              OrderCriteriaResolverOutOfRange_error_selector,
              OrderCriteriaResolverOutOfRange_error_side_ptr,
              OrderIsCancelled_error_length,
              OrderIsCancelled_error_orderHash_ptr,
              OrderIsCancelled_error_selector,
              OrderPartiallyFilled_error_length,
              OrderPartiallyFilled_error_orderHash_ptr,
              OrderPartiallyFilled_error_selector,
              PartialFillsNotEnabledForOrder_error_length,
              PartialFillsNotEnabledForOrder_error_selector,
              UnresolvedConsiderationCriteria_error_considerationIdx_ptr,
              UnresolvedConsiderationCriteria_error_length,
              UnresolvedConsiderationCriteria_error_orderIndex_ptr,
              UnresolvedConsiderationCriteria_error_selector,
              UnresolvedOfferCriteria_error_length,
              UnresolvedOfferCriteria_error_offerIndex_ptr,
              UnresolvedOfferCriteria_error_orderIndex_ptr,
              UnresolvedOfferCriteria_error_selector,
              UnusedItemParameters_error_length,
              UnusedItemParameters_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @dev Reverts the current transaction with a "BadFraction" error message.
           */
          function _revertBadFraction() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, BadFraction_error_selector)
                  // revert(abi.encodeWithSignature("BadFraction()"))
                  revert(Error_selector_offset, BadFraction_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with a "ConsiderationNotMet" error
           *      message, including the provided order index, consideration index, and
           *      shortfall amount.
           *
           * @param orderIndex         The index of the order that did not meet the
           *                           consideration criteria.
           * @param considerationIndex The index of the consideration item that did not
           *                           meet its criteria.
           * @param shortfallAmount    The amount by which the consideration criteria were
           *                           not met.
           */
          function _revertConsiderationNotMet(
              uint256 orderIndex,
              uint256 considerationIndex,
              uint256 shortfallAmount
          ) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, ConsiderationNotMet_error_selector)
                  // Store arguments.
                  mstore(ConsiderationNotMet_error_orderIndex_ptr, orderIndex)
                  mstore(
                      ConsiderationNotMet_error_considerationIndex_ptr,
                      considerationIndex
                  )
                  mstore(ConsiderationNotMet_error_shortfallAmount_ptr, shortfallAmount)
                  // revert(abi.encodeWithSignature(
                  //     "ConsiderationNotMet(uint256,uint256,uint256)",
                  //     orderIndex,
                  //     considerationIndex,
                  //     shortfallAmount
                  // ))
                  revert(Error_selector_offset, ConsiderationNotMet_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with a "CriteriaNotEnabledForItem" error
           *      message.
           */
          function _revertCriteriaNotEnabledForItem() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, CriteriaNotEnabledForItem_error_selector)
                  // revert(abi.encodeWithSignature("CriteriaNotEnabledForItem()"))
                  revert(Error_selector_offset, CriteriaNotEnabledForItem_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an
           *      "InsufficientNativeTokensSupplied" error message.
           */
          function _revertInsufficientNativeTokensSupplied() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InsufficientNativeTokensSupplied_error_selector)
                  // revert(abi.encodeWithSignature("InsufficientNativeTokensSupplied()"))
                  revert(
                      Error_selector_offset,
                      InsufficientNativeTokensSupplied_error_length
                  )
              }
          }
          /**
           * @dev Reverts the current transaction with an
           *      "InvalidBasicOrderParameterEncoding" error message.
           */
          function _revertInvalidBasicOrderParameterEncoding() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidBasicOrderParameterEncoding_error_selector)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidBasicOrderParameterEncoding()"
                  // ))
                  revert(
                      Error_selector_offset,
                      InvalidBasicOrderParameterEncoding_error_length
                  )
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidCallToConduit" error
           *      message, including the provided address of the conduit that was called
           *      improperly.
           *
           * @param conduit The address of the conduit that was called improperly.
           */
          function _revertInvalidCallToConduit(address conduit) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidCallToConduit_error_selector)
                  // Store argument.
                  mstore(InvalidCallToConduit_error_conduit_ptr, conduit)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidCallToConduit(address)",
                  //     conduit
                  // ))
                  revert(Error_selector_offset, InvalidCallToConduit_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "CannotCancelOrder" error
           *      message.
           */
          function _revertCannotCancelOrder() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, CannotCancelOrder_error_selector)
                  // revert(abi.encodeWithSignature("CannotCancelOrder()"))
                  revert(Error_selector_offset, CannotCancelOrder_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidConduit" error message,
           *      including the provided key and address of the invalid conduit.
           *
           * @param conduitKey    The key of the invalid conduit.
           * @param conduit       The address of the invalid conduit.
           */
          function _revertInvalidConduit(bytes32 conduitKey, address conduit) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidConduit_error_selector)
                  // Store arguments.
                  mstore(InvalidConduit_error_conduitKey_ptr, conduitKey)
                  mstore(InvalidConduit_error_conduit_ptr, conduit)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidConduit(bytes32,address)",
                  //     conduitKey,
                  //     conduit
                  // ))
                  revert(Error_selector_offset, InvalidConduit_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidERC721TransferAmount"
           *      error message.
           *
           * @param amount The invalid amount.
           */
          function _revertInvalidERC721TransferAmount(uint256 amount) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidERC721TransferAmount_error_selector)
                  // Store argument.
                  mstore(InvalidERC721TransferAmount_error_amount_ptr, amount)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidERC721TransferAmount(uint256)",
                  //     amount
                  // ))
                  revert(Error_selector_offset, InvalidERC721TransferAmount_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidMsgValue" error message,
           *      including the invalid value that was sent in the transaction's
           *      `msg.value` field.
           *
           * @param value The invalid value that was sent in the transaction's `msg.value`
           *              field.
           */
          function _revertInvalidMsgValue(uint256 value) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidMsgValue_error_selector)
                  // Store argument.
                  mstore(InvalidMsgValue_error_value_ptr, value)
                  // revert(abi.encodeWithSignature("InvalidMsgValue(uint256)", value))
                  revert(Error_selector_offset, InvalidMsgValue_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidNativeOfferItem" error
           *      message.
           */
          function _revertInvalidNativeOfferItem() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidNativeOfferItem_error_selector)
                  // revert(abi.encodeWithSignature("InvalidNativeOfferItem()"))
                  revert(Error_selector_offset, InvalidNativeOfferItem_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidProof" error message.
           */
          function _revertInvalidProof() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidProof_error_selector)
                  // revert(abi.encodeWithSignature("InvalidProof()"))
                  revert(Error_selector_offset, InvalidProof_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidContractOrder" error
           *      message.
           *
           * @param orderHash The hash of the contract order that caused the error.
           */
          function _revertInvalidContractOrder(bytes32 orderHash) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidContractOrder_error_selector)
                  // Store arguments.
                  mstore(InvalidContractOrder_error_orderHash_ptr, orderHash)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidContractOrder(bytes32)",
                  //     orderHash
                  // ))
                  revert(Error_selector_offset, InvalidContractOrder_error_length)
              }
          }
          /**
           * @dev Reverts the current transaction with an "InvalidTime" error message.
           *
           * @param startTime       The time at which the order becomes active.
           * @param endTime         The time at which the order becomes inactive.
           */
          function _revertInvalidTime(uint256 startTime, uint256 endTime) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, InvalidTime_error_selector)
                  // Store arguments.
                  mstore(InvalidTime_error_startTime_ptr, startTime)
                  mstore(InvalidTime_error_endTime_ptr, endTime)
                  // revert(abi.encodeWithSignature(
                  //     "InvalidTime(uint256,uint256)",
                  //     startTime,
                  //     endTime
                  // ))
                  revert(Error_selector_offset, InvalidTime_error_length)
              }
          }
          /**
           * @dev Reverts execution with a
           *      "MismatchedFulfillmentOfferAndConsiderationComponents" error message.
           *
           * @param fulfillmentIndex         The index of the fulfillment that caused the
           *                                 error.
           */
          function _revertMismatchedFulfillmentOfferAndConsiderationComponents(
              uint256 fulfillmentIndex
          ) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, MismatchedOfferAndConsiderationComponents_error_selector)
                  // Store fulfillment index argument.
                  mstore(
                      MismatchedOfferAndConsiderationComponents_error_idx_ptr,
                      fulfillmentIndex
                  )
                  // revert(abi.encodeWithSignature(
                  //     "MismatchedFulfillmentOfferAndConsiderationComponents(uint256)",
                  //     fulfillmentIndex
                  // ))
                  revert(
                      Error_selector_offset,
                      MismatchedOfferAndConsiderationComponents_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with a "MissingFulfillmentComponentOnAggregation"
           *       error message.
           *
           * @param side The side of the fulfillment component that is missing (0 for
           *             offer, 1 for consideration).
           *
           */
          function _revertMissingFulfillmentComponentOnAggregation(Side side) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, MissingFulfillmentComponentOnAggregation_error_selector)
                  // Store argument.
                  mstore(MissingFulfillmentComponentOnAggregation_error_side_ptr, side)
                  // revert(abi.encodeWithSignature(
                  //     "MissingFulfillmentComponentOnAggregation(uint8)",
                  //     side
                  // ))
                  revert(
                      Error_selector_offset,
                      MissingFulfillmentComponentOnAggregation_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with a "MissingOriginalConsiderationItems" error
           *      message.
           */
          function _revertMissingOriginalConsiderationItems() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, MissingOriginalConsiderationItems_error_selector)
                  // revert(abi.encodeWithSignature(
                  //     "MissingOriginalConsiderationItems()"
                  // ))
                  revert(
                      Error_selector_offset,
                      MissingOriginalConsiderationItems_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with a "NoReentrantCalls" error message.
           */
          function _revertNoReentrantCalls() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, NoReentrantCalls_error_selector)
                  // revert(abi.encodeWithSignature("NoReentrantCalls()"))
                  revert(Error_selector_offset, NoReentrantCalls_error_length)
              }
          }
          /**
           * @dev Reverts execution with a "NoSpecifiedOrdersAvailable" error message.
           */
          function _revertNoSpecifiedOrdersAvailable() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, NoSpecifiedOrdersAvailable_error_selector)
                  // revert(abi.encodeWithSignature("NoSpecifiedOrdersAvailable()"))
                  revert(Error_selector_offset, NoSpecifiedOrdersAvailable_error_length)
              }
          }
          /**
           * @dev Reverts execution with a "OfferAndConsiderationRequiredOnFulfillment"
           *      error message.
           */
          function _revertOfferAndConsiderationRequiredOnFulfillment() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, OfferAndConsiderationRequiredOnFulfillment_error_selector)
                  // revert(abi.encodeWithSignature(
                  //     "OfferAndConsiderationRequiredOnFulfillment()"
                  // ))
                  revert(
                      Error_selector_offset,
                      OfferAndConsiderationRequiredOnFulfillment_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with an "OrderAlreadyFilled" error message.
           *
           * @param orderHash The hash of the order that has already been filled.
           */
          function _revertOrderAlreadyFilled(bytes32 orderHash) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, OrderAlreadyFilled_error_selector)
                  // Store argument.
                  mstore(OrderAlreadyFilled_error_orderHash_ptr, orderHash)
                  // revert(abi.encodeWithSignature(
                  //     "OrderAlreadyFilled(bytes32)",
                  //     orderHash
                  // ))
                  revert(Error_selector_offset, OrderAlreadyFilled_error_length)
              }
          }
          /**
           * @dev Reverts execution with an "OrderCriteriaResolverOutOfRange" error
           *      message.
           *
           * @param side The side of the criteria that is missing (0 for offer, 1 for
           *             consideration).
           *
           */
          function _revertOrderCriteriaResolverOutOfRange(Side side) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, OrderCriteriaResolverOutOfRange_error_selector)
                  // Store argument.
                  mstore(OrderCriteriaResolverOutOfRange_error_side_ptr, side)
                  // revert(abi.encodeWithSignature(
                  //     "OrderCriteriaResolverOutOfRange(uint8)",
                  //     side
                  // ))
                  revert(
                      Error_selector_offset,
                      OrderCriteriaResolverOutOfRange_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with an "OrderIsCancelled" error message.
           *
           * @param orderHash The hash of the order that has already been cancelled.
           */
          function _revertOrderIsCancelled(bytes32 orderHash) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, OrderIsCancelled_error_selector)
                  // Store argument.
                  mstore(OrderIsCancelled_error_orderHash_ptr, orderHash)
                  // revert(abi.encodeWithSignature(
                  //     "OrderIsCancelled(bytes32)",
                  //     orderHash
                  // ))
                  revert(Error_selector_offset, OrderIsCancelled_error_length)
              }
          }
          /**
           * @dev Reverts execution with an "OrderPartiallyFilled" error message.
           *
           * @param orderHash The hash of the order that has already been partially
           *                  filled.
           */
          function _revertOrderPartiallyFilled(bytes32 orderHash) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, OrderPartiallyFilled_error_selector)
                  // Store argument.
                  mstore(OrderPartiallyFilled_error_orderHash_ptr, orderHash)
                  // revert(abi.encodeWithSignature(
                  //     "OrderPartiallyFilled(bytes32)",
                  //     orderHash
                  // ))
                  revert(Error_selector_offset, OrderPartiallyFilled_error_length)
              }
          }
          /**
           * @dev Reverts execution with a "PartialFillsNotEnabledForOrder" error message.
           */
          function _revertPartialFillsNotEnabledForOrder() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, PartialFillsNotEnabledForOrder_error_selector)
                  // revert(abi.encodeWithSignature("PartialFillsNotEnabledForOrder()"))
                  revert(
                      Error_selector_offset,
                      PartialFillsNotEnabledForOrder_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with an "UnresolvedConsiderationCriteria" error
           *      message.
           */
          function _revertUnresolvedConsiderationCriteria(
              uint256 orderIndex,
              uint256 considerationIndex
          ) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, UnresolvedConsiderationCriteria_error_selector)
                  // Store orderIndex and considerationIndex arguments.
                  mstore(UnresolvedConsiderationCriteria_error_orderIndex_ptr, orderIndex)
                  mstore(
                      UnresolvedConsiderationCriteria_error_considerationIdx_ptr,
                      considerationIndex
                  )
                  // revert(abi.encodeWithSignature(
                  //     "UnresolvedConsiderationCriteria(uint256, uint256)",
                  //     orderIndex,
                  //     considerationIndex
                  // ))
                  revert(
                      Error_selector_offset,
                      UnresolvedConsiderationCriteria_error_length
                  )
              }
          }
          /**
           * @dev Reverts execution with an "UnresolvedOfferCriteria" error message.
           */
          function _revertUnresolvedOfferCriteria(
              uint256 orderIndex,
              uint256 offerIndex
          ) pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, UnresolvedOfferCriteria_error_selector)
                  // Store arguments.
                  mstore(UnresolvedOfferCriteria_error_orderIndex_ptr, orderIndex)
                  mstore(UnresolvedOfferCriteria_error_offerIndex_ptr, offerIndex)
                  // revert(abi.encodeWithSignature(
                  //     "UnresolvedOfferCriteria(uint256, uint256)",
                  //     orderIndex,
                  //     offerIndex
                  // ))
                  revert(Error_selector_offset, UnresolvedOfferCriteria_error_length)
              }
          }
          /**
           * @dev Reverts execution with an "UnusedItemParameters" error message.
           */
          function _revertUnusedItemParameters() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, UnusedItemParameters_error_selector)
                  // revert(abi.encodeWithSignature("UnusedItemParameters()"))
                  revert(Error_selector_offset, UnusedItemParameters_error_length)
              }
          }
          /**
           * @dev Reverts execution with a "ConsiderationLengthNotEqualToTotalOriginal"
           *      error message.
           */
          function _revertConsiderationLengthNotEqualToTotalOriginal() pure {
              assembly {
                  // Store left-padded selector with push4 (reduces bytecode),
                  // mem[28:32] = selector
                  mstore(0, ConsiderationLengthNotEqualToTotalOriginal_error_selector)
                  // revert(abi.encodeWithSignature(
                  //     "ConsiderationLengthNotEqualToTotalOriginal()"
                  // ))
                  revert(
                      Error_selector_offset,
                      ConsiderationLengthNotEqualToTotalOriginal_error_length
                  )
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          enum OrderType {
              // 0: no partial fills, anyone can execute
              FULL_OPEN,
              // 1: partial fills supported, anyone can execute
              PARTIAL_OPEN,
              // 2: no partial fills, only offerer or zone can execute
              FULL_RESTRICTED,
              // 3: partial fills supported, only offerer or zone can execute
              PARTIAL_RESTRICTED,
              // 4: contract order type
              CONTRACT
          }
          enum BasicOrderType {
              // 0: no partial fills, anyone can execute
              ETH_TO_ERC721_FULL_OPEN,
              // 1: partial fills supported, anyone can execute
              ETH_TO_ERC721_PARTIAL_OPEN,
              // 2: no partial fills, only offerer or zone can execute
              ETH_TO_ERC721_FULL_RESTRICTED,
              // 3: partial fills supported, only offerer or zone can execute
              ETH_TO_ERC721_PARTIAL_RESTRICTED,
              // 4: no partial fills, anyone can execute
              ETH_TO_ERC1155_FULL_OPEN,
              // 5: partial fills supported, anyone can execute
              ETH_TO_ERC1155_PARTIAL_OPEN,
              // 6: no partial fills, only offerer or zone can execute
              ETH_TO_ERC1155_FULL_RESTRICTED,
              // 7: partial fills supported, only offerer or zone can execute
              ETH_TO_ERC1155_PARTIAL_RESTRICTED,
              // 8: no partial fills, anyone can execute
              ERC20_TO_ERC721_FULL_OPEN,
              // 9: partial fills supported, anyone can execute
              ERC20_TO_ERC721_PARTIAL_OPEN,
              // 10: no partial fills, only offerer or zone can execute
              ERC20_TO_ERC721_FULL_RESTRICTED,
              // 11: partial fills supported, only offerer or zone can execute
              ERC20_TO_ERC721_PARTIAL_RESTRICTED,
              // 12: no partial fills, anyone can execute
              ERC20_TO_ERC1155_FULL_OPEN,
              // 13: partial fills supported, anyone can execute
              ERC20_TO_ERC1155_PARTIAL_OPEN,
              // 14: no partial fills, only offerer or zone can execute
              ERC20_TO_ERC1155_FULL_RESTRICTED,
              // 15: partial fills supported, only offerer or zone can execute
              ERC20_TO_ERC1155_PARTIAL_RESTRICTED,
              // 16: no partial fills, anyone can execute
              ERC721_TO_ERC20_FULL_OPEN,
              // 17: partial fills supported, anyone can execute
              ERC721_TO_ERC20_PARTIAL_OPEN,
              // 18: no partial fills, only offerer or zone can execute
              ERC721_TO_ERC20_FULL_RESTRICTED,
              // 19: partial fills supported, only offerer or zone can execute
              ERC721_TO_ERC20_PARTIAL_RESTRICTED,
              // 20: no partial fills, anyone can execute
              ERC1155_TO_ERC20_FULL_OPEN,
              // 21: partial fills supported, anyone can execute
              ERC1155_TO_ERC20_PARTIAL_OPEN,
              // 22: no partial fills, only offerer or zone can execute
              ERC1155_TO_ERC20_FULL_RESTRICTED,
              // 23: partial fills supported, only offerer or zone can execute
              ERC1155_TO_ERC20_PARTIAL_RESTRICTED
          }
          enum BasicOrderRouteType {
              // 0: provide Ether (or other native token) to receive offered ERC721 item.
              ETH_TO_ERC721,
              // 1: provide Ether (or other native token) to receive offered ERC1155 item.
              ETH_TO_ERC1155,
              // 2: provide ERC20 item to receive offered ERC721 item.
              ERC20_TO_ERC721,
              // 3: provide ERC20 item to receive offered ERC1155 item.
              ERC20_TO_ERC1155,
              // 4: provide ERC721 item to receive offered ERC20 item.
              ERC721_TO_ERC20,
              // 5: provide ERC1155 item to receive offered ERC20 item.
              ERC1155_TO_ERC20
          }
          enum ItemType {
              // 0: ETH on mainnet, MATIC on polygon, etc.
              NATIVE,
              // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work)
              ERC20,
              // 2: ERC721 items
              ERC721,
              // 3: ERC1155 items
              ERC1155,
              // 4: ERC721 items where a number of tokenIds are supported
              ERC721_WITH_CRITERIA,
              // 5: ERC1155 items where a number of ids are supported
              ERC1155_WITH_CRITERIA
          }
          enum Side {
              // 0: Items that can be spent
              OFFER,
              // 1: Items that must be received
              CONSIDERATION
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { ItemType, Side } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              Execution,
              FulfillmentComponent,
              ReceivedItem
          } from "./ConsiderationStructs.sol";
          import {
              _revertMismatchedFulfillmentOfferAndConsiderationComponents,
              _revertMissingFulfillmentComponentOnAggregation,
              _revertOfferAndConsiderationRequiredOnFulfillment
          } from "./ConsiderationErrors.sol";
          import {
              FulfillmentApplicationErrors
          } from "../interfaces/FulfillmentApplicationErrors.sol";
          import {
              AdvancedOrder_numerator_offset,
              Common_amount_offset,
              Common_identifier_offset,
              Common_token_offset,
              Execution_conduit_offset,
              Execution_offerer_offset,
              Fulfillment_itemIndex_offset,
              OneWord,
              OneWordShift,
              OrderParameters_conduit_offset,
              OrderParameters_consideration_head_offset,
              OrderParameters_offer_head_offset,
              ReceivedItem_CommonParams_size,
              ReceivedItem_recipient_offset,
              ReceivedItem_size
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              InvalidFulfillmentComponentData_error_length,
              InvalidFulfillmentComponentData_error_selector,
              MissingItemAmount_error_length,
              MissingItemAmount_error_selector,
              Panic_arithmetic,
              Panic_error_code_ptr,
              Panic_error_length,
              Panic_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title FulfillmentApplier
           * @author 0age
           * @notice FulfillmentApplier contains logic related to applying fulfillments,
           *         both as part of order matching (where offer items are matched to
           *         consideration items) as well as fulfilling available orders (where
           *         order items and consideration items are independently aggregated).
           */
          contract FulfillmentApplier is FulfillmentApplicationErrors {
              /**
               * @dev Internal pure function to match offer items to consideration items
               *      on a group of orders via a supplied fulfillment.
               *
               * @param advancedOrders          The orders to match.
               * @param offerComponents         An array designating offer components to
               *                                match to consideration components.
               * @param considerationComponents An array designating consideration
               *                                components to match to offer components.
               *                                Note that each consideration amount must
               *                                be zero in order for the match operation
               *                                to be valid.
               * @param fulfillmentIndex        The index of the fulfillment being
               *                                applied.
               *
               * @return execution The transfer performed as a result of the fulfillment.
               */
              function _applyFulfillment(
                  AdvancedOrder[] memory advancedOrders,
                  FulfillmentComponent[] memory offerComponents,
                  FulfillmentComponent[] memory considerationComponents,
                  uint256 fulfillmentIndex
              ) internal pure returns (Execution memory execution) {
                  // Ensure 1+ of both offer and consideration components are supplied.
                  if (
                      offerComponents.length == 0 || considerationComponents.length == 0
                  ) {
                      _revertOfferAndConsiderationRequiredOnFulfillment();
                  }
                  // Declare a new Execution struct.
                  Execution memory considerationExecution;
                  // Validate & aggregate consideration items to new Execution object.
                  _aggregateValidFulfillmentConsiderationItems(
                      advancedOrders,
                      considerationComponents,
                      considerationExecution
                  );
                  // Retrieve the consideration item from the execution struct.
                  ReceivedItem memory considerationItem = considerationExecution.item;
                  // Skip aggregating offer items if no consideration items are available.
                  if (considerationItem.amount == 0) {
                      // Set the offerer and recipient to null address and the item type
                      // to a non-native item type if the execution amount is zero. This
                      // will cause the execution item to be skipped.
                      considerationExecution.offerer = address(0);
                      considerationExecution.item.recipient = payable(0);
                      considerationExecution.item.itemType = ItemType.ERC20;
                      return considerationExecution;
                  }
                  // Recipient does not need to be specified because it will always be set
                  // to that of the consideration.
                  // Validate & aggregate offer items to Execution object.
                  _aggregateValidFulfillmentOfferItems(
                      advancedOrders,
                      offerComponents,
                      execution
                  );
                  // Ensure offer & consideration item types, tokens, & identifiers match.
                  // (a != b || c != d || e != f) == (((a ^ b) | (c ^ d) | (e ^ f)) != 0),
                  // but the second expression requires less gas to evaluate.
                  if (
                      ((uint8(execution.item.itemType) ^
                          uint8(considerationItem.itemType)) |
                          (uint160(execution.item.token) ^
                              uint160(considerationItem.token)) |
                          (execution.item.identifier ^ considerationItem.identifier)) != 0
                  ) {
                      _revertMismatchedFulfillmentOfferAndConsiderationComponents(
                          fulfillmentIndex
                      );
                  }
                  // If total consideration amount exceeds the offer amount...
                  if (considerationItem.amount > execution.item.amount) {
                      // Retrieve the first consideration component from the fulfillment.
                      FulfillmentComponent memory targetComponent = (
                          considerationComponents[0]
                      );
                      // Skip underflow check as the conditional being true implies that
                      // considerationItem.amount > execution.item.amount.
                      unchecked {
                          // Add excess consideration item amount to original order array.
                          advancedOrders[targetComponent.orderIndex]
                              .parameters
                              .consideration[targetComponent.itemIndex]
                              .startAmount = (considerationItem.amount -
                              execution.item.amount);
                      }
                  } else {
                      // Retrieve the first offer component from the fulfillment.
                      FulfillmentComponent memory targetComponent = offerComponents[0];
                      // Skip underflow check as the conditional being false implies that
                      // execution.item.amount >= considerationItem.amount.
                      unchecked {
                          // Add excess offer item amount to the original array of orders.
                          advancedOrders[targetComponent.orderIndex]
                              .parameters
                              .offer[targetComponent.itemIndex]
                              .startAmount = (execution.item.amount -
                              considerationItem.amount);
                      }
                      // Reduce total offer amount to equal the consideration amount.
                      execution.item.amount = considerationItem.amount;
                  }
                  // Reuse consideration recipient.
                  execution.item.recipient = considerationItem.recipient;
                  // Return the final execution that will be triggered for relevant items.
                  return execution; // Execution(considerationItem, offerer, conduitKey);
              }
              /**
               * @dev Internal view function to aggregate offer or consideration items
               *      from a group of orders into a single execution via a supplied array
               *      of fulfillment components. Items that are not available to aggregate
               *      will not be included in the aggregated execution.
               *
               * @param advancedOrders        The orders to aggregate.
               * @param side                  The side (i.e. offer or consideration).
               * @param fulfillmentComponents An array designating item components to
               *                              aggregate if part of an available order.
               * @param fulfillerConduitKey   A bytes32 value indicating what conduit, if
               *                              any, to source the fulfiller's token
               *                              approvals from. The zero hash signifies that
               *                              no conduit should be used, with approvals
               *                              set directly on this contract.
               * @param recipient             The intended recipient for all received
               *                              items.
               *
               * @return execution The transfer performed as a result of the fulfillment.
               */
              function _aggregateAvailable(
                  AdvancedOrder[] memory advancedOrders,
                  Side side,
                  FulfillmentComponent[] memory fulfillmentComponents,
                  bytes32 fulfillerConduitKey,
                  address recipient
              ) internal view returns (Execution memory execution) {
                  // Skip overflow / underflow checks; conditions checked or unreachable.
                  unchecked {
                      // Retrieve fulfillment components array length and place on stack.
                      // Ensure at least one fulfillment component has been supplied.
                      if (fulfillmentComponents.length == 0) {
                          _revertMissingFulfillmentComponentOnAggregation(side);
                      }
                      // Retrieve the received item on the execution being returned.
                      ReceivedItem memory item = execution.item;
                      // If the fulfillment components are offer components...
                      if (side == Side.OFFER) {
                          // Set the supplied recipient on the execution item.
                          item.recipient = payable(recipient);
                          // Return execution for aggregated items provided by offerer.
                          _aggregateValidFulfillmentOfferItems(
                              advancedOrders,
                              fulfillmentComponents,
                              execution
                          );
                      } else {
                          // Otherwise, fulfillment components are consideration
                          // components. Return execution for aggregated items provided by
                          // the fulfiller.
                          _aggregateValidFulfillmentConsiderationItems(
                              advancedOrders,
                              fulfillmentComponents,
                              execution
                          );
                          // Set the caller as the offerer on the execution.
                          execution.offerer = msg.sender;
                          // Set fulfiller conduit key as the conduit key on execution.
                          execution.conduitKey = fulfillerConduitKey;
                      }
                      // Set the offerer and recipient to null address and the item type
                      // to a non-native item type if the execution amount is zero. This
                      // will cause the execution item to be skipped.
                      if (item.amount == 0) {
                          execution.offerer = address(0);
                          item.recipient = payable(0);
                          item.itemType = ItemType.ERC20;
                      }
                  }
              }
              /**
               * @dev Internal pure function to aggregate a group of offer items using
               *      supplied directives on which component items are candidates for
               *      aggregation, skipping items on orders that are not available.
               *
               * @param advancedOrders  The orders to aggregate offer items from.
               * @param offerComponents An array of FulfillmentComponent structs
               *                        indicating the order index and item index of each
               *                        candidate offer item for aggregation.
               * @param execution       The execution to apply the aggregation to.
               */
              function _aggregateValidFulfillmentOfferItems(
                  AdvancedOrder[] memory advancedOrders,
                  FulfillmentComponent[] memory offerComponents,
                  Execution memory execution
              ) internal pure {
                  assembly {
                      // Declare a variable for the final aggregated item amount.
                      let amount
                      // Declare a variable to track errors encountered with amount.
                      let errorBuffer
                      // Declare a variable for the hash of itemType, token, & identifier.
                      let dataHash
                      // Iterate over each offer component.
                      for {
                          // Create variable to track position in offerComponents head.
                          let fulfillmentHeadPtr := offerComponents
                          // Get position one word past last element in head of array.
                          let endPtr := add(
                              offerComponents,
                              shl(OneWordShift, mload(offerComponents))
                          )
                      } lt(fulfillmentHeadPtr, endPtr) {
                      } {
                          // Increment position in considerationComponents head.
                          fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
                          // Retrieve the order index using the fulfillment pointer.
                          let orderIndex := mload(mload(fulfillmentHeadPtr))
                          // Ensure that the order index is not out of range.
                          if iszero(lt(orderIndex, mload(advancedOrders))) {
                              throwInvalidFulfillmentComponentData()
                          }
                          // Read advancedOrders[orderIndex] pointer from its array head.
                          let orderPtr := mload(
                              // Calculate head position of advancedOrders[orderIndex].
                              add(
                                  add(advancedOrders, OneWord),
                                  shl(OneWordShift, orderIndex)
                              )
                          )
                          // Read the pointer to OrderParameters from the AdvancedOrder.
                          let paramsPtr := mload(orderPtr)
                          // Retrieve item index using an offset of fulfillment pointer.
                          let itemIndex := mload(
                              add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
                          )
                          let offerItemPtr
                          {
                              // Load the offer array pointer.
                              let offerArrPtr := mload(
                                  add(paramsPtr, OrderParameters_offer_head_offset)
                              )
                              // If the offer item index is out of range or the numerator
                              // is zero, skip this item.
                              if or(
                                  iszero(lt(itemIndex, mload(offerArrPtr))),
                                  iszero(
                                      mload(add(orderPtr, AdvancedOrder_numerator_offset))
                                  )
                              ) {
                                  continue
                              }
                              // Retrieve offer item pointer using the item index.
                              offerItemPtr := mload(
                                  add(
                                      // Get pointer to beginning of receivedItem.
                                      add(offerArrPtr, OneWord),
                                      // Calculate offset to pointer for desired order.
                                      shl(OneWordShift, itemIndex)
                                  )
                              )
                          }
                          // Declare a separate scope for the amount update.
                          {
                              // Retrieve amount pointer using consideration item pointer.
                              let amountPtr := add(offerItemPtr, Common_amount_offset)
                              // Add offer item amount to execution amount.
                              let newAmount := add(amount, mload(amountPtr))
                              // Update error buffer:
                              // 1 = zero amount, 2 = overflow, 3 = both.
                              errorBuffer := or(
                                  errorBuffer,
                                  or(
                                      shl(1, lt(newAmount, amount)),
                                      iszero(mload(amountPtr))
                                  )
                              )
                              // Update the amount to the new, summed amount.
                              amount := newAmount
                              // Zero out amount on original item to indicate it is spent.
                              mstore(amountPtr, 0)
                          }
                          // Retrieve ReceivedItem pointer from Execution.
                          let receivedItem := mload(execution)
                          // Check if this is the first valid fulfillment item.
                          switch iszero(dataHash)
                          case 1 {
                              // On first valid item, populate the received item in memory
                              // for later comparison.
                              // Set the item type on the received item.
                              mstore(receivedItem, mload(offerItemPtr))
                              // Set the token on the received item.
                              mstore(
                                  add(receivedItem, Common_token_offset),
                                  mload(add(offerItemPtr, Common_token_offset))
                              )
                              // Set the identifier on the received item.
                              mstore(
                                  add(receivedItem, Common_identifier_offset),
                                  mload(add(offerItemPtr, Common_identifier_offset))
                              )
                              // Set offerer on returned execution using order pointer.
                              mstore(
                                  add(execution, Execution_offerer_offset),
                                  mload(paramsPtr)
                              )
                              // Set execution conduitKey via order pointer offset.
                              mstore(
                                  add(execution, Execution_conduit_offset),
                                  mload(add(paramsPtr, OrderParameters_conduit_offset))
                              )
                              // Calculate the hash of (itemType, token, identifier).
                              dataHash := keccak256(
                                  receivedItem,
                                  ReceivedItem_CommonParams_size
                              )
                              // If component index > 0, swap component pointer with
                              // pointer to first component so that any remainder after
                              // fulfillment can be added back to the first item.
                              let firstFulfillmentHeadPtr := add(offerComponents, OneWord)
                              if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) {
                                  let firstFulfillmentPtr := mload(
                                      firstFulfillmentHeadPtr
                                  )
                                  let fulfillmentPtr := mload(fulfillmentHeadPtr)
                                  mstore(firstFulfillmentHeadPtr, fulfillmentPtr)
                              }
                          }
                          default {
                              // Compare every subsequent item to the first.
                              if or(
                                  or(
                                      // The offerer must match on both items.
                                      xor(
                                          mload(paramsPtr),
                                          mload(add(execution, Execution_offerer_offset))
                                      ),
                                      // The conduit key must match on both items.
                                      xor(
                                          mload(
                                              add(
                                                  paramsPtr,
                                                  OrderParameters_conduit_offset
                                              )
                                          ),
                                          mload(add(execution, Execution_conduit_offset))
                                      )
                                  ),
                                  // The itemType, token, and identifier must match.
                                  xor(
                                      dataHash,
                                      keccak256(
                                          offerItemPtr,
                                          ReceivedItem_CommonParams_size
                                      )
                                  )
                              ) {
                                  // Throw if any of the requirements are not met.
                                  throwInvalidFulfillmentComponentData()
                              }
                          }
                      }
                      // Write final amount to execution.
                      mstore(add(mload(execution), Common_amount_offset), amount)
                      // Determine whether the error buffer contains a nonzero error code.
                      if errorBuffer {
                          // If errorBuffer is 1, an item had an amount of zero.
                          if eq(errorBuffer, 1) {
                              // Store left-padded selector with push4 (reduces bytecode)
                              // mem[28:32] = selector
                              mstore(0, MissingItemAmount_error_selector)
                              // revert(abi.encodeWithSignature("MissingItemAmount()"))
                              revert(
                                  Error_selector_offset,
                                  MissingItemAmount_error_length
                              )
                          }
                          // If errorBuffer is not 1 or 0, the sum overflowed.
                          // Panic!
                          throwOverflow()
                      }
                      // Declare function for reverts on invalid fulfillment data.
                      function throwInvalidFulfillmentComponentData() {
                          // Store left-padded selector (uses push4 and reduces code size)
                          mstore(0, InvalidFulfillmentComponentData_error_selector)
                          // revert(abi.encodeWithSignature(
                          //     "InvalidFulfillmentComponentData()"
                          // ))
                          revert(
                              Error_selector_offset,
                              InvalidFulfillmentComponentData_error_length
                          )
                      }
                      // Declare function for reverts due to arithmetic overflows.
                      function throwOverflow() {
                          // Store the Panic error signature.
                          mstore(0, Panic_error_selector)
                          // Store the arithmetic (0x11) panic code.
                          mstore(Panic_error_code_ptr, Panic_arithmetic)
                          // revert(abi.encodeWithSignature("Panic(uint256)", 0x11))
                          revert(Error_selector_offset, Panic_error_length)
                      }
                  }
              }
              /**
               * @dev Internal pure function to aggregate a group of consideration items
               *      using supplied directives on which component items are candidates
               *      for aggregation, skipping items on orders that are not available.
               *      Note that this function depends on memory layout affected by an
               *      earlier call to _validateOrdersAndPrepareToFulfill.  The memory for
               *      the consideration arrays needs to be updated before calling
               *      _aggregateValidFulfillmentConsiderationItems.
               *      _validateOrdersAndPrepareToFulfill is called in _matchAdvancedOrders
               *      and _fulfillAvailableAdvancedOrders in the current version.
               *
               * @param advancedOrders          The orders to aggregate consideration
               *                                items from.
               * @param considerationComponents An array of FulfillmentComponent structs
               *                                indicating the order index and item index
               *                                of each candidate consideration item for
               *                                aggregation.
               * @param execution               The execution to apply the aggregation to.
               */
              function _aggregateValidFulfillmentConsiderationItems(
                  AdvancedOrder[] memory advancedOrders,
                  FulfillmentComponent[] memory considerationComponents,
                  Execution memory execution
              ) internal pure {
                  // Utilize assembly in order to efficiently aggregate the items.
                  assembly {
                      // Declare a variable for the final aggregated item amount.
                      let amount
                      // Create variable to track errors encountered with amount.
                      let errorBuffer
                      // Declare variable for hash(itemType, token, identifier, recipient)
                      let dataHash
                      // Iterate over each consideration component.
                      for {
                          // Track position in considerationComponents head.
                          let fulfillmentHeadPtr := considerationComponents
                          // Get position one word past last element in head of array.
                          let endPtr := add(
                              considerationComponents,
                              shl(OneWordShift, mload(considerationComponents))
                          )
                      } lt(fulfillmentHeadPtr, endPtr) {
                      } {
                          // Increment position in considerationComponents head.
                          fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
                          // Retrieve the order index using the fulfillment pointer.
                          let orderIndex := mload(mload(fulfillmentHeadPtr))
                          // Ensure that the order index is not out of range.
                          if iszero(lt(orderIndex, mload(advancedOrders))) {
                              throwInvalidFulfillmentComponentData()
                          }
                          // Read advancedOrders[orderIndex] pointer from its array head.
                          let orderPtr := mload(
                              // Calculate head position of advancedOrders[orderIndex].
                              add(
                                  add(advancedOrders, OneWord),
                                  shl(OneWordShift, orderIndex)
                              )
                          )
                          // Retrieve item index using an offset of fulfillment pointer.
                          let itemIndex := mload(
                              add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
                          )
                          let considerationItemPtr
                          {
                              // Load consideration array pointer.
                              let considerationArrPtr := mload(
                                  add(
                                      // Read OrderParameters pointer from AdvancedOrder.
                                      mload(orderPtr),
                                      OrderParameters_consideration_head_offset
                                  )
                              )
                              // If the consideration item index is out of range or the
                              // numerator is zero, skip this item.
                              if or(
                                  iszero(lt(itemIndex, mload(considerationArrPtr))),
                                  iszero(
                                      mload(add(orderPtr, AdvancedOrder_numerator_offset))
                                  )
                              ) {
                                  continue
                              }
                              // Retrieve consideration item pointer using the item index.
                              considerationItemPtr := mload(
                                  add(
                                      // Get pointer to beginning of receivedItem.
                                      add(considerationArrPtr, OneWord),
                                      // Calculate offset to pointer for desired order.
                                      shl(OneWordShift, itemIndex)
                                  )
                              )
                          }
                          // Declare a separate scope for the amount update.
                          {
                              // Retrieve amount pointer using consideration item pointer.
                              let amountPtr := add(
                                  considerationItemPtr,
                                  Common_amount_offset
                              )
                              // Add consideration item amount to execution amount.
                              let newAmount := add(amount, mload(amountPtr))
                              // Update error buffer:
                              // 1 = zero amount, 2 = overflow, 3 = both.
                              errorBuffer := or(
                                  errorBuffer,
                                  or(
                                      shl(1, lt(newAmount, amount)),
                                      iszero(mload(amountPtr))
                                  )
                              )
                              // Update the amount to the new, summed amount.
                              amount := newAmount
                              // Zero out original item amount to indicate it is credited.
                              mstore(amountPtr, 0)
                          }
                          // Retrieve ReceivedItem pointer from Execution.
                          let receivedItem := mload(execution)
                          switch iszero(dataHash)
                          case 1 {
                              // On first valid item, populate the received item in
                              // memory for later comparison.
                              // Set the item type on the received item.
                              mstore(receivedItem, mload(considerationItemPtr))
                              // Set the token on the received item.
                              mstore(
                                  add(receivedItem, Common_token_offset),
                                  mload(add(considerationItemPtr, Common_token_offset))
                              )
                              // Set the identifier on the received item.
                              mstore(
                                  add(receivedItem, Common_identifier_offset),
                                  mload(
                                      add(considerationItemPtr, Common_identifier_offset)
                                  )
                              )
                              // Set the recipient on the received item. Note that this
                              // depends on the memory layout established by the
                              // _validateOrdersAndPrepareToFulfill function.
                              mstore(
                                  add(receivedItem, ReceivedItem_recipient_offset),
                                  mload(
                                      add(
                                          considerationItemPtr,
                                          ReceivedItem_recipient_offset
                                      )
                                  )
                              )
                              // Calculate the hash of (itemType, token, identifier,
                              // recipient). This is run after amount is set to zero, so
                              // there will be one blank word after identifier included in
                              // the hash buffer.
                              dataHash := keccak256(
                                  considerationItemPtr,
                                  ReceivedItem_size
                              )
                              // If component index > 0, swap component pointer with
                              // pointer to first component so that any remainder after
                              // fulfillment can be added back to the first item.
                              let firstFulfillmentHeadPtr := add(
                                  considerationComponents,
                                  OneWord
                              )
                              if xor(firstFulfillmentHeadPtr, fulfillmentHeadPtr) {
                                  let firstFulfillmentPtr := mload(
                                      firstFulfillmentHeadPtr
                                  )
                                  let fulfillmentPtr := mload(fulfillmentHeadPtr)
                                  mstore(firstFulfillmentHeadPtr, fulfillmentPtr)
                              }
                          }
                          default {
                              // Compare every subsequent item to the first; the item
                              // type, token, identifier and recipient must match.
                              if xor(
                                  dataHash,
                                  // Calculate the hash of (itemType, token, identifier,
                                  // recipient). This is run after amount is set to zero,
                                  // so there will be one blank word after identifier
                                  // included in the hash buffer.
                                  keccak256(considerationItemPtr, ReceivedItem_size)
                              ) {
                                  // Throw if any of the requirements are not met.
                                  throwInvalidFulfillmentComponentData()
                              }
                          }
                      }
                      // Retrieve ReceivedItem pointer from Execution.
                      let receivedItem := mload(execution)
                      // Write final amount to execution.
                      mstore(add(receivedItem, Common_amount_offset), amount)
                      // Determine whether the error buffer contains a nonzero error code.
                      if errorBuffer {
                          // If errorBuffer is 1, an item had an amount of zero.
                          if eq(errorBuffer, 1) {
                              // Store left-padded selector with push4, mem[28:32]
                              mstore(0, MissingItemAmount_error_selector)
                              // revert(abi.encodeWithSignature("MissingItemAmount()"))
                              revert(
                                  Error_selector_offset,
                                  MissingItemAmount_error_length
                              )
                          }
                          // If errorBuffer is not 1 or 0, `amount` overflowed.
                          // Panic!
                          throwOverflow()
                      }
                      // Declare function for reverts on invalid fulfillment data.
                      function throwInvalidFulfillmentComponentData() {
                          // Store the InvalidFulfillmentComponentData error signature.
                          mstore(0, InvalidFulfillmentComponentData_error_selector)
                          // revert(abi.encodeWithSignature(
                          //     "InvalidFulfillmentComponentData()"
                          // ))
                          revert(
                              Error_selector_offset,
                              InvalidFulfillmentComponentData_error_length
                          )
                      }
                      // Declare function for reverts due to arithmetic overflows.
                      function throwOverflow() {
                          // Store the Panic error signature.
                          mstore(0, Panic_error_selector)
                          // Store the arithmetic (0x11) panic code.
                          mstore(Panic_error_code_ptr, Panic_arithmetic)
                          // revert(abi.encodeWithSignature("Panic(uint256)", 0x11))
                          revert(Error_selector_offset, Panic_error_length)
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { ItemType, OrderType } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              ConsiderationItem,
              CriteriaResolver,
              OfferItem,
              OrderParameters,
              ReceivedItem,
              SpentItem
          } from "./ConsiderationStructs.sol";
          import { BasicOrderFulfiller } from "./BasicOrderFulfiller.sol";
          import { CriteriaResolution } from "./CriteriaResolution.sol";
          import { AmountDeriver } from "./AmountDeriver.sol";
          import {
              _revertInsufficientNativeTokensSupplied,
              _revertInvalidNativeOfferItem
          } from "./ConsiderationErrors.sol";
          import {
              AccumulatorDisarmed,
              ConsiderationItem_recipient_offset,
              ReceivedItem_amount_offset,
              ReceivedItem_recipient_offset
          } from "./ConsiderationConstants.sol";
          /**
           * @title OrderFulfiller
           * @author 0age
           * @notice OrderFulfiller contains logic related to order fulfillment where a
           *         single order is being fulfilled and where basic order fulfillment is
           *         not available as an option.
           */
          contract OrderFulfiller is
              BasicOrderFulfiller,
              CriteriaResolution,
              AmountDeriver
          {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(
                  address conduitController
              ) BasicOrderFulfiller(conduitController) {}
              /**
               * @dev Internal function to validate an order and update its status, adjust
               *      prices based on current time, apply criteria resolvers, determine
               *      what portion to fill, and transfer relevant tokens.
               *
               * @param advancedOrder       The order to fulfill as well as the fraction
               *                            to fill. Note that all offer and consideration
               *                            components must divide with no remainder for
               *                            the partial fill to be valid.
               * @param criteriaResolvers   An array where each element contains a
               *                            reference to a specific offer or
               *                            consideration, a token identifier, and a proof
               *                            that the supplied token identifier is
               *                            contained in the order's merkle root. Note
               *                            that a criteria of zero indicates that any
               *                            (transferable) token identifier is valid and
               *                            that no proof needs to be supplied.
               * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
               *                            any, to source the fulfiller's token approvals
               *                            from. The zero hash signifies that no conduit
               *                            should be used, with direct approvals set on
               *                            Consideration.
               * @param recipient           The intended recipient for all received items.
               *
               * @return A boolean indicating whether the order has been fulfilled.
               */
              function _validateAndFulfillAdvancedOrder(
                  AdvancedOrder memory advancedOrder,
                  CriteriaResolver[] memory criteriaResolvers,
                  bytes32 fulfillerConduitKey,
                  address recipient
              ) internal returns (bool) {
                  // Ensure this function cannot be triggered during a reentrant call.
                  _setReentrancyGuard(
                      // Native tokens accepted during execution for contract order types.
                      advancedOrder.parameters.orderType == OrderType.CONTRACT
                  );
                  // Validate order, update status, and determine fraction to fill.
                  (
                      bytes32 orderHash,
                      uint256 fillNumerator,
                      uint256 fillDenominator
                  ) = _validateOrderAndUpdateStatus(advancedOrder, true);
                  // Create an array with length 1 containing the order.
                  AdvancedOrder[] memory advancedOrders = new AdvancedOrder[](1);
                  // Populate the order as the first and only element of the new array.
                  advancedOrders[0] = advancedOrder;
                  // Apply criteria resolvers using generated orders and details arrays.
                  _applyCriteriaResolvers(advancedOrders, criteriaResolvers);
                  // Retrieve the order parameters after applying criteria resolvers.
                  OrderParameters memory orderParameters = advancedOrders[0].parameters;
                  // Perform each item transfer with the appropriate fractional amount.
                  _applyFractionsAndTransferEach(
                      orderParameters,
                      fillNumerator,
                      fillDenominator,
                      fulfillerConduitKey,
                      recipient
                  );
                  // Declare empty bytes32 array and populate with the order hash.
                  bytes32[] memory orderHashes = new bytes32[](1);
                  orderHashes[0] = orderHash;
                  // Ensure restricted orders have a valid submitter or pass a zone check.
                  _assertRestrictedAdvancedOrderValidity(
                      advancedOrders[0],
                      orderHashes,
                      orderHash
                  );
                  // Emit an event signifying that the order has been fulfilled.
                  _emitOrderFulfilledEvent(
                      orderHash,
                      orderParameters.offerer,
                      orderParameters.zone,
                      recipient,
                      orderParameters.offer,
                      orderParameters.consideration
                  );
                  // Clear the reentrancy guard.
                  _clearReentrancyGuard();
                  return true;
              }
              /**
               * @dev Internal function to transfer each item contained in a given single
               *      order fulfillment after applying a respective fraction to the amount
               *      being transferred.
               *
               * @param orderParameters     The parameters for the fulfilled order.
               * @param numerator           A value indicating the portion of the order
               *                            that should be filled.
               * @param denominator         A value indicating the total order size.
               * @param fulfillerConduitKey A bytes32 value indicating what conduit, if
               *                            any, to source the fulfiller's token approvals
               *                            from. The zero hash signifies that no conduit
               *                            should be used, with direct approvals set on
               *                            Consideration.
               * @param recipient           The intended recipient for all received items.
               */
              function _applyFractionsAndTransferEach(
                  OrderParameters memory orderParameters,
                  uint256 numerator,
                  uint256 denominator,
                  bytes32 fulfillerConduitKey,
                  address recipient
              ) internal {
                  // Read start time & end time from order parameters and place on stack.
                  uint256 startTime = orderParameters.startTime;
                  uint256 endTime = orderParameters.endTime;
                  // Initialize an accumulator array. From this point forward, no new
                  // memory regions can be safely allocated until the accumulator is no
                  // longer being utilized, as the accumulator operates in an open-ended
                  // fashion from this memory pointer; existing memory may still be
                  // accessed and modified, however.
                  bytes memory accumulator = new bytes(AccumulatorDisarmed);
                  // As of solidity 0.6.0, inline assembly cannot directly access function
                  // definitions, but can still access locally scoped function variables.
                  // This means that a local variable to reference the internal function
                  // definition (using the same type), along with a local variable with
                  // the desired type, must first be created. Then, the original function
                  // pointer can be recast to the desired type.
                  /**
                   * Repurpose existing OfferItem memory regions on the offer array for
                   * the order by overriding the _transfer function pointer to accept a
                   * modified OfferItem argument in place of the usual ReceivedItem:
                   *
                   *   ========= OfferItem ==========   ====== ReceivedItem ======
                   *   ItemType itemType; ------------> ItemType itemType;
                   *   address token; ----------------> address token;
                   *   uint256 identifierOrCriteria; -> uint256 identifier;
                   *   uint256 startAmount; ----------> uint256 amount;
                   *   uint256 endAmount; ------------> address recipient;
                   */
                  // Declare a nested scope to minimize stack depth.
                  unchecked {
                      // Read offer array length from memory and place on stack.
                      uint256 totalOfferItems = orderParameters.offer.length;
                      // Create a variable to indicate whether the order has any
                      // native offer items
                      uint256 anyNativeItems;
                      // Iterate over each offer on the order.
                      // Skip overflow check as for loop is indexed starting at zero.
                      for (uint256 i = 0; i < totalOfferItems; ++i) {
                          // Retrieve the offer item.
                          OfferItem memory offerItem = orderParameters.offer[i];
                          // Offer items for the native token can not be received outside
                          // of a match order function except as part of a contract order.
                          {
                              ItemType itemType = offerItem.itemType;
                              assembly {
                                  anyNativeItems := or(anyNativeItems, iszero(itemType))
                              }
                          }
                          // Declare an additional nested scope to minimize stack depth.
                          {
                              // Apply fill fraction to get offer item amount to transfer.
                              uint256 amount = _applyFraction(
                                  offerItem.startAmount,
                                  offerItem.endAmount,
                                  numerator,
                                  denominator,
                                  startTime,
                                  endTime,
                                  false
                              );
                              // Utilize assembly to set overloaded offerItem arguments.
                              assembly {
                                  // Write new fractional amount to startAmount as amount.
                                  mstore(
                                      add(offerItem, ReceivedItem_amount_offset),
                                      amount
                                  )
                                  // Write recipient to endAmount.
                                  mstore(
                                      add(offerItem, ReceivedItem_recipient_offset),
                                      recipient
                                  )
                              }
                          }
                          // Transfer the item from the offerer to the recipient.
                          _toOfferItemInput(_transfer)(
                              offerItem,
                              orderParameters.offerer,
                              orderParameters.conduitKey,
                              accumulator
                          );
                      }
                      // If a non-contract order has native offer items, throw with an
                      // `InvalidNativeOfferItem` custom error.
                      {
                          OrderType orderType = orderParameters.orderType;
                          uint256 invalidNativeOfferItem;
                          assembly {
                              invalidNativeOfferItem := and(
                                  // Note that this check requires that there are no order
                                  // types beyond the current set (0-4).  It will need to
                                  // be modified if more order types are added.
                                  lt(orderType, 4),
                                  anyNativeItems
                              )
                          }
                          if (invalidNativeOfferItem != 0) {
                              _revertInvalidNativeOfferItem();
                          }
                      }
                  }
                  // Declare a variable for the available native token balance.
                  uint256 nativeTokenBalance;
                  /**
                   * Repurpose existing ConsiderationItem memory regions on the
                   * consideration array for the order by overriding the _transfer
                   * function pointer to accept a modified ConsiderationItem argument in
                   * place of the usual ReceivedItem:
                   *
                   *   ====== ConsiderationItem =====   ====== ReceivedItem ======
                   *   ItemType itemType; ------------> ItemType itemType;
                   *   address token; ----------------> address token;
                   *   uint256 identifierOrCriteria;--> uint256 identifier;
                   *   uint256 startAmount; ----------> uint256 amount;
                   *   uint256 endAmount;        /----> address recipient;
                   *   address recipient; ------/
                   */
                  // Declare a nested scope to minimize stack depth.
                  unchecked {
                      // Read consideration array length from memory and place on stack.
                      uint256 totalConsiderationItems = orderParameters
                          .consideration
                          .length;
                      // Iterate over each consideration item on the order.
                      // Skip overflow check as for loop is indexed starting at zero.
                      for (uint256 i = 0; i < totalConsiderationItems; ++i) {
                          // Retrieve the consideration item.
                          ConsiderationItem memory considerationItem = (
                              orderParameters.consideration[i]
                          );
                          // Apply fraction & derive considerationItem amount to transfer.
                          uint256 amount = _applyFraction(
                              considerationItem.startAmount,
                              considerationItem.endAmount,
                              numerator,
                              denominator,
                              startTime,
                              endTime,
                              true
                          );
                          // Use assembly to set overloaded considerationItem arguments.
                          assembly {
                              // Write derived fractional amount to startAmount as amount.
                              mstore(
                                  add(considerationItem, ReceivedItem_amount_offset),
                                  amount
                              )
                              // Write original recipient to endAmount as recipient.
                              mstore(
                                  add(considerationItem, ReceivedItem_recipient_offset),
                                  mload(
                                      add(
                                          considerationItem,
                                          ConsiderationItem_recipient_offset
                                      )
                                  )
                              )
                          }
                          if (considerationItem.itemType == ItemType.NATIVE) {
                              // Get the current available balance of native tokens.
                              assembly {
                                  nativeTokenBalance := selfbalance()
                              }
                              // Ensure that sufficient native tokens are still available.
                              if (amount > nativeTokenBalance) {
                                  _revertInsufficientNativeTokensSupplied();
                              }
                          }
                          // Transfer item from caller to recipient specified by the item.
                          _toConsiderationItemInput(_transfer)(
                              considerationItem,
                              msg.sender,
                              fulfillerConduitKey,
                              accumulator
                          );
                      }
                  }
                  // Trigger any remaining accumulated transfers via call to the conduit.
                  _triggerIfArmed(accumulator);
                  // Determine whether any native token balance remains.
                  assembly {
                      nativeTokenBalance := selfbalance()
                  }
                  // Return any remaining native token balance to the caller.
                  if (nativeTokenBalance != 0) {
                      _transferNativeTokens(payable(msg.sender), nativeTokenBalance);
                  }
              }
              /**
               * @dev Internal function to emit an OrderFulfilled event. OfferItems are
               *      translated into SpentItems and ConsiderationItems are translated
               *      into ReceivedItems.
               *
               * @param orderHash     The order hash.
               * @param offerer       The offerer for the order.
               * @param zone          The zone for the order.
               * @param recipient     The recipient of the order, or the null address if
               *                      the order was fulfilled via order matching.
               * @param offer         The offer items for the order.
               * @param consideration The consideration items for the order.
               */
              function _emitOrderFulfilledEvent(
                  bytes32 orderHash,
                  address offerer,
                  address zone,
                  address recipient,
                  OfferItem[] memory offer,
                  ConsiderationItem[] memory consideration
              ) internal {
                  // Cast already-modified offer memory region as spent items.
                  SpentItem[] memory spentItems;
                  assembly {
                      spentItems := offer
                  }
                  // Cast already-modified consideration memory region as received items.
                  ReceivedItem[] memory receivedItems;
                  assembly {
                      receivedItems := consideration
                  }
                  // Emit an event signifying that the order has been fulfilled.
                  emit OrderFulfilled(
                      orderHash,
                      offerer,
                      zone,
                      recipient,
                      spentItems,
                      receivedItems
                  );
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          uint256 constant Error_selector_offset = 0x1c;
          /*
           *  error MissingFulfillmentComponentOnAggregation(uint8 side)
           *    - Defined in FulfillmentApplicationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: side
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant MissingFulfillmentComponentOnAggregation_error_selector = (
              0x375c24c1
          );
          uint256 constant MissingFulfillmentComponentOnAggregation_error_side_ptr = 0x20;
          uint256 constant MissingFulfillmentComponentOnAggregation_error_length = 0x24;
          /*
           *  error OfferAndConsiderationRequiredOnFulfillment()
           *    - Defined in FulfillmentApplicationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_selector = (
              0x98e9db6e
          );
          uint256 constant OfferAndConsiderationRequiredOnFulfillment_error_length = 0x04;
          /*
           *  error MismatchedFulfillmentOfferAndConsiderationComponents(
           *      uint256 fulfillmentIndex
           *  )
           *    - Defined in FulfillmentApplicationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: fulfillmentIndex
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant MismatchedOfferAndConsiderationComponents_error_selector = (
              0xbced929d
          );
          uint256 constant MismatchedOfferAndConsiderationComponents_error_idx_ptr = 0x20;
          uint256 constant MismatchedOfferAndConsiderationComponents_error_length = 0x24;
          /*
           *  error InvalidFulfillmentComponentData()
           *    - Defined in FulfillmentApplicationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidFulfillmentComponentData_error_selector = 0x7fda7279;
          uint256 constant InvalidFulfillmentComponentData_error_length = 0x04;
          /*
           *  error InexactFraction()
           *    - Defined in AmountDerivationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InexactFraction_error_selector = 0xc63cf089;
          uint256 constant InexactFraction_error_length = 0x04;
          /*
           *  error OrderCriteriaResolverOutOfRange(uint8 side)
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: side
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant OrderCriteriaResolverOutOfRange_error_selector = 0x133c37c6;
          uint256 constant OrderCriteriaResolverOutOfRange_error_side_ptr = 0x20;
          uint256 constant OrderCriteriaResolverOutOfRange_error_length = 0x24;
          /*
           *  error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex)
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderIndex
           *    - 0x40: offerIndex
           * Revert buffer is memory[0x1c:0x60]
           */
          uint256 constant UnresolvedOfferCriteria_error_selector = 0xd6929332;
          uint256 constant UnresolvedOfferCriteria_error_orderIndex_ptr = 0x20;
          uint256 constant UnresolvedOfferCriteria_error_offerIndex_ptr = 0x40;
          uint256 constant UnresolvedOfferCriteria_error_length = 0x44;
          /*
           *  error UnresolvedConsiderationCriteria(
           *      uint256 orderIndex,
           *      uint256 considerationIndex
           *  )
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderIndex
           *    - 0x40: considerationIndex
           * Revert buffer is memory[0x1c:0x60]
           */
          uint256 constant UnresolvedConsiderationCriteria_error_selector = 0xa8930e9a;
          uint256 constant UnresolvedConsiderationCriteria_error_orderIndex_ptr = 0x20;
          uint256 constant UnresolvedConsiderationCriteria_error_considerationIdx_ptr = (
              0x40
          );
          uint256 constant UnresolvedConsiderationCriteria_error_length = 0x44;
          /*
           *  error OfferCriteriaResolverOutOfRange()
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant OfferCriteriaResolverOutOfRange_error_selector = 0xbfb3f8ce;
          // uint256 constant OfferCriteriaResolverOutOfRange_error_length = 0x04;
          /*
           *  error ConsiderationCriteriaResolverOutOfRange()
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant ConsiderationCriteriaResolverOutOfRange_error_selector = (
              0x6088d7de
          );
          uint256 constant ConsiderationCriteriaResolverOutOfRange_err_selector = (
              0x6088d7de
          );
          // uint256 constant ConsiderationCriteriaResolverOutOfRange_error_length = 0x04;
          /*
           *  error CriteriaNotEnabledForItem()
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant CriteriaNotEnabledForItem_error_selector = 0x94eb6af6;
          uint256 constant CriteriaNotEnabledForItem_error_length = 0x04;
          /*
           *  error InvalidProof()
           *    - Defined in CriteriaResolutionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidProof_error_selector = 0x09bde339;
          uint256 constant InvalidProof_error_length = 0x04;
          /*
           *  error InvalidRestrictedOrder(bytes32 orderHash)
           *    - Defined in ZoneInteractionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderHash
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant InvalidRestrictedOrder_error_selector = 0xfb5014fc;
          uint256 constant InvalidRestrictedOrder_error_orderHash_ptr = 0x20;
          uint256 constant InvalidRestrictedOrder_error_length = 0x24;
          /*
           *  error InvalidContractOrder(bytes32 orderHash)
           *    - Defined in ZoneInteractionErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderHash
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant InvalidContractOrder_error_selector = 0x93979285;
          uint256 constant InvalidContractOrder_error_orderHash_ptr = 0x20;
          uint256 constant InvalidContractOrder_error_length = 0x24;
          /*
           *  error BadSignatureV(uint8 v)
           *    - Defined in SignatureVerificationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: v
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant BadSignatureV_error_selector = 0x1f003d0a;
          uint256 constant BadSignatureV_error_v_ptr = 0x20;
          uint256 constant BadSignatureV_error_length = 0x24;
          /*
           *  error InvalidSigner()
           *    - Defined in SignatureVerificationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidSigner_error_selector = 0x815e1d64;
          uint256 constant InvalidSigner_error_length = 0x04;
          /*
           *  error InvalidSignature()
           *    - Defined in SignatureVerificationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidSignature_error_selector = 0x8baa579f;
          uint256 constant InvalidSignature_error_length = 0x04;
          /*
           *  error BadContractSignature()
           *    - Defined in SignatureVerificationErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant BadContractSignature_error_selector = 0x4f7fb80d;
          uint256 constant BadContractSignature_error_length = 0x04;
          /*
           *  error InvalidERC721TransferAmount(uint256 amount)
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: amount
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant InvalidERC721TransferAmount_error_selector = 0x69f95827;
          uint256 constant InvalidERC721TransferAmount_error_amount_ptr = 0x20;
          uint256 constant InvalidERC721TransferAmount_error_length = 0x24;
          /*
           *  error MissingItemAmount()
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant MissingItemAmount_error_selector = 0x91b3e514;
          uint256 constant MissingItemAmount_error_length = 0x04;
          /*
           *  error UnusedItemParameters()
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant UnusedItemParameters_error_selector = 0x6ab37ce7;
          uint256 constant UnusedItemParameters_error_length = 0x04;
          /*
           *  error NoReentrantCalls()
           *    - Defined in ReentrancyErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant NoReentrantCalls_error_selector = 0x7fa8a987;
          uint256 constant NoReentrantCalls_error_length = 0x04;
          /*
           *  error OrderAlreadyFilled(bytes32 orderHash)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderHash
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant OrderAlreadyFilled_error_selector = 0x10fda3e1;
          uint256 constant OrderAlreadyFilled_error_orderHash_ptr = 0x20;
          uint256 constant OrderAlreadyFilled_error_length = 0x24;
          /*
           *  error InvalidTime(uint256 startTime, uint256 endTime)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: startTime
           *    - 0x40: endTime
           * Revert buffer is memory[0x1c:0x60]
           */
          uint256 constant InvalidTime_error_selector = 0x21ccfeb7;
          uint256 constant InvalidTime_error_startTime_ptr = 0x20;
          uint256 constant InvalidTime_error_endTime_ptr = 0x40;
          uint256 constant InvalidTime_error_length = 0x44;
          /*
           *  error InvalidConduit(bytes32 conduitKey, address conduit)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: conduitKey
           *    - 0x40: conduit
           * Revert buffer is memory[0x1c:0x60]
           */
          uint256 constant InvalidConduit_error_selector = 0x1cf99b26;
          uint256 constant InvalidConduit_error_conduitKey_ptr = 0x20;
          uint256 constant InvalidConduit_error_conduit_ptr = 0x40;
          uint256 constant InvalidConduit_error_length = 0x44;
          /*
           *  error MissingOriginalConsiderationItems()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant MissingOriginalConsiderationItems_error_selector = 0x466aa616;
          uint256 constant MissingOriginalConsiderationItems_error_length = 0x04;
          /*
           *  error InvalidCallToConduit(address conduit)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: conduit
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant InvalidCallToConduit_error_selector = 0xd13d53d4;
          uint256 constant InvalidCallToConduit_error_conduit_ptr = 0x20;
          uint256 constant InvalidCallToConduit_error_length = 0x24;
          /*
           *  error ConsiderationNotMet(
           *      uint256 orderIndex,
           *      uint256 considerationIndex,
           *      uint256 shortfallAmount
           *  )
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderIndex
           *    - 0x40: considerationIndex
           *    - 0x60: shortfallAmount
           * Revert buffer is memory[0x1c:0x80]
           */
          uint256 constant ConsiderationNotMet_error_selector = 0xa5f54208;
          uint256 constant ConsiderationNotMet_error_orderIndex_ptr = 0x20;
          uint256 constant ConsiderationNotMet_error_considerationIndex_ptr = 0x40;
          uint256 constant ConsiderationNotMet_error_shortfallAmount_ptr = 0x60;
          uint256 constant ConsiderationNotMet_error_length = 0x64;
          /*
           *  error InsufficientNativeTokensSupplied()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InsufficientNativeTokensSupplied_error_selector = 0x8ffff980;
          uint256 constant InsufficientNativeTokensSupplied_error_length = 0x04;
          /*
           *  error NativeTokenTransferGenericFailure(address account, uint256 amount)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: account
           *    - 0x40: amount
           * Revert buffer is memory[0x1c:0x60]
           */
          uint256 constant NativeTokenTransferGenericFailure_error_selector = 0xbc806b96;
          uint256 constant NativeTokenTransferGenericFailure_error_account_ptr = 0x20;
          uint256 constant NativeTokenTransferGenericFailure_error_amount_ptr = 0x40;
          uint256 constant NativeTokenTransferGenericFailure_error_length = 0x44;
          /*
           *  error PartialFillsNotEnabledForOrder()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant PartialFillsNotEnabledForOrder_error_selector = 0xa11b63ff;
          uint256 constant PartialFillsNotEnabledForOrder_error_length = 0x04;
          /*
           *  error OrderIsCancelled(bytes32 orderHash)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderHash
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant OrderIsCancelled_error_selector = 0x1a515574;
          uint256 constant OrderIsCancelled_error_orderHash_ptr = 0x20;
          uint256 constant OrderIsCancelled_error_length = 0x24;
          /*
           *  error OrderPartiallyFilled(bytes32 orderHash)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: orderHash
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant OrderPartiallyFilled_error_selector = 0xee9e0e63;
          uint256 constant OrderPartiallyFilled_error_orderHash_ptr = 0x20;
          uint256 constant OrderPartiallyFilled_error_length = 0x24;
          /*
           *  error CannotCancelOrder()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant CannotCancelOrder_error_selector = 0xfed398fc;
          uint256 constant CannotCancelOrder_error_length = 0x04;
          /*
           *  error BadFraction()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant BadFraction_error_selector = 0x5a052b32;
          uint256 constant BadFraction_error_length = 0x04;
          /*
           *  error InvalidMsgValue(uint256 value)
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: value
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant InvalidMsgValue_error_selector = 0xa61be9f0;
          uint256 constant InvalidMsgValue_error_value_ptr = 0x20;
          uint256 constant InvalidMsgValue_error_length = 0x24;
          /*
           *  error InvalidBasicOrderParameterEncoding()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidBasicOrderParameterEncoding_error_selector = 0x39f3e3fd;
          uint256 constant InvalidBasicOrderParameterEncoding_error_length = 0x04;
          /*
           *  error NoSpecifiedOrdersAvailable()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant NoSpecifiedOrdersAvailable_error_selector = 0xd5da9a1b;
          uint256 constant NoSpecifiedOrdersAvailable_error_length = 0x04;
          /*
           *  error InvalidNativeOfferItem()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant InvalidNativeOfferItem_error_selector = 0x12d3f5a3;
          uint256 constant InvalidNativeOfferItem_error_length = 0x04;
          /*
           *  error ConsiderationLengthNotEqualToTotalOriginal()
           *    - Defined in ConsiderationEventsAndErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           * Revert buffer is memory[0x1c:0x20]
           */
          uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_selector = (
              0x2165628a
          );
          uint256 constant ConsiderationLengthNotEqualToTotalOriginal_error_length = 0x04;
          /*
           *  error Panic(uint256 code)
           *    - Built-in Solidity error
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: code
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant Panic_error_selector = 0x4e487b71;
          uint256 constant Panic_error_code_ptr = 0x20;
          uint256 constant Panic_error_length = 0x24;
          uint256 constant Panic_arithmetic = 0x11;
          // uint256 constant Panic_resource = 0x41;
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import { Side } from "../lib/ConsiderationEnums.sol";
          /**
           * @title FulfillmentApplicationErrors
           * @author 0age
           * @notice FulfillmentApplicationErrors contains errors related to fulfillment
           *         application and aggregation.
           */
          interface FulfillmentApplicationErrors {
              /**
               * @dev Revert with an error when a fulfillment is provided that does not
               *      declare at least one component as part of a call to fulfill
               *      available orders.
               */
              error MissingFulfillmentComponentOnAggregation(Side side);
              /**
               * @dev Revert with an error when a fulfillment is provided that does not
               *      declare at least one offer component and at least one consideration
               *      component.
               */
              error OfferAndConsiderationRequiredOnFulfillment();
              /**
               * @dev Revert with an error when the initial offer item named by a
               *      fulfillment component does not match the type, token, identifier,
               *      or conduit preference of the initial consideration item.
               *
               * @param fulfillmentIndex The index of the fulfillment component that
               *                         does not match the initial offer item.
               */
              error MismatchedFulfillmentOfferAndConsiderationComponents(
                  uint256 fulfillmentIndex
              );
              /**
               * @dev Revert with an error when an order or item index are out of range
               *      or a fulfillment component does not match the type, token,
               *      identifier, or conduit preference of the initial consideration item.
               */
              error InvalidFulfillmentComponentData();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { ItemType, Side } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              CriteriaResolver,
              MemoryPointer,
              OfferItem,
              OrderParameters
          } from "./ConsiderationStructs.sol";
          import {
              _revertCriteriaNotEnabledForItem,
              _revertInvalidProof,
              _revertOrderCriteriaResolverOutOfRange,
              _revertUnresolvedConsiderationCriteria,
              _revertUnresolvedOfferCriteria
          } from "./ConsiderationErrors.sol";
          import {
              CriteriaResolutionErrors
          } from "../interfaces/CriteriaResolutionErrors.sol";
          import {
              OneWord,
              OneWordShift,
              OrderParameters_consideration_head_offset,
              Selector_length,
              TwoWords
          } from "./ConsiderationConstants.sol";
          import {
              ConsiderationCriteriaResolverOutOfRange_err_selector,
              Error_selector_offset,
              OfferCriteriaResolverOutOfRange_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title CriteriaResolution
           * @author 0age
           * @notice CriteriaResolution contains a collection of pure functions related to
           *         resolving criteria-based items.
           */
          contract CriteriaResolution is CriteriaResolutionErrors {
              /**
               * @dev Internal pure function to apply criteria resolvers containing
               *      specific token identifiers and associated proofs to order items.
               *
               * @param advancedOrders     The orders to apply criteria resolvers to.
               * @param criteriaResolvers  An array where each element contains a
               *                           reference to a specific order as well as that
               *                           order's offer or consideration, a token
               *                           identifier, and a proof that the supplied token
               *                           identifier is contained in the order's merkle
               *                           root. Note that a root of zero indicates that
               *                           any transferable token identifier is valid and
               *                           that no proof needs to be supplied.
               */
              function _applyCriteriaResolvers(
                  AdvancedOrder[] memory advancedOrders,
                  CriteriaResolver[] memory criteriaResolvers
              ) internal pure {
                  // Skip overflow checks as all for loops are indexed starting at zero.
                  unchecked {
                      // Retrieve length of criteria resolvers array and place on stack.
                      uint256 totalCriteriaResolvers = criteriaResolvers.length;
                      // Retrieve length of orders array and place on stack.
                      uint256 totalAdvancedOrders = advancedOrders.length;
                      // Iterate over each criteria resolver.
                      for (uint256 i = 0; i < totalCriteriaResolvers; ++i) {
                          // Retrieve the criteria resolver.
                          CriteriaResolver memory criteriaResolver = (
                              criteriaResolvers[i]
                          );
                          // Read the order index from memory and place it on the stack.
                          uint256 orderIndex = criteriaResolver.orderIndex;
                          // Ensure that the order index is in range.
                          if (orderIndex >= totalAdvancedOrders) {
                              _revertOrderCriteriaResolverOutOfRange(
                                  criteriaResolver.side
                              );
                          }
                          // Retrieve the referenced advanced order.
                          AdvancedOrder memory advancedOrder = advancedOrders[orderIndex];
                          // Skip criteria resolution for order if not fulfilled.
                          if (advancedOrder.numerator == 0) {
                              continue;
                          }
                          // Retrieve the parameters for the order.
                          OrderParameters memory orderParameters = (
                              advancedOrder.parameters
                          );
                          {
                              // Get a pointer to the list of items to give to
                              // _updateCriteriaItem. If the resolver refers to a
                              // consideration item, this array pointer will be replaced
                              // with the consideration array.
                              OfferItem[] memory items = orderParameters.offer;
                              // Read component index from memory and place it on stack.
                              uint256 componentIndex = criteriaResolver.index;
                              // Get error selector for `OfferCriteriaResolverOutOfRange`.
                              uint256 errorSelector = (
                                  OfferCriteriaResolverOutOfRange_error_selector
                              );
                              // If the resolver refers to a consideration item...
                              if (criteriaResolver.side != Side.OFFER) {
                                  // Get the pointer to `orderParameters.consideration`
                                  // Using the array directly has a significant impact on
                                  // the optimized compiler output.
                                  MemoryPointer considerationPtr = orderParameters
                                      .toMemoryPointer()
                                      .pptr(OrderParameters_consideration_head_offset);
                                  // Replace the items pointer with a pointer to the
                                  // consideration array.
                                  assembly {
                                      items := considerationPtr
                                  }
                                  // Replace the error selector with the selector for
                                  // `ConsiderationCriteriaResolverOutOfRange`.
                                  errorSelector = (
                                      ConsiderationCriteriaResolverOutOfRange_err_selector
                                  );
                              }
                              // Ensure that the component index is in range.
                              if (componentIndex >= items.length) {
                                  assembly {
                                      // Revert with either
                                      // `OfferCriteriaResolverOutOfRange()` or
                                      // `ConsiderationCriteriaResolverOutOfRange()`,
                                      // depending on whether the resolver refers to a
                                      // consideration item.
                                      mstore(0, errorSelector)
                                      // revert(abi.encodeWithSignature(
                                      //    "OfferCriteriaResolverOutOfRange()"
                                      // ))
                                      // or
                                      // revert(abi.encodeWithSignature(
                                      //    "ConsiderationCriteriaResolverOutOfRange()"
                                      // ))
                                      revert(Error_selector_offset, Selector_length)
                                  }
                              }
                              // Apply the criteria resolver to the item in question.
                              _updateCriteriaItem(
                                  items,
                                  componentIndex,
                                  criteriaResolver
                              );
                          }
                      }
                      // Iterate over each advanced order.
                      for (uint256 i = 0; i < totalAdvancedOrders; ++i) {
                          // Retrieve the advanced order.
                          AdvancedOrder memory advancedOrder = advancedOrders[i];
                          // Skip criteria resolution for order if not fulfilled.
                          if (advancedOrder.numerator == 0) {
                              continue;
                          }
                          // Retrieve the parameters for the order.
                          OrderParameters memory orderParameters = (
                              advancedOrder.parameters
                          );
                          // Read consideration length from memory and place on stack.
                          uint256 totalItems = orderParameters.consideration.length;
                          // Iterate over each consideration item on the order.
                          for (uint256 j = 0; j < totalItems; ++j) {
                              // Ensure item type no longer indicates criteria usage.
                              if (
                                  _isItemWithCriteria(
                                      orderParameters.consideration[j].itemType
                                  )
                              ) {
                                  _revertUnresolvedConsiderationCriteria(i, j);
                              }
                          }
                          // Read offer length from memory and place on stack.
                          totalItems = orderParameters.offer.length;
                          // Iterate over each offer item on the order.
                          for (uint256 j = 0; j < totalItems; ++j) {
                              // Ensure item type no longer indicates criteria usage.
                              if (
                                  _isItemWithCriteria(orderParameters.offer[j].itemType)
                              ) {
                                  _revertUnresolvedOfferCriteria(i, j);
                              }
                          }
                      }
                  }
              }
              /**
               * @dev Internal pure function to update a criteria item.
               *
               * @param offer             The offer containing the item to update.
               * @param componentIndex    The index of the item to update.
               * @param criteriaResolver  The criteria resolver to use to update the item.
               */
              function _updateCriteriaItem(
                  OfferItem[] memory offer,
                  uint256 componentIndex,
                  CriteriaResolver memory criteriaResolver
              ) internal pure {
                  // Retrieve relevant item using the component index.
                  OfferItem memory offerItem = offer[componentIndex];
                  // Read item type and criteria from memory & place on stack.
                  ItemType itemType = offerItem.itemType;
                  // Ensure the specified item type indicates criteria usage.
                  if (!_isItemWithCriteria(itemType)) {
                      _revertCriteriaNotEnabledForItem();
                  }
                  uint256 identifierOrCriteria = offerItem.identifierOrCriteria;
                  // If criteria is not 0 (i.e. a collection-wide criteria-based item)...
                  if (identifierOrCriteria != uint256(0)) {
                      // Verify identifier inclusion in criteria root using proof.
                      _verifyProof(
                          criteriaResolver.identifier,
                          identifierOrCriteria,
                          criteriaResolver.criteriaProof
                      );
                  } else if (criteriaResolver.criteriaProof.length != 0) {
                      // Revert if non-empty proof is supplied for a collection-wide item.
                      _revertInvalidProof();
                  }
                  // Update item type to remove criteria usage.
                  // Use assembly to operate on ItemType enum as a number.
                  ItemType newItemType;
                  assembly {
                      // Item type 4 becomes 2 and item type 5 becomes 3.
                      newItemType := sub(3, eq(itemType, 4))
                  }
                  offerItem.itemType = newItemType;
                  // Update identifier w/ supplied identifier.
                  offerItem.identifierOrCriteria = criteriaResolver.identifier;
              }
              /**
               * @dev Internal pure function to check whether a given item type represents
               *      a criteria-based ERC721 or ERC1155 item (e.g. an item that can be
               *      resolved to one of a number of different identifiers at the time of
               *      order fulfillment).
               *
               * @param itemType The item type in question.
               *
               * @return withCriteria A boolean indicating that the item type in question
               *                      represents a criteria-based item.
               */
              function _isItemWithCriteria(
                  ItemType itemType
              ) internal pure returns (bool withCriteria) {
                  // ERC721WithCriteria is ItemType 4. ERC1155WithCriteria is ItemType 5.
                  assembly {
                      withCriteria := gt(itemType, 3)
                  }
              }
              /**
               * @dev Internal pure function to ensure that a given element is contained
               *      in a merkle root via a supplied proof.
               *
               * @param leaf  The element for which to prove inclusion.
               * @param root  The merkle root that inclusion will be proved against.
               * @param proof The merkle proof.
               */
              function _verifyProof(
                  uint256 leaf,
                  uint256 root,
                  bytes32[] memory proof
              ) internal pure {
                  // Declare a variable that will be used to determine proof validity.
                  bool isValid;
                  // Utilize assembly to efficiently verify the proof against the root.
                  assembly {
                      // Store the leaf at the beginning of scratch space.
                      mstore(0, leaf)
                      // Derive the hash of the leaf to use as the initial proof element.
                      let computedHash := keccak256(0, OneWord)
                      // Get memory start location of the first element in proof array.
                      let data := add(proof, OneWord)
                      // Iterate over each proof element to compute the root hash.
                      for {
                          // Left shift by 5 is equivalent to multiplying by 0x20.
                          let end := add(data, shl(OneWordShift, mload(proof)))
                      } lt(data, end) {
                          // Increment by one word at a time.
                          data := add(data, OneWord)
                      } {
                          // Get the proof element.
                          let loadedData := mload(data)
                          // Sort proof elements and place them in scratch space.
                          // Slot of `computedHash` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(OneWordShift, gt(computedHash, loadedData))
                          // Store elements to hash contiguously in scratch space. Scratch
                          // space is 64 bytes (0x00 - 0x3f) & both elements are 32 bytes.
                          mstore(scratch, computedHash)
                          mstore(xor(scratch, OneWord), loadedData)
                          // Derive the updated hash.
                          computedHash := keccak256(0, TwoWords)
                      }
                      // Compare the final hash to the supplied root.
                      isValid := eq(computedHash, root)
                  }
                  // Revert if computed hash does not equal supplied root.
                  if (!isValid) {
                      _revertInvalidProof();
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              AmountDerivationErrors
          } from "../interfaces/AmountDerivationErrors.sol";
          import {
              Error_selector_offset,
              InexactFraction_error_length,
              InexactFraction_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title AmountDeriver
           * @author 0age
           * @notice AmountDeriver contains view and pure functions related to deriving
           *         item amounts based on partial fill quantity and on linear
           *         interpolation based on current time when the start amount and end
           *         amount differ.
           */
          contract AmountDeriver is AmountDerivationErrors {
              /**
               * @dev Internal view function to derive the current amount of a given item
               *      based on the current price, the starting price, and the ending
               *      price. If the start and end prices differ, the current price will be
               *      interpolated on a linear basis. Note that this function expects that
               *      the startTime parameter of orderParameters is not greater than the
               *      current block timestamp and that the endTime parameter is greater
               *      than the current block timestamp. If this condition is not upheld,
               *      duration / elapsed / remaining variables will underflow.
               *
               * @param startAmount The starting amount of the item.
               * @param endAmount   The ending amount of the item.
               * @param startTime   The starting time of the order.
               * @param endTime     The end time of the order.
               * @param roundUp     A boolean indicating whether the resultant amount
               *                    should be rounded up or down.
               *
               * @return amount The current amount.
               */
              function _locateCurrentAmount(
                  uint256 startAmount,
                  uint256 endAmount,
                  uint256 startTime,
                  uint256 endTime,
                  bool roundUp
              ) internal view returns (uint256 amount) {
                  // Only modify end amount if it doesn't already equal start amount.
                  if (startAmount != endAmount) {
                      // Declare variables to derive in the subsequent unchecked scope.
                      uint256 duration;
                      uint256 elapsed;
                      uint256 remaining;
                      // Skip underflow checks as startTime <= block.timestamp < endTime.
                      unchecked {
                          // Derive the duration for the order and place it on the stack.
                          duration = endTime - startTime;
                          // Derive time elapsed since the order started & place on stack.
                          elapsed = block.timestamp - startTime;
                          // Derive time remaining until order expires and place on stack.
                          remaining = duration - elapsed;
                      }
                      // Aggregate new amounts weighted by time with rounding factor.
                      uint256 totalBeforeDivision = ((startAmount * remaining) +
                          (endAmount * elapsed));
                      // Use assembly to combine operations and skip divide-by-zero check.
                      assembly {
                          // Multiply by iszero(iszero(totalBeforeDivision)) to ensure
                          // amount is set to zero if totalBeforeDivision is zero,
                          // as intermediate overflow can occur if it is zero.
                          amount := mul(
                              iszero(iszero(totalBeforeDivision)),
                              // Subtract 1 from the numerator and add 1 to the result if
                              // roundUp is true to get the proper rounding direction.
                              // Division is performed with no zero check as duration
                              // cannot be zero as long as startTime < endTime.
                              add(
                                  div(sub(totalBeforeDivision, roundUp), duration),
                                  roundUp
                              )
                          )
                      }
                      // Return the current amount.
                      return amount;
                  }
                  // Return the original amount as startAmount == endAmount.
                  return endAmount;
              }
              /**
               * @dev Internal pure function to return a fraction of a given value and to
               *      ensure the resultant value does not have any fractional component.
               *      Note that this function assumes that zero will never be supplied as
               *      the denominator parameter; invalid / undefined behavior will result
               *      should a denominator of zero be provided.
               *
               * @param numerator   A value indicating the portion of the order that
               *                    should be filled.
               * @param denominator A value indicating the total size of the order. Note
               *                    that this value cannot be equal to zero.
               * @param value       The value for which to compute the fraction.
               *
               * @return newValue The value after applying the fraction.
               */
              function _getFraction(
                  uint256 numerator,
                  uint256 denominator,
                  uint256 value
              ) internal pure returns (uint256 newValue) {
                  // Return value early in cases where the fraction resolves to 1.
                  if (numerator == denominator) {
                      return value;
                  }
                  // Ensure fraction can be applied to the value with no remainder. Note
                  // that the denominator cannot be zero.
                  assembly {
                      // Ensure new value contains no remainder via mulmod operator.
                      // Credit to @hrkrshnn + @axic for proposing this optimal solution.
                      if mulmod(value, numerator, denominator) {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, InexactFraction_error_selector)
                          // revert(abi.encodeWithSignature("InexactFraction()"))
                          revert(Error_selector_offset, InexactFraction_error_length)
                      }
                  }
                  // Multiply the numerator by the value and ensure no overflow occurs.
                  uint256 valueTimesNumerator = value * numerator;
                  // Divide and check for remainder. Note that denominator cannot be zero.
                  assembly {
                      // Perform division without zero check.
                      newValue := div(valueTimesNumerator, denominator)
                  }
              }
              /**
               * @dev Internal view function to apply a fraction to a consideration
               * or offer item.
               *
               * @param startAmount     The starting amount of the item.
               * @param endAmount       The ending amount of the item.
               * @param numerator       A value indicating the portion of the order that
               *                        should be filled.
               * @param denominator     A value indicating the total size of the order.
               * @param startTime       The starting time of the order.
               * @param endTime         The end time of the order.
               * @param roundUp         A boolean indicating whether the resultant
               *                        amount should be rounded up or down.
               *
               * @return amount The received item to transfer with the final amount.
               */
              function _applyFraction(
                  uint256 startAmount,
                  uint256 endAmount,
                  uint256 numerator,
                  uint256 denominator,
                  uint256 startTime,
                  uint256 endTime,
                  bool roundUp
              ) internal view returns (uint256 amount) {
                  // If start amount equals end amount, apply fraction to end amount.
                  if (startAmount == endAmount) {
                      // Apply fraction to end amount.
                      amount = _getFraction(numerator, denominator, endAmount);
                  } else {
                      // Otherwise, apply fraction to both and interpolated final amount.
                      amount = _locateCurrentAmount(
                          _getFraction(numerator, denominator, startAmount),
                          _getFraction(numerator, denominator, endAmount),
                          startTime,
                          endTime,
                          roundUp
                      );
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              BasicOrderRouteType,
              ItemType,
              OrderType
          } from "./ConsiderationEnums.sol";
          import { BasicOrderParameters } from "./ConsiderationStructs.sol";
          import { OrderValidator } from "./OrderValidator.sol";
          import {
              _revertInsufficientNativeTokensSupplied,
              _revertInvalidMsgValue,
              _revertInvalidERC721TransferAmount,
              _revertUnusedItemParameters
          } from "./ConsiderationErrors.sol";
          import {
              AccumulatorDisarmed,
              AdditionalRecipient_size_shift,
              AdditionalRecipient_size,
              BasicOrder_additionalRecipients_data_cdPtr,
              BasicOrder_additionalRecipients_length_cdPtr,
              BasicOrder_basicOrderType_cdPtr,
              BasicOrder_common_params_size,
              BasicOrder_considerationAmount_cdPtr,
              BasicOrder_considerationHashesArray_ptr,
              BasicOrder_considerationIdentifier_cdPtr,
              BasicOrder_considerationItem_endAmount_ptr,
              BasicOrder_considerationItem_identifier_ptr,
              BasicOrder_considerationItem_itemType_ptr,
              BasicOrder_considerationItem_startAmount_ptr,
              BasicOrder_considerationItem_token_ptr,
              BasicOrder_considerationItem_typeHash_ptr,
              BasicOrder_considerationToken_cdPtr,
              BasicOrder_endTime_cdPtr,
              BasicOrder_fulfillerConduit_cdPtr,
              BasicOrder_offerAmount_cdPtr,
              BasicOrder_offeredItemByteMap,
              BasicOrder_offerer_cdPtr,
              BasicOrder_offererConduit_cdPtr,
              BasicOrder_offerIdentifier_cdPtr,
              BasicOrder_offerItem_endAmount_ptr,
              BasicOrder_offerItem_itemType_ptr,
              BasicOrder_offerItem_token_ptr,
              BasicOrder_offerItem_typeHash_ptr,
              BasicOrder_offerToken_cdPtr,
              BasicOrder_order_considerationHashes_ptr,
              BasicOrder_order_counter_ptr,
              BasicOrder_order_offerer_ptr,
              BasicOrder_order_offerHashes_ptr,
              BasicOrder_order_orderType_ptr,
              BasicOrder_order_startTime_ptr,
              BasicOrder_order_typeHash_ptr,
              BasicOrder_receivedItemByteMap,
              BasicOrder_startTime_cdPtr,
              BasicOrder_totalOriginalAdditionalRecipients_cdPtr,
              BasicOrder_zone_cdPtr,
              Common_token_offset,
              Conduit_execute_ConduitTransfer_length_ptr,
              Conduit_execute_ConduitTransfer_length,
              Conduit_execute_ConduitTransfer_offset_ptr,
              Conduit_execute_ConduitTransfer_ptr,
              Conduit_execute_signature,
              Conduit_execute_transferAmount_ptr,
              Conduit_execute_transferIdentifier_ptr,
              Conduit_execute_transferFrom_ptr,
              Conduit_execute_transferItemType_ptr,
              Conduit_execute_transferTo_ptr,
              Conduit_execute_transferToken_ptr,
              EIP712_ConsiderationItem_size,
              EIP712_OfferItem_size,
              EIP712_Order_size,
              FiveWords,
              FourWords,
              FreeMemoryPointerSlot,
              MaskOverLastTwentyBytes,
              OneConduitExecute_size,
              OneWord,
              OneWordShift,
              OrderFulfilled_baseOffset,
              OrderFulfilled_baseSize,
              OrderFulfilled_consideration_body_offset,
              OrderFulfilled_consideration_head_offset,
              OrderFulfilled_consideration_length_baseOffset,
              OrderFulfilled_fulfiller_offset,
              OrderFulfilled_offer_body_offset,
              OrderFulfilled_offer_head_offset,
              OrderFulfilled_offer_length_baseOffset,
              OrderFulfilled_selector,
              ReceivedItem_amount_offset,
              ReceivedItem_size,
              receivedItemsHash_ptr,
              ThreeWords,
              TwoWords,
              ZeroSlot
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              InvalidBasicOrderParameterEncoding_error_length,
              InvalidBasicOrderParameterEncoding_error_selector,
              InvalidTime_error_endTime_ptr,
              InvalidTime_error_length,
              InvalidTime_error_selector,
              InvalidTime_error_startTime_ptr,
              MissingOriginalConsiderationItems_error_length,
              MissingOriginalConsiderationItems_error_selector,
              UnusedItemParameters_error_length,
              UnusedItemParameters_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title BasicOrderFulfiller
           * @author 0age
           * @notice BasicOrderFulfiller contains functionality for fulfilling "basic"
           *         orders with minimal overhead. See documentation for details on what
           *         qualifies as a basic order.
           */
          contract BasicOrderFulfiller is OrderValidator {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) OrderValidator(conduitController) {}
              /**
               * @dev Internal function to fulfill an order offering an ERC20, ERC721, or
               *      ERC1155 item by supplying Ether (or other native tokens), ERC20
               *      tokens, an ERC721 item, or an ERC1155 item as consideration. Six
               *      permutations are supported: Native token to ERC721, Native token to
               *      ERC1155, ERC20 to ERC721, ERC20 to ERC1155, ERC721 to ERC20, and
               *      ERC1155 to ERC20 (with native tokens supplied as msg.value). For an
               *      order to be eligible for fulfillment via this method, it must
               *      contain a single offer item (though that item may have a greater
               *      amount if the item is not an ERC721). An arbitrary number of
               *      "additional recipients" may also be supplied which will each receive
               *      native tokens or ERC20 items from the fulfiller as consideration.
               *      Refer to the documentation for a more comprehensive summary of how
               *      to utilize this method and what orders are compatible with it.
               *
               * @param parameters Additional information on the fulfilled order. Note
               *                   that the offerer and the fulfiller must first approve
               *                   this contract (or their chosen conduit if indicated)
               *                   before any tokens can be transferred. Also note that
               *                   contract recipients of ERC1155 consideration items must
               *                   implement `onERC1155Received` in order to receive those
               *                   items.
               *
               * @return A boolean indicating whether the order has been fulfilled.
               */
              function _validateAndFulfillBasicOrder(
                  BasicOrderParameters calldata parameters
              ) internal returns (bool) {
                  // Declare enums for order type & route to extract from basicOrderType.
                  BasicOrderRouteType route;
                  OrderType orderType;
                  // Declare additional recipient item type to derive from the route type.
                  ItemType additionalRecipientsItemType;
                  bytes32 orderHash;
                  // Utilize assembly to extract the order type and the basic order route.
                  assembly {
                      // Read basicOrderType from calldata.
                      let basicOrderType := calldataload(BasicOrder_basicOrderType_cdPtr)
                      // Mask all but 2 least-significant bits to derive the order type.
                      orderType := and(basicOrderType, 3)
                      // Divide basicOrderType by four to derive the route.
                      route := shr(2, basicOrderType)
                      // If route > 1 additionalRecipient items are ERC20 (1) else native
                      // token (0).
                      additionalRecipientsItemType := gt(route, 1)
                  }
                  {
                      // Declare temporary variable for enforcing payable status.
                      bool correctPayableStatus;
                      // Utilize assembly to compare the route to the callvalue.
                      assembly {
                          // route 0 and 1 are payable, otherwise route is not payable.
                          correctPayableStatus := eq(
                              additionalRecipientsItemType,
                              iszero(callvalue())
                          )
                      }
                      // Revert if msg.value has not been supplied as part of payable
                      // routes or has been supplied as part of non-payable routes.
                      if (!correctPayableStatus) {
                          _revertInvalidMsgValue(msg.value);
                      }
                  }
                  // Declare more arguments that will be derived from route and calldata.
                  address additionalRecipientsToken;
                  ItemType offeredItemType;
                  bool offerTypeIsAdditionalRecipientsType;
                  // Declare scope for received item type to manage stack pressure.
                  {
                      ItemType receivedItemType;
                      // Utilize assembly to retrieve function arguments and cast types.
                      assembly {
                          // Check if offered item type == additional recipient item type.
                          offerTypeIsAdditionalRecipientsType := gt(route, 3)
                          // If route > 3 additionalRecipientsToken is at 0xc4 else 0x24.
                          additionalRecipientsToken := calldataload(
                              add(
                                  BasicOrder_considerationToken_cdPtr,
                                  mul(
                                      offerTypeIsAdditionalRecipientsType,
                                      BasicOrder_common_params_size
                                  )
                              )
                          )
                          // If route > 2, receivedItemType is route - 2. If route is 2,
                          // the receivedItemType is ERC20 (1). Otherwise, it is native
                          // token (0).
                          receivedItemType := byte(route, BasicOrder_receivedItemByteMap)
                          // If route > 3, offeredItemType is ERC20 (1). Route is 2 or 3,
                          // offeredItemType = route. Route is 0 or 1, it is route + 2.
                          offeredItemType := byte(route, BasicOrder_offeredItemByteMap)
                      }
                      // Derive & validate order using parameters and update order status.
                      orderHash = _prepareBasicFulfillmentFromCalldata(
                          parameters,
                          orderType,
                          receivedItemType,
                          additionalRecipientsItemType,
                          additionalRecipientsToken,
                          offeredItemType
                      );
                  }
                  // Declare conduitKey argument used by transfer functions.
                  bytes32 conduitKey;
                  // Utilize assembly to derive conduit (if relevant) based on route.
                  assembly {
                      // use offerer conduit for routes 0-3, fulfiller conduit otherwise.
                      conduitKey := calldataload(
                          add(
                              BasicOrder_offererConduit_cdPtr,
                              shl(OneWordShift, offerTypeIsAdditionalRecipientsType)
                          )
                      )
                  }
                  // Transfer tokens based on the route.
                  if (additionalRecipientsItemType == ItemType.NATIVE) {
                      // Ensure neither consideration token nor identifier are set. Note
                      // that dirty upper bits in the consideration token will still cause
                      // this error to be thrown.
                      assembly {
                          if or(
                              calldataload(BasicOrder_considerationToken_cdPtr),
                              calldataload(BasicOrder_considerationIdentifier_cdPtr)
                          ) {
                              // Store left-padded selector with push4 (reduces bytecode),
                              // mem[28:32] = selector
                              mstore(0, UnusedItemParameters_error_selector)
                              // revert(abi.encodeWithSignature("UnusedItemParameters()"))
                              revert(
                                  Error_selector_offset,
                                  UnusedItemParameters_error_length
                              )
                          }
                      }
                      // Transfer the ERC721 or ERC1155 item, bypassing the accumulator.
                      _transferIndividual721Or1155Item(offeredItemType, conduitKey);
                      // Transfer native to recipients, return excess to caller & wrap up.
                      _transferNativeTokensAndFinalize();
                  } else {
                      // Initialize an accumulator array. From this point forward, no new
                      // memory regions can be safely allocated until the accumulator is
                      // no longer being utilized, as the accumulator operates in an
                      // open-ended fashion from this memory pointer; existing memory may
                      // still be accessed and modified, however.
                      bytes memory accumulator = new bytes(AccumulatorDisarmed);
                      // Choose transfer method for ERC721 or ERC1155 item based on route.
                      if (route == BasicOrderRouteType.ERC20_TO_ERC721) {
                          // Transfer ERC721 to caller using offerer's conduit preference.
                          _transferERC721(
                              parameters.offerToken,
                              parameters.offerer,
                              msg.sender,
                              parameters.offerIdentifier,
                              parameters.offerAmount,
                              conduitKey,
                              accumulator
                          );
                      } else if (route == BasicOrderRouteType.ERC20_TO_ERC1155) {
                          // Transfer ERC1155 to caller with offerer's conduit preference.
                          _transferERC1155(
                              parameters.offerToken,
                              parameters.offerer,
                              msg.sender,
                              parameters.offerIdentifier,
                              parameters.offerAmount,
                              conduitKey,
                              accumulator
                          );
                      } else if (route == BasicOrderRouteType.ERC721_TO_ERC20) {
                          // Transfer ERC721 to offerer using caller's conduit preference.
                          _transferERC721(
                              parameters.considerationToken,
                              msg.sender,
                              parameters.offerer,
                              parameters.considerationIdentifier,
                              parameters.considerationAmount,
                              conduitKey,
                              accumulator
                          );
                      } else {
                          // route == BasicOrderRouteType.ERC1155_TO_ERC20
                          // Transfer ERC1155 to offerer with caller's conduit preference.
                          _transferERC1155(
                              parameters.considerationToken,
                              msg.sender,
                              parameters.offerer,
                              parameters.considerationIdentifier,
                              parameters.considerationAmount,
                              conduitKey,
                              accumulator
                          );
                      }
                      // Transfer ERC20 tokens to all recipients and wrap up.
                      _transferERC20AndFinalize(
                          offerTypeIsAdditionalRecipientsType,
                          accumulator
                      );
                      // Trigger any remaining accumulated transfers via call to conduit.
                      _triggerIfArmed(accumulator);
                  }
                  // Determine whether order is restricted and, if so, that it is valid.
                  _assertRestrictedBasicOrderValidity(orderHash, orderType, parameters);
                  // Clear the reentrancy guard.
                  _clearReentrancyGuard();
                  return true;
              }
              /**
               * @dev Internal function to prepare fulfillment of a basic order with
               *      manual calldata and memory access. This calculates the order hash,
               *      emits an OrderFulfilled event, and asserts basic order validity.
               *      Note that calldata offsets must be validated as this function
               *      accesses constant calldata pointers for dynamic types that match
               *      default ABI encoding, but valid ABI encoding can use arbitrary
               *      offsets. Checking that the offsets were produced by default encoding
               *      will ensure that other functions using Solidity's calldata accessors
               *      (which calculate pointers from the stored offsets) are reading the
               *      same data as the order hash is derived from. Also note that this
               *      function accesses memory directly.
               *
               * @param parameters                   The parameters of the basic order.
               * @param orderType                    The order type.
               * @param receivedItemType             The item type of the initial
               *                                     consideration item on the order.
               * @param additionalRecipientsItemType The item type of any additional
               *                                     consideration item on the order.
               * @param additionalRecipientsToken    The ERC20 token contract address (if
               *                                     applicable) for any additional
               *                                     consideration item on the order.
               * @param offeredItemType              The item type of the offered item on
               *                                     the order.
               * @return orderHash The calculated order hash.
               */
              function _prepareBasicFulfillmentFromCalldata(
                  BasicOrderParameters calldata parameters,
                  OrderType orderType,
                  ItemType receivedItemType,
                  ItemType additionalRecipientsItemType,
                  address additionalRecipientsToken,
                  ItemType offeredItemType
              ) internal returns (bytes32 orderHash) {
                  // Ensure this function cannot be triggered during a reentrant call.
                  _setReentrancyGuard(false); // Native tokens rejected during execution.
                  // Verify that calldata offsets for all dynamic types were produced by
                  // default encoding. This ensures that the constants used for calldata
                  // pointers to dynamic types are the same as those calculated by
                  // Solidity using their offsets. Also verify that the basic order type
                  // is within range.
                  _assertValidBasicOrderParameters();
                  // Check for invalid time and missing original consideration items.
                  // Utilize assembly so that constant calldata pointers can be applied.
                  assembly {
                      // Ensure current timestamp is between order start time & end time.
                      if or(
                          gt(calldataload(BasicOrder_startTime_cdPtr), timestamp()),
                          iszero(gt(calldataload(BasicOrder_endTime_cdPtr), timestamp()))
                      ) {
                          // Store left-padded selector with push4 (reduces bytecode),
                          // mem[28:32] = selector
                          mstore(0, InvalidTime_error_selector)
                          // Store arguments.
                          mstore(
                              InvalidTime_error_startTime_ptr,
                              calldataload(BasicOrder_startTime_cdPtr)
                          )
                          mstore(
                              InvalidTime_error_endTime_ptr,
                              calldataload(BasicOrder_endTime_cdPtr)
                          )
                          // revert(abi.encodeWithSignature(
                          //     "InvalidTime(uint256,uint256)",
                          //     startTime,
                          //     endTime
                          // ))
                          revert(Error_selector_offset, InvalidTime_error_length)
                      }
                      // Ensure consideration array length isn't less than total original.
                      if lt(
                          calldataload(BasicOrder_additionalRecipients_length_cdPtr),
                          calldataload(BasicOrder_totalOriginalAdditionalRecipients_cdPtr)
                      ) {
                          // Store left-padded selector with push4 (reduces bytecode),
                          // mem[28:32] = selector
                          mstore(0, MissingOriginalConsiderationItems_error_selector)
                          // revert(abi.encodeWithSignature(
                          //     "MissingOriginalConsiderationItems()"
                          // ))
                          revert(
                              Error_selector_offset,
                              MissingOriginalConsiderationItems_error_length
                          )
                      }
                  }
                  {
                      /**
                       * First, handle consideration items. Memory Layout:
                       *  0x60: final hash of the array of consideration item hashes
                       *  0x80-0x160: reused space for EIP712 hashing of each item
                       *   - 0x80: ConsiderationItem EIP-712 typehash (constant)
                       *   - 0xa0: itemType
                       *   - 0xc0: token
                       *   - 0xe0: identifier
                       *   - 0x100: startAmount
                       *   - 0x120: endAmount
                       *   - 0x140: recipient
                       *  0x160-END_ARR: array of consideration item hashes
                       *   - 0x160: primary consideration item EIP712 hash
                       *   - 0x180-END_ARR: additional recipient item EIP712 hashes
                       *  END_ARR: beginning of data for OrderFulfilled event
                       *   - END_ARR + 0x120: length of ReceivedItem array
                       *   - END_ARR + 0x140: beginning of data for first ReceivedItem
                       * (Note: END_ARR = 0x180 + RECIPIENTS_LENGTH * 0x20)
                       */
                      // Load consideration item typehash from runtime and place on stack.
                      bytes32 typeHash = _CONSIDERATION_ITEM_TYPEHASH;
                      // Utilize assembly to enable reuse of memory regions and use
                      // constant pointers when possible.
                      assembly {
                          /*
                           * 1. Calculate the EIP712 ConsiderationItem hash for the
                           * primary consideration item of the basic order.
                           */
                          // Write ConsiderationItem type hash and item type to memory.
                          mstore(BasicOrder_considerationItem_typeHash_ptr, typeHash)
                          mstore(
                              BasicOrder_considerationItem_itemType_ptr,
                              receivedItemType
                          )
                          // Copy calldata region with (token, identifier, amount) from
                          // BasicOrderParameters to ConsiderationItem. The
                          // considerationAmount is written to startAmount and endAmount
                          // as basic orders do not have dynamic amounts.
                          calldatacopy(
                              BasicOrder_considerationItem_token_ptr,
                              BasicOrder_considerationToken_cdPtr,
                              ThreeWords
                          )
                          // Copy calldata region with considerationAmount and offerer
                          // from BasicOrderParameters to endAmount and recipient in
                          // ConsiderationItem.
                          calldatacopy(
                              BasicOrder_considerationItem_endAmount_ptr,
                              BasicOrder_considerationAmount_cdPtr,
                              TwoWords
                          )
                          // Calculate EIP712 ConsiderationItem hash and store it in the
                          // array of EIP712 consideration hashes.
                          mstore(
                              BasicOrder_considerationHashesArray_ptr,
                              keccak256(
                                  BasicOrder_considerationItem_typeHash_ptr,
                                  EIP712_ConsiderationItem_size
                              )
                          )
                          /*
                           * 2. Write a ReceivedItem struct for the primary consideration
                           * item to the consideration array in OrderFulfilled.
                           */
                          // Get the length of the additional recipients array.
                          let totalAdditionalRecipients := calldataload(
                              BasicOrder_additionalRecipients_length_cdPtr
                          )
                          // Calculate pointer to length of OrderFulfilled consideration
                          // array.
                          let eventConsiderationArrPtr := add(
                              OrderFulfilled_consideration_length_baseOffset,
                              shl(OneWordShift, totalAdditionalRecipients)
                          )
                          // Set the length of the consideration array to the number of
                          // additional recipients, plus one for the primary consideration
                          // item.
                          mstore(
                              eventConsiderationArrPtr,
                              add(totalAdditionalRecipients, 1)
                          )
                          // Overwrite the consideration array pointer so it points to the
                          // body of the first element
                          eventConsiderationArrPtr := add(
                              eventConsiderationArrPtr,
                              OneWord
                          )
                          // Set itemType at start of the ReceivedItem memory region.
                          mstore(eventConsiderationArrPtr, receivedItemType)
                          // Copy calldata region (token, identifier, amount & recipient)
                          // from BasicOrderParameters to ReceivedItem memory.
                          calldatacopy(
                              add(eventConsiderationArrPtr, Common_token_offset),
                              BasicOrder_considerationToken_cdPtr,
                              FourWords
                          )
                          /*
                           * 3. Calculate EIP712 ConsiderationItem hashes for original
                           * additional recipients and add a ReceivedItem for each to the
                           * consideration array in the OrderFulfilled event. The original
                           * additional recipients are all the consideration items signed
                           * by the offerer aside from the primary consideration items of
                           * the order. Uses memory region from 0x80-0x160 as a buffer for
                           * calculating EIP712 ConsiderationItem hashes.
                           */
                          // Put pointer to consideration hashes array on the stack.
                          // This will be updated as each additional recipient is hashed
                          let
                              considerationHashesPtr
                          := BasicOrder_considerationHashesArray_ptr
                          // Write item type, token, & identifier for additional recipient
                          // to memory region for hashing EIP712 ConsiderationItem; these
                          // values will be reused for each recipient.
                          mstore(
                              BasicOrder_considerationItem_itemType_ptr,
                              additionalRecipientsItemType
                          )
                          mstore(
                              BasicOrder_considerationItem_token_ptr,
                              additionalRecipientsToken
                          )
                          mstore(BasicOrder_considerationItem_identifier_ptr, 0)
                          // Declare a stack variable where all additional recipients will
                          // be combined to guard against providing dirty upper bits.
                          let combinedAdditionalRecipients
                          // Read length of the additionalRecipients array from calldata
                          // and iterate.
                          totalAdditionalRecipients := calldataload(
                              BasicOrder_totalOriginalAdditionalRecipients_cdPtr
                          )
                          let i := 0
                          for {} lt(i, totalAdditionalRecipients) {
                              i := add(i, 1)
                          } {
                              /*
                               * Calculate EIP712 ConsiderationItem hash for recipient.
                               */
                              // Retrieve calldata pointer for additional recipient.
                              let additionalRecipientCdPtr := add(
                                  BasicOrder_additionalRecipients_data_cdPtr,
                                  mul(AdditionalRecipient_size, i)
                              )
                              // Copy startAmount from calldata to the ConsiderationItem
                              // struct.
                              calldatacopy(
                                  BasicOrder_considerationItem_startAmount_ptr,
                                  additionalRecipientCdPtr,
                                  OneWord
                              )
                              // Copy endAmount and recipient from calldata to the
                              // ConsiderationItem struct.
                              calldatacopy(
                                  BasicOrder_considerationItem_endAmount_ptr,
                                  additionalRecipientCdPtr,
                                  AdditionalRecipient_size
                              )
                              // Include the recipient as part of combined recipients.
                              combinedAdditionalRecipients := or(
                                  combinedAdditionalRecipients,
                                  calldataload(add(additionalRecipientCdPtr, OneWord))
                              )
                              // Add 1 word to the pointer as part of each loop to reduce
                              // operations needed to get local offset into the array.
                              considerationHashesPtr := add(
                                  considerationHashesPtr,
                                  OneWord
                              )
                              // Calculate EIP712 ConsiderationItem hash and store it in
                              // the array of consideration hashes.
                              mstore(
                                  considerationHashesPtr,
                                  keccak256(
                                      BasicOrder_considerationItem_typeHash_ptr,
                                      EIP712_ConsiderationItem_size
                                  )
                              )
                              /*
                               * Write ReceivedItem to OrderFulfilled data.
                               */
                              // At this point, eventConsiderationArrPtr points to the
                              // beginning of the ReceivedItem struct of the previous
                              // element in the array. Increase it by the size of the
                              // struct to arrive at the pointer for the current element.
                              eventConsiderationArrPtr := add(
                                  eventConsiderationArrPtr,
                                  ReceivedItem_size
                              )
                              // Write itemType to the ReceivedItem struct.
                              mstore(
                                  eventConsiderationArrPtr,
                                  additionalRecipientsItemType
                              )
                              // Write token to the next word of the ReceivedItem struct.
                              mstore(
                                  add(eventConsiderationArrPtr, OneWord),
                                  additionalRecipientsToken
                              )
                              // Copy endAmount & recipient words to ReceivedItem struct.
                              calldatacopy(
                                  add(
                                      eventConsiderationArrPtr,
                                      ReceivedItem_amount_offset
                                  ),
                                  additionalRecipientCdPtr,
                                  TwoWords
                              )
                          }
                          /*
                           * 4. Hash packed array of ConsiderationItem EIP712 hashes:
                           *   `keccak256(abi.encodePacked(receivedItemHashes))`
                           * Note that it is set at 0x60 — all other memory begins at
                           * 0x80. 0x60 is the "zero slot" and will be restored at the end
                           * of the assembly section and before required by the compiler.
                           */
                          mstore(
                              receivedItemsHash_ptr,
                              keccak256(
                                  BasicOrder_considerationHashesArray_ptr,
                                  shl(OneWordShift, add(totalAdditionalRecipients, 1))
                              )
                          )
                          /*
                           * 5. Add a ReceivedItem for each tip to the consideration array
                           * in the OrderFulfilled event. The tips are all the
                           * consideration items that were not signed by the offerer and
                           * were provided by the fulfiller.
                           */
                          // Overwrite length to length of the additionalRecipients array.
                          totalAdditionalRecipients := calldataload(
                              BasicOrder_additionalRecipients_length_cdPtr
                          )
                          for {} lt(i, totalAdditionalRecipients) {
                              i := add(i, 1)
                          } {
                              // Retrieve calldata pointer for additional recipient.
                              let additionalRecipientCdPtr := add(
                                  BasicOrder_additionalRecipients_data_cdPtr,
                                  mul(AdditionalRecipient_size, i)
                              )
                              // At this point, eventConsiderationArrPtr points to the
                              // beginning of the ReceivedItem struct of the previous
                              // element in the array. Increase it by the size of the
                              // struct to arrive at the pointer for the current element.
                              eventConsiderationArrPtr := add(
                                  eventConsiderationArrPtr,
                                  ReceivedItem_size
                              )
                              // Write itemType to the ReceivedItem struct.
                              mstore(
                                  eventConsiderationArrPtr,
                                  additionalRecipientsItemType
                              )
                              // Write token to the next word of the ReceivedItem struct.
                              mstore(
                                  add(eventConsiderationArrPtr, OneWord),
                                  additionalRecipientsToken
                              )
                              // Copy endAmount & recipient words to ReceivedItem struct.
                              calldatacopy(
                                  add(
                                      eventConsiderationArrPtr,
                                      ReceivedItem_amount_offset
                                  ),
                                  additionalRecipientCdPtr,
                                  TwoWords
                              )
                              // Include the recipient as part of combined recipients.
                              combinedAdditionalRecipients := or(
                                  combinedAdditionalRecipients,
                                  calldataload(add(additionalRecipientCdPtr, OneWord))
                              )
                          }
                          // Ensure no dirty upper bits on combined additional recipients.
                          if gt(combinedAdditionalRecipients, MaskOverLastTwentyBytes) {
                              // Store left-padded selector with push4 (reduces bytecode),
                              // mem[28:32] = selector
                              mstore(0, InvalidBasicOrderParameterEncoding_error_selector)
                              // revert(abi.encodeWithSignature(
                              //     "InvalidBasicOrderParameterEncoding()"
                              // ))
                              revert(
                                  Error_selector_offset,
                                  InvalidBasicOrderParameterEncoding_error_length
                              )
                          }
                      }
                  }
                  {
                      /**
                       * Next, handle offered items. Memory Layout:
                       *  EIP712 data for OfferItem
                       *   - 0x80:  OfferItem EIP-712 typehash (constant)
                       *   - 0xa0:  itemType
                       *   - 0xc0:  token
                       *   - 0xe0:  identifier (reused for offeredItemsHash)
                       *   - 0x100: startAmount
                       *   - 0x120: endAmount
                       */
                      // Place offer item typehash on the stack.
                      bytes32 typeHash = _OFFER_ITEM_TYPEHASH;
                      // Utilize assembly to enable reuse of memory regions when possible.
                      assembly {
                          /*
                           * 1. Calculate OfferItem EIP712 hash
                           */
                          // Write the OfferItem typeHash to memory.
                          mstore(BasicOrder_offerItem_typeHash_ptr, typeHash)
                          // Write the OfferItem item type to memory.
                          mstore(BasicOrder_offerItem_itemType_ptr, offeredItemType)
                          // Copy calldata region with (offerToken, offerIdentifier,
                          // offerAmount) from OrderParameters to (token, identifier,
                          // startAmount) in OfferItem struct. The offerAmount is written
                          // to startAmount and endAmount as basic orders do not have
                          // dynamic amounts.
                          calldatacopy(
                              BasicOrder_offerItem_token_ptr,
                              BasicOrder_offerToken_cdPtr,
                              ThreeWords
                          )
                          // Copy offerAmount from calldata to endAmount in OfferItem
                          // struct.
                          calldatacopy(
                              BasicOrder_offerItem_endAmount_ptr,
                              BasicOrder_offerAmount_cdPtr,
                              OneWord
                          )
                          // Compute EIP712 OfferItem hash, write result to scratch space:
                          //   `keccak256(abi.encode(offeredItem))`
                          mstore(
                              0,
                              keccak256(
                                  BasicOrder_offerItem_typeHash_ptr,
                                  EIP712_OfferItem_size
                              )
                          )
                          /*
                           * 2. Calculate hash of array of EIP712 hashes and write the
                           * result to the corresponding OfferItem struct:
                           *   `keccak256(abi.encodePacked(offerItemHashes))`
                           */
                          mstore(BasicOrder_order_offerHashes_ptr, keccak256(0, OneWord))
                          /*
                           * 3. Write SpentItem to offer array in OrderFulfilled event.
                           */
                          let eventConsiderationArrPtr := add(
                              OrderFulfilled_offer_length_baseOffset,
                              shl(
                                  OneWordShift,
                                  calldataload(
                                      BasicOrder_additionalRecipients_length_cdPtr
                                  )
                              )
                          )
                          // Set a length of 1 for the offer array.
                          mstore(eventConsiderationArrPtr, 1)
                          // Write itemType to the SpentItem struct.
                          mstore(add(eventConsiderationArrPtr, OneWord), offeredItemType)
                          // Copy calldata region with (offerToken, offerIdentifier,
                          // offerAmount) from OrderParameters to (token, identifier,
                          // amount) in SpentItem struct.
                          calldatacopy(
                              add(eventConsiderationArrPtr, AdditionalRecipient_size),
                              BasicOrder_offerToken_cdPtr,
                              ThreeWords
                          )
                      }
                  }
                  {
                      /**
                       * Once consideration items and offer items have been handled,
                       * derive the final order hash. Memory Layout:
                       *  0x80-0x1c0: EIP712 data for order
                       *   - 0x80:   Order EIP-712 typehash (constant)
                       *   - 0xa0:   orderParameters.offerer
                       *   - 0xc0:   orderParameters.zone
                       *   - 0xe0:   keccak256(abi.encodePacked(offerHashes))
                       *   - 0x100:  keccak256(abi.encodePacked(considerationHashes))
                       *   - 0x120:  orderParameters.basicOrderType (% 4 = orderType)
                       *   - 0x140:  orderParameters.startTime
                       *   - 0x160:  orderParameters.endTime
                       *   - 0x180:  orderParameters.zoneHash
                       *   - 0x1a0:  orderParameters.salt
                       *   - 0x1c0:  orderParameters.conduitKey
                       *   - 0x1e0:  _counters[orderParameters.offerer] (from storage)
                       */
                      // Read the offerer from calldata and place on the stack.
                      address offerer;
                      assembly {
                          offerer := calldataload(BasicOrder_offerer_cdPtr)
                      }
                      // Read offerer's current counter from storage and place on stack.
                      uint256 counter = _getCounter(offerer);
                      // Load order typehash from runtime code and place on stack.
                      bytes32 typeHash = _ORDER_TYPEHASH;
                      assembly {
                          // Set the OrderItem typeHash in memory.
                          mstore(BasicOrder_order_typeHash_ptr, typeHash)
                          // Copy offerer and zone from OrderParameters in calldata to the
                          // Order struct.
                          calldatacopy(
                              BasicOrder_order_offerer_ptr,
                              BasicOrder_offerer_cdPtr,
                              TwoWords
                          )
                          // Copy receivedItemsHash from zero slot to the Order struct.
                          mstore(
                              BasicOrder_order_considerationHashes_ptr,
                              mload(receivedItemsHash_ptr)
                          )
                          // Write the supplied orderType to the Order struct.
                          mstore(BasicOrder_order_orderType_ptr, orderType)
                          // Copy startTime, endTime, zoneHash, salt & conduit from
                          // calldata to the Order struct.
                          calldatacopy(
                              BasicOrder_order_startTime_ptr,
                              BasicOrder_startTime_cdPtr,
                              FiveWords
                          )
                          // Write offerer's counter, retrieved from storage, to struct.
                          mstore(BasicOrder_order_counter_ptr, counter)
                          // Compute the EIP712 Order hash.
                          orderHash := keccak256(
                              BasicOrder_order_typeHash_ptr,
                              EIP712_Order_size
                          )
                      }
                  }
                  assembly {
                      /**
                       * After the order hash has been derived, emit OrderFulfilled event:
                       *   event OrderFulfilled(
                       *     bytes32 orderHash,
                       *     address indexed offerer,
                       *     address indexed zone,
                       *     address fulfiller,
                       *     SpentItem[] offer,
                       *       > (itemType, token, id, amount)
                       *     ReceivedItem[] consideration
                       *       > (itemType, token, id, amount, recipient)
                       *   )
                       * topic0 - OrderFulfilled event signature
                       * topic1 - offerer
                       * topic2 - zone
                       * data:
                       *  - 0x00: orderHash
                       *  - 0x20: fulfiller
                       *  - 0x40: offer arr ptr (0x80)
                       *  - 0x60: consideration arr ptr (0x120)
                       *  - 0x80: offer arr len (1)
                       *  - 0xa0: offer.itemType
                       *  - 0xc0: offer.token
                       *  - 0xe0: offer.identifier
                       *  - 0x100: offer.amount
                       *  - 0x120: 1 + recipients.length
                       *  - 0x140: recipient 0
                       */
                      // Derive pointer to start of OrderFulfilled event data.
                      let eventDataPtr := add(
                          OrderFulfilled_baseOffset,
                          shl(
                              OneWordShift,
                              calldataload(BasicOrder_additionalRecipients_length_cdPtr)
                          )
                      )
                      // Write the order hash to the head of the event's data region.
                      mstore(eventDataPtr, orderHash)
                      // Write the fulfiller (i.e. the caller) next for receiver argument.
                      mstore(add(eventDataPtr, OrderFulfilled_fulfiller_offset), caller())
                      // Write the SpentItem and ReceivedItem array offsets (constants).
                      mstore(
                          // SpentItem array offset
                          add(eventDataPtr, OrderFulfilled_offer_head_offset),
                          OrderFulfilled_offer_body_offset
                      )
                      mstore(
                          // ReceivedItem array offset
                          add(eventDataPtr, OrderFulfilled_consideration_head_offset),
                          OrderFulfilled_consideration_body_offset
                      )
                      // Derive total data size including SpentItem and ReceivedItem data.
                      // SpentItem portion is already included in the baseSize constant,
                      // as there can only be one element in the array.
                      let dataSize := add(
                          OrderFulfilled_baseSize,
                          mul(
                              calldataload(BasicOrder_additionalRecipients_length_cdPtr),
                              ReceivedItem_size
                          )
                      )
                      // Emit OrderFulfilled log with three topics (the event signature
                      // as well as the two indexed arguments, the offerer and the zone).
                      log3(
                          // Supply the pointer for event data in memory.
                          eventDataPtr,
                          // Supply the size of event data in memory.
                          dataSize,
                          // Supply the OrderFulfilled event signature.
                          OrderFulfilled_selector,
                          // Supply the first topic (the offerer).
                          calldataload(BasicOrder_offerer_cdPtr),
                          // Supply the second topic (the zone).
                          calldataload(BasicOrder_zone_cdPtr)
                      )
                      // Restore the zero slot.
                      mstore(ZeroSlot, 0)
                      // Update the free memory pointer so that event data is persisted.
                      mstore(FreeMemoryPointerSlot, add(eventDataPtr, dataSize))
                  }
                  // Verify and update the status of the derived order.
                  _validateBasicOrderAndUpdateStatus(orderHash, parameters.signature);
                  // Return the derived order hash.
                  return orderHash;
              }
              /**
               * @dev Internal function to transfer an individual ERC721 or ERC1155 item
               *      from a given originator to a given recipient. The accumulator will
               *      be bypassed, meaning that this function should be utilized in cases
               *      where multiple item transfers can be accumulated into a single
               *      conduit call. Sufficient approvals must be set, either on the
               *      respective conduit or on this contract. Note that this function may
               *      only be safely called as part of basic orders, as it assumes a
               *      specific calldata encoding structure that must first be validated.
               *
               * @param itemType   The type of item to transfer, either ERC721 or ERC1155.
               * @param conduitKey A bytes32 value indicating what corresponding conduit,
               *                   if any, to source token approvals from. The zero hash
               *                   signifies that no conduit should be used, with direct
               *                   approvals set on this contract.
               */
              function _transferIndividual721Or1155Item(
                  ItemType itemType,
                  bytes32 conduitKey
              ) internal {
                  // Retrieve token, from, identifier, and amount from calldata using
                  // fixed calldata offsets based on strict basic parameter encoding.
                  address token;
                  address from;
                  uint256 identifier;
                  uint256 amount;
                  assembly {
                      token := calldataload(BasicOrder_offerToken_cdPtr)
                      from := calldataload(BasicOrder_offerer_cdPtr)
                      identifier := calldataload(BasicOrder_offerIdentifier_cdPtr)
                      amount := calldataload(BasicOrder_offerAmount_cdPtr)
                  }
                  // Determine if the transfer is to be performed via a conduit.
                  if (conduitKey != bytes32(0)) {
                      // Use free memory pointer as calldata offset for the conduit call.
                      uint256 callDataOffset;
                      // Utilize assembly to place each argument in free memory.
                      assembly {
                          // Retrieve the free memory pointer and use it as the offset.
                          callDataOffset := mload(FreeMemoryPointerSlot)
                          // Write ConduitInterface.execute.selector to memory.
                          mstore(callDataOffset, Conduit_execute_signature)
                          // Write the offset to the ConduitTransfer array in memory.
                          mstore(
                              add(
                                  callDataOffset,
                                  Conduit_execute_ConduitTransfer_offset_ptr
                              ),
                              Conduit_execute_ConduitTransfer_ptr
                          )
                          // Write the length of the ConduitTransfer array to memory.
                          mstore(
                              add(
                                  callDataOffset,
                                  Conduit_execute_ConduitTransfer_length_ptr
                              ),
                              Conduit_execute_ConduitTransfer_length
                          )
                          // Write the item type to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferItemType_ptr),
                              itemType
                          )
                          // Write the token to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferToken_ptr),
                              token
                          )
                          // Write the transfer source to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferFrom_ptr),
                              from
                          )
                          // Write the transfer recipient (the caller) to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferTo_ptr),
                              caller()
                          )
                          // Write the token identifier to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferIdentifier_ptr),
                              identifier
                          )
                          // Write the transfer amount to memory.
                          mstore(
                              add(callDataOffset, Conduit_execute_transferAmount_ptr),
                              amount
                          )
                      }
                      // Perform the call to the conduit.
                      _callConduitUsingOffsets(
                          conduitKey,
                          callDataOffset,
                          OneConduitExecute_size
                      );
                  } else {
                      // Otherwise, determine whether it is an ERC721 or ERC1155 item.
                      if (itemType == ItemType.ERC721) {
                          // Ensure that exactly one 721 item is being transferred.
                          if (amount != 1) {
                              _revertInvalidERC721TransferAmount(amount);
                          }
                          // Perform transfer to caller via the token contract directly.
                          _performERC721Transfer(token, from, msg.sender, identifier);
                      } else {
                          // Perform transfer to caller via the token contract directly.
                          _performERC1155Transfer(
                              token,
                              from,
                              msg.sender,
                              identifier,
                              amount
                          );
                      }
                  }
              }
              /**
               * @dev Internal function to transfer Ether (or other native tokens) to a
               *      given recipient as part of basic order fulfillment. Note that
               *      conduits are not utilized for native tokens as the transferred
               *      amount must be provided as msg.value. Also note that this function
               *      may only be safely called as part of basic orders, as it assumes a
               *      specific calldata encoding structure that must first be validated.
               */
              function _transferNativeTokensAndFinalize() internal {
                  // Put native token value supplied by the caller on the stack.
                  uint256 nativeTokensRemaining = msg.value;
                  // Retrieve consideration amount, offerer, and total size of additional
                  // recipients data from calldata using fixed offsets and place on stack.
                  uint256 amount;
                  address payable to;
                  uint256 totalAdditionalRecipientsDataSize;
                  assembly {
                      amount := calldataload(BasicOrder_considerationAmount_cdPtr)
                      to := calldataload(BasicOrder_offerer_cdPtr)
                      totalAdditionalRecipientsDataSize := shl(
                          AdditionalRecipient_size_shift,
                          calldataload(BasicOrder_additionalRecipients_length_cdPtr)
                      )
                  }
                  uint256 additionalRecipientAmount;
                  address payable recipient;
                  // Skip overflow check as for loop is indexed starting at zero.
                  unchecked {
                      // Iterate over additional recipient data by two-word element.
                      for (
                          uint256 i = 0;
                          i < totalAdditionalRecipientsDataSize;
                          i += AdditionalRecipient_size
                      ) {
                          assembly {
                              // Retrieve calldata pointer for additional recipient.
                              let additionalRecipientCdPtr := add(
                                  BasicOrder_additionalRecipients_data_cdPtr,
                                  i
                              )
                              additionalRecipientAmount := calldataload(
                                  additionalRecipientCdPtr
                              )
                              recipient := calldataload(
                                  add(OneWord, additionalRecipientCdPtr)
                              )
                          }
                          // Ensure that sufficient native tokens are available.
                          if (additionalRecipientAmount > nativeTokensRemaining) {
                              _revertInsufficientNativeTokensSupplied();
                          }
                          // Reduce native token value available. Skip underflow check as
                          // subtracted value is confirmed above as less than remaining.
                          nativeTokensRemaining -= additionalRecipientAmount;
                          // Transfer native tokens to the additional recipient.
                          _transferNativeTokens(recipient, additionalRecipientAmount);
                      }
                  }
                  // Ensure that sufficient native tokens are still available.
                  if (amount > nativeTokensRemaining) {
                      _revertInsufficientNativeTokensSupplied();
                  }
                  // Transfer native tokens to the offerer.
                  _transferNativeTokens(to, amount);
                  // If any native tokens remain after transfers, return to the caller.
                  if (nativeTokensRemaining > amount) {
                      // Skip underflow check as nativeTokensRemaining > amount.
                      unchecked {
                          // Transfer remaining native tokens to the caller.
                          _transferNativeTokens(
                              payable(msg.sender),
                              nativeTokensRemaining - amount
                          );
                      }
                  }
              }
              /**
               * @dev Internal function to transfer ERC20 tokens to a given recipient as
               *      part of basic order fulfillment. Note that this function may only be
               *      safely called as part of basic orders, as it assumes a specific
               *      calldata encoding structure that must first be validated. Also note
               *      that basic order parameters are retrieved using fixed offsets, this
               *      requires that strict basic order encoding has already been verified.
               *
               * @param fromOfferer A boolean indicating whether to decrement amount from
               *                    the offered amount.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _transferERC20AndFinalize(
                  bool fromOfferer,
                  bytes memory accumulator
              ) internal {
                  // Declare from and to variables determined by fromOfferer value.
                  address from;
                  address to;
                  // Declare token and amount variables determined by fromOfferer value.
                  address token;
                  uint256 amount;
                  // Declare and check identifier variable within an isolated scope.
                  {
                      // Declare identifier variable determined by fromOfferer value.
                      uint256 identifier;
                      // Set ERC20 token transfer variables based on fromOfferer boolean.
                      if (fromOfferer) {
                          // Use offerer as from value, msg.sender as to value, and offer
                          // token, identifier, & amount values if token is from offerer.
                          assembly {
                              from := calldataload(BasicOrder_offerer_cdPtr)
                              to := caller()
                              token := calldataload(BasicOrder_offerToken_cdPtr)
                              identifier := calldataload(BasicOrder_offerIdentifier_cdPtr)
                              amount := calldataload(BasicOrder_offerAmount_cdPtr)
                          }
                      } else {
                          // Otherwise, use msg.sender as from value, offerer as to value,
                          // and consideration token, identifier, and amount values.
                          assembly {
                              from := caller()
                              to := calldataload(BasicOrder_offerer_cdPtr)
                              token := calldataload(BasicOrder_considerationToken_cdPtr)
                              identifier := calldataload(
                                  BasicOrder_considerationIdentifier_cdPtr
                              )
                              amount := calldataload(BasicOrder_considerationAmount_cdPtr)
                          }
                      }
                      // Ensure that no identifier is supplied.
                      if (identifier != 0) {
                          _revertUnusedItemParameters();
                      }
                  }
                  // Determine the appropriate conduit to utilize.
                  bytes32 conduitKey;
                  // Utilize assembly to derive conduit (if relevant) based on route.
                  assembly {
                      // Use offerer conduit if fromOfferer, fulfiller conduit otherwise.
                      conduitKey := calldataload(
                          sub(
                              BasicOrder_fulfillerConduit_cdPtr,
                              shl(OneWordShift, fromOfferer)
                          )
                      )
                  }
                  // Retrieve total size of additional recipients data and place on stack.
                  uint256 totalAdditionalRecipientsDataSize;
                  assembly {
                      totalAdditionalRecipientsDataSize := shl(
                          AdditionalRecipient_size_shift,
                          calldataload(BasicOrder_additionalRecipients_length_cdPtr)
                      )
                  }
                  uint256 additionalRecipientAmount;
                  address recipient;
                  // Iterate over each additional recipient.
                  for (uint256 i = 0; i < totalAdditionalRecipientsDataSize; ) {
                      assembly {
                          // Retrieve calldata pointer for additional recipient.
                          let additionalRecipientCdPtr := add(
                              BasicOrder_additionalRecipients_data_cdPtr,
                              i
                          )
                          additionalRecipientAmount := calldataload(
                              additionalRecipientCdPtr
                          )
                          recipient := calldataload(
                              add(OneWord, additionalRecipientCdPtr)
                          )
                      }
                      // Decrement the amount to transfer to fulfiller if indicated.
                      if (fromOfferer) {
                          amount -= additionalRecipientAmount;
                      }
                      // Transfer ERC20 tokens to additional recipient given approval.
                      _transferERC20(
                          token,
                          from,
                          recipient,
                          additionalRecipientAmount,
                          conduitKey,
                          accumulator
                      );
                      // Skip overflow check as for loop is indexed starting at zero.
                      unchecked {
                          i += AdditionalRecipient_size;
                      }
                  }
                  // Transfer ERC20 token amount (from account must have proper approval).
                  _transferERC20(token, from, to, amount, conduitKey, accumulator);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import { Side } from "../lib/ConsiderationEnums.sol";
          /**
           * @title CriteriaResolutionErrors
           * @author 0age
           * @notice CriteriaResolutionErrors contains all errors related to criteria
           *         resolution.
           */
          interface CriteriaResolutionErrors {
              /**
               * @dev Revert with an error when providing a criteria resolver that refers
               *      to an order that has not been supplied.
               *
               * @param side The side of the order that was not supplied.
               */
              error OrderCriteriaResolverOutOfRange(Side side);
              /**
               * @dev Revert with an error if an offer item still has unresolved criteria
               *      after applying all criteria resolvers.
               *
               * @param orderIndex The index of the order that contains the offer item.
               * @param offerIndex The index of the offer item that still has unresolved
               *                   criteria.
               */
              error UnresolvedOfferCriteria(uint256 orderIndex, uint256 offerIndex);
              /**
               * @dev Revert with an error if a consideration item still has unresolved
               *      criteria after applying all criteria resolvers.
               *
               * @param orderIndex         The index of the order that contains the
               *                           consideration item.
               * @param considerationIndex The index of the consideration item that still
               *                           has unresolved criteria.
               */
              error UnresolvedConsiderationCriteria(
                  uint256 orderIndex,
                  uint256 considerationIndex
              );
              /**
               * @dev Revert with an error when providing a criteria resolver that refers
               *      to an order with an offer item that has not been supplied.
               */
              error OfferCriteriaResolverOutOfRange();
              /**
               * @dev Revert with an error when providing a criteria resolver that refers
               *      to an order with a consideration item that has not been supplied.
               */
              error ConsiderationCriteriaResolverOutOfRange();
              /**
               * @dev Revert with an error when providing a criteria resolver that refers
               *      to an order with an item that does not expect a criteria to be
               *      resolved.
               */
              error CriteriaNotEnabledForItem();
              /**
               * @dev Revert with an error when providing a criteria resolver that
               *      contains an invalid proof with respect to the given item and
               *      chosen identifier.
               */
              error InvalidProof();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title AmountDerivationErrors
           * @author 0age
           * @notice AmountDerivationErrors contains errors related to amount derivation.
           */
          interface AmountDerivationErrors {
              /**
               * @dev Revert with an error when attempting to apply a fraction as part of
               *      a partial fill that does not divide the target amount cleanly.
               */
              error InexactFraction();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { OrderType } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              ConsiderationItem,
              OfferItem,
              Order,
              OrderComponents,
              OrderParameters,
              OrderStatus
          } from "./ConsiderationStructs.sol";
          import {
              _revertBadFraction,
              _revertCannotCancelOrder,
              _revertConsiderationLengthNotEqualToTotalOriginal,
              _revertInvalidContractOrder,
              _revertPartialFillsNotEnabledForOrder
          } from "./ConsiderationErrors.sol";
          import { Executor } from "./Executor.sol";
          import { ZoneInteraction } from "./ZoneInteraction.sol";
          import { MemoryPointer } from "../helpers/PointerLibraries.sol";
          import {
              AdvancedOrder_denominator_offset,
              AdvancedOrder_numerator_offset,
              BasicOrder_offerer_cdPtr,
              Common_amount_offset,
              Common_endAmount_offset,
              Common_identifier_offset,
              Common_token_offset,
              ConsiderItem_recipient_offset,
              ContractOrder_orderHash_offerer_shift,
              MaxUint120,
              OrderStatus_filledDenominator_offset,
              OrderStatus_filledNumerator_offset,
              OrderStatus_ValidatedAndNotCancelled
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              Panic_arithmetic,
              Panic_error_code_ptr,
              Panic_error_length,
              Panic_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title OrderValidator
           * @author 0age
           * @notice OrderValidator contains functionality related to validating orders
           *         and updating their status.
           */
          contract OrderValidator is Executor, ZoneInteraction {
              // Track status of each order (validated, cancelled, and fraction filled).
              mapping(bytes32 => OrderStatus) private _orderStatus;
              // Track nonces for contract offerers.
              mapping(address => uint256) internal _contractNonces;
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) Executor(conduitController) {}
              /**
               * @dev Internal function to verify and update the status of a basic order.
               *      Note that this function may only be safely called as part of basic
               *      orders, as it assumes a specific calldata encoding structure that
               *      must first be validated.
               *
               * @param orderHash The hash of the order.
               * @param signature A signature from the offerer indicating that the order
               *                  has been approved.
               */
              function _validateBasicOrderAndUpdateStatus(
                  bytes32 orderHash,
                  bytes calldata signature
              ) internal {
                  // Retrieve offerer directly using fixed calldata offset based on strict
                  // basic parameter encoding.
                  address offerer;
                  assembly {
                      offerer := calldataload(BasicOrder_offerer_cdPtr)
                  }
                  // Retrieve the order status for the given order hash.
                  OrderStatus storage orderStatus = _orderStatus[orderHash];
                  // Ensure order is fillable and is not cancelled.
                  _verifyOrderStatus(
                      orderHash,
                      orderStatus,
                      true, // Only allow unused orders when fulfilling basic orders.
                      true // Signifies to revert if the order is invalid.
                  );
                  // If the order is not already validated, verify the supplied signature.
                  if (!orderStatus.isValidated) {
                      _verifySignature(offerer, orderHash, signature);
                  }
                  // Update order status as fully filled, packing struct values.
                  orderStatus.isValidated = true;
                  orderStatus.isCancelled = false;
                  orderStatus.numerator = 1;
                  orderStatus.denominator = 1;
              }
              /**
               * @dev Internal function to validate an order, determine what portion to
               *      fill, and update its status. The desired fill amount is supplied as
               *      a fraction, as is the returned amount to fill.
               *
               * @param advancedOrder     The order to fulfill as well as the fraction to
               *                          fill. Note that all offer and consideration
               *                          amounts must divide with no remainder in order
               *                          for a partial fill to be valid.
               * @param revertOnInvalid   A boolean indicating whether to revert if the
               *                          order is invalid due to the time or status.
               *
               * @return orderHash      The order hash.
               * @return numerator      A value indicating the portion of the order that
               *                        will be filled.
               * @return denominator    A value indicating the total size of the order.
               */
              function _validateOrderAndUpdateStatus(
                  AdvancedOrder memory advancedOrder,
                  bool revertOnInvalid
              )
                  internal
                  returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
              {
                  // Retrieve the parameters for the order.
                  OrderParameters memory orderParameters = advancedOrder.parameters;
                  // Ensure current timestamp falls between order start time and end time.
                  if (
                      !_verifyTime(
                          orderParameters.startTime,
                          orderParameters.endTime,
                          revertOnInvalid
                      )
                  ) {
                      // Assuming an invalid time and no revert, return zeroed out values.
                      return (bytes32(0), 0, 0);
                  }
                  // Read numerator and denominator from memory and place on the stack.
                  // Note that overflowed values are masked.
                  assembly {
                      numerator := and(
                          mload(add(advancedOrder, AdvancedOrder_numerator_offset)),
                          MaxUint120
                      )
                      denominator := and(
                          mload(add(advancedOrder, AdvancedOrder_denominator_offset)),
                          MaxUint120
                      )
                  }
                  // Declare variable for tracking the validity of the supplied fraction.
                  bool invalidFraction;
                  // If the order is a contract order, return the generated order.
                  if (orderParameters.orderType == OrderType.CONTRACT) {
                      // Ensure that the numerator and denominator are both equal to 1.
                      assembly {
                          // (1 ^ nd =/= 0) => (nd =/= 1) => (n =/= 1) || (d =/= 1)
                          // It's important that the values are 120-bit masked before
                          // multiplication is applied. Otherwise, the last implication
                          // above is not correct (mod 2^256).
                          invalidFraction := xor(mul(numerator, denominator), 1)
                      }
                      // Revert if the supplied numerator and denominator are not valid.
                      if (invalidFraction) {
                          _revertBadFraction();
                      }
                      // Return the generated order based on the order params and the
                      // provided extra data. If revertOnInvalid is true, the function
                      // will revert if the input is invalid.
                      return
                          _getGeneratedOrder(
                              orderParameters,
                              advancedOrder.extraData,
                              revertOnInvalid
                          );
                  }
                  // Ensure numerator does not exceed denominator and is not zero.
                  assembly {
                      invalidFraction := or(gt(numerator, denominator), iszero(numerator))
                  }
                  // Revert if the supplied numerator and denominator are not valid.
                  if (invalidFraction) {
                      _revertBadFraction();
                  }
                  // If attempting partial fill (n < d) check order type & ensure support.
                  if (
                      _doesNotSupportPartialFills(
                          orderParameters.orderType,
                          numerator,
                          denominator
                      )
                  ) {
                      // Revert if partial fill was attempted on an unsupported order.
                      _revertPartialFillsNotEnabledForOrder();
                  }
                  // Retrieve current counter & use it w/ parameters to derive order hash.
                  orderHash = _assertConsiderationLengthAndGetOrderHash(orderParameters);
                  // Retrieve the order status using the derived order hash.
                  OrderStatus storage orderStatus = _orderStatus[orderHash];
                  // Ensure order is fillable and is not cancelled.
                  if (
                      !_verifyOrderStatus(
                          orderHash,
                          orderStatus,
                          false, // Allow partially used orders to be filled.
                          revertOnInvalid
                      )
                  ) {
                      // Assuming an invalid order status and no revert, return zero fill.
                      return (orderHash, 0, 0);
                  }
                  // If the order is not already validated, verify the supplied signature.
                  if (!orderStatus.isValidated) {
                      _verifySignature(
                          orderParameters.offerer,
                          orderHash,
                          advancedOrder.signature
                      );
                  }
                  // Utilize assembly to determine the fraction to fill and update status.
                  assembly {
                      let orderStatusSlot := orderStatus.slot
                      // Read filled amount as numerator and denominator and put on stack.
                      let filledNumerator := sload(orderStatusSlot)
                      let filledDenominator := shr(
                          OrderStatus_filledDenominator_offset,
                          filledNumerator
                      )
                      // "Loop" until the appropriate fill fraction has been determined.
                      for { } 1 { } {
                          // If no portion of the order has been filled yet...
                          if iszero(filledDenominator) {
                              // fill the full supplied fraction.
                              filledNumerator := numerator
                              // Exit the "loop" early.
                              break
                          }
                          // Shift and mask to calculate the current filled numerator.
                          filledNumerator := and(
                              shr(OrderStatus_filledNumerator_offset, filledNumerator),
                              MaxUint120
                          )
                          // If denominator of 1 supplied, fill entire remaining amount.
                          if eq(denominator, 1) {
                              // Set the amount to fill to the remaining amount.
                              numerator := sub(filledDenominator, filledNumerator)
                              // Set the fill size to the current size.
                              denominator := filledDenominator
                              // Set the filled amount to the current size.
                              filledNumerator := filledDenominator
                              // Exit the "loop" early.
                              break
                          }
                          // If supplied denominator is equal to the current one:
                          if eq(denominator, filledDenominator) {
                              // Increment the filled numerator by the new numerator.
                              filledNumerator := add(numerator, filledNumerator)
                              // Once adjusted, if current + supplied numerator exceeds
                              // the denominator:
                              let carry := mul(
                                  sub(filledNumerator, denominator),
                                  gt(filledNumerator, denominator)
                              )
                              // reduce the amount to fill by the excess.
                              numerator := sub(numerator, carry)
                              // Reduce the filled amount by the excess as well.
                              filledNumerator := sub(filledNumerator, carry)
                              // Exit the "loop" early.
                              break
                          }
                          // Otherwise, if supplied denominator differs from current one:
                          // Scale the filled amount up by the supplied size.
                          filledNumerator := mul(filledNumerator, denominator)
                          // Scale the supplied amount and size up by the current size.
                          numerator := mul(numerator, filledDenominator)
                          denominator := mul(denominator, filledDenominator)
                          // Increment the filled numerator by the new numerator.
                          filledNumerator := add(numerator, filledNumerator)
                          // Once adjusted, if current + supplied numerator exceeds
                          // denominator:
                          let carry := mul(
                              sub(filledNumerator, denominator),
                              gt(filledNumerator, denominator)
                          )
                          // reduce the amount to fill by the excess.
                          numerator := sub(numerator, carry)
                          // Reduce the filled amount by the excess as well.
                          filledNumerator := sub(filledNumerator, carry)
                          // Check filledNumerator and denominator for uint120 overflow.
                          if or(
                              gt(filledNumerator, MaxUint120),
                              gt(denominator, MaxUint120)
                          ) {
                              // Derive greatest common divisor using euclidean algorithm.
                              function gcd(_a, _b) -> out {
                                  // "Loop" until only one non-zero value remains.
                                  for { } _b { } {
                                      // Assign the second value to a temporary variable.
                                      let _c := _b
                                      // Derive the modulus of the two values.
                                      _b := mod(_a, _c)
                                      // Set the first value to the temporary value.
                                      _a := _c
                                  }
                                  // Return the remaining non-zero value.
                                  out := _a
                              }
                              // Determine the amount to scale down the fill fractions.
                              let scaleDown := gcd(
                                  numerator,
                                  gcd(filledNumerator, denominator)
                              )
                              // Ensure that the divisor is at least one.
                              let safeScaleDown := add(scaleDown, iszero(scaleDown))
                              // Scale all fractional values down by gcd.
                              numerator := div(numerator, safeScaleDown)
                              filledNumerator := div(filledNumerator, safeScaleDown)
                              denominator := div(denominator, safeScaleDown)
                              // Perform the overflow check a second time.
                              if or(
                                  gt(filledNumerator, MaxUint120),
                                  gt(denominator, MaxUint120)
                              ) {
                                  // Store the Panic error signature.
                                  mstore(0, Panic_error_selector)
                                  // Store the arithmetic (0x11) panic code.
                                  mstore(Panic_error_code_ptr, Panic_arithmetic)
                                  // revert(abi.encodeWithSignature(
                                  //     "Panic(uint256)", 0x11
                                  // ))
                                  revert(Error_selector_offset, Panic_error_length)
                              }
                          }
                          // Exit the "loop" now that all evaluation is complete.
                          break
                      }
                      // Update order status and fill amount, packing struct values.
                      // [denominator: 15 bytes] [numerator: 15 bytes]
                      // [isCancelled: 1 byte] [isValidated: 1 byte]
                      sstore(
                          orderStatusSlot,
                          or(
                              OrderStatus_ValidatedAndNotCancelled,
                              or(
                                  shl(
                                      OrderStatus_filledNumerator_offset,
                                      filledNumerator
                                  ),
                                  shl(OrderStatus_filledDenominator_offset, denominator)
                              )
                          )
                      )
                  }
              }
              /**
               * @dev Internal pure function to check the compatibility of two offer
               *      or consideration items for contract orders.  Note that the itemType
               *      and identifier are reset in cases where criteria = 0 (collection-
               *      wide offers), which means that a contract offerer has full latitude
               *      to choose any identifier it wants mid-flight, in contrast to the
               *      normal behavior, where the fulfiller can pick which identifier to
               *      receive by providing a CriteriaResolver.
               *
               * @param originalItem The original offer or consideration item.
               * @param newItem      The new offer or consideration item.
               *
               * @return isInvalid Error buffer indicating if items are incompatible.
               */
              function _compareItems(
                  MemoryPointer originalItem,
                  MemoryPointer newItem
              ) internal pure returns (uint256 isInvalid) {
                  assembly {
                      let itemType := mload(originalItem)
                      let identifier := mload(add(originalItem, Common_identifier_offset))
                      // Set returned identifier for criteria-based items w/ criteria = 0.
                      if and(gt(itemType, 3), iszero(identifier)) {
                          // replace item type
                          itemType := sub(3, eq(itemType, 4))
                          identifier := mload(add(newItem, Common_identifier_offset))
                      }
                      let originalAmount := mload(add(originalItem, Common_amount_offset))
                      let newAmount := mload(add(newItem, Common_amount_offset))
                      isInvalid := iszero(
                          and(
                              // originalItem.token == newItem.token &&
                              // originalItem.itemType == newItem.itemType
                              and(
                                  eq(
                                      mload(add(originalItem, Common_token_offset)),
                                      mload(add(newItem, Common_token_offset))
                                  ),
                                  eq(itemType, mload(newItem))
                              ),
                              // originalItem.identifier == newItem.identifier &&
                              // originalItem.startAmount == originalItem.endAmount
                              and(
                                  eq(
                                      identifier,
                                      mload(add(newItem, Common_identifier_offset))
                                  ),
                                  eq(
                                      originalAmount,
                                      mload(add(originalItem, Common_endAmount_offset))
                                  )
                              )
                          )
                      )
                  }
              }
              /**
               * @dev Internal pure function to check the compatibility of two recipients
               *      on consideration items for contract orders. This check is skipped if
               *      no recipient is originally supplied.
               *
               * @param originalRecipient The original consideration item recipient.
               * @param newRecipient      The new consideration item recipient.
               *
               * @return isInvalid Error buffer indicating if recipients are incompatible.
               */
              function _checkRecipients(
                  address originalRecipient,
                  address newRecipient
              ) internal pure returns (uint256 isInvalid) {
                  assembly {
                      isInvalid := iszero(
                          or(
                              iszero(originalRecipient),
                              eq(newRecipient, originalRecipient)
                          )
                      )
                  }
              }
              /**
               * @dev Internal function to generate a contract order. When a
               *      collection-wide criteria-based item (criteria = 0) is provided as an
               *      input to a contract order, the contract offerer has full latitude to
               *      choose any identifier it wants mid-flight, which differs from the
               *      usual behavior.  For regular criteria-based orders with
               *      identifierOrCriteria = 0, the fulfiller can pick which identifier to
               *      receive by providing a CriteriaResolver. For contract offers with
               *      identifierOrCriteria = 0, Seaport does not expect a corresponding
               *      CriteriaResolver, and will revert if one is provided.
               *
               * @param orderParameters The parameters for the order.
               * @param context         The context for generating the order.
               * @param revertOnInvalid Whether to revert on invalid input.
               *
               * @return orderHash   The order hash.
               * @return numerator   The numerator.
               * @return denominator The denominator.
               */
              function _getGeneratedOrder(
                  OrderParameters memory orderParameters,
                  bytes memory context,
                  bool revertOnInvalid
              )
                  internal
                  returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
              {
                  // Ensure that consideration array length is equal to the total original
                  // consideration items value.
                  if (
                      orderParameters.consideration.length !=
                      orderParameters.totalOriginalConsiderationItems
                  ) {
                      _revertConsiderationLengthNotEqualToTotalOriginal();
                  }
                  {
                      address offerer = orderParameters.offerer;
                      bool success;
                      (MemoryPointer cdPtr, uint256 size) = _encodeGenerateOrder(
                          orderParameters,
                          context
                      );
                      assembly {
                          success := call(gas(), offerer, 0, cdPtr, size, 0, 0)
                      }
                      {
                          // Note: overflow impossible; nonce can't increment that high.
                          uint256 contractNonce;
                          unchecked {
                              // Note: nonce will be incremented even for skipped orders,
                              // and  even if generateOrder's return data does not satisfy
                              // all the constraints. This is the case when errorBuffer
                              // != 0 and revertOnInvalid == false.
                              contractNonce = _contractNonces[offerer]++;
                          }
                          assembly {
                              // Shift offerer address up 96 bytes and combine with nonce.
                              orderHash := xor(
                                  contractNonce,
                                  shl(ContractOrder_orderHash_offerer_shift, offerer)
                              )
                          }
                      }
                      // Revert or skip if the call to generate the contract order failed.
                      if (!success) {
                          return _revertOrReturnEmpty(revertOnInvalid, orderHash);
                      }
                  }
                  // From this point onward, do not allow for skipping orders as the
                  // contract offerer may have modified state in expectation of any named
                  // consideration items being sent to their designated recipients.
                  // Decode the returned contract order and/or update the error buffer.
                  (
                      uint256 errorBuffer,
                      OfferItem[] memory offer,
                      ConsiderationItem[] memory consideration
                  ) = _convertGetGeneratedOrderResult(_decodeGenerateOrderReturndata)();
                  // Revert if the returndata could not be decoded correctly.
                  if (errorBuffer != 0) {
                      _revertInvalidContractOrder(orderHash);
                  }
                  {
                      // Designate lengths.
                      uint256 originalOfferLength = orderParameters.offer.length;
                      uint256 newOfferLength = offer.length;
                      // Explicitly specified offer items cannot be removed.
                      if (originalOfferLength > newOfferLength) {
                          _revertInvalidContractOrder(orderHash);
                      }
                      // Iterate over each specified offer (e.g. minimumReceived) item.
                      for (uint256 i = 0; i < originalOfferLength; ) {
                          // Retrieve the pointer to the originally supplied item.
                          MemoryPointer mPtrOriginal = orderParameters
                              .offer[i]
                              .toMemoryPointer();
                          // Retrieve the pointer to the newly returned item.
                          MemoryPointer mPtrNew = offer[i].toMemoryPointer();
                          // Compare the items and update the error buffer accordingly.
                          errorBuffer |=
                              _cast(
                                  mPtrOriginal
                                      .offset(Common_amount_offset)
                                      .readUint256() >
                                      mPtrNew.offset(Common_amount_offset).readUint256()
                              ) |
                              _compareItems(mPtrOriginal, mPtrNew);
                          // Increment the array (cannot overflow as index starts at 0).
                          unchecked {
                              ++i;
                          }
                      }
                      // Assign the returned offer item in place of the original item.
                      orderParameters.offer = offer;
                  }
                  {
                      // Designate lengths & memory locations.
                      ConsiderationItem[] memory originalConsiderationArray = (
                          orderParameters.consideration
                      );
                      uint256 newConsiderationLength = consideration.length;
                      // New consideration items cannot be created.
                      if (newConsiderationLength > originalConsiderationArray.length) {
                          _revertInvalidContractOrder(orderHash);
                      }
                      // Iterate over returned consideration & do not exceed maximumSpent.
                      for (uint256 i = 0; i < newConsiderationLength; ) {
                          // Retrieve the pointer to the originally supplied item.
                          MemoryPointer mPtrOriginal = originalConsiderationArray[i]
                              .toMemoryPointer();
                          // Retrieve the pointer to the newly returned item.
                          MemoryPointer mPtrNew = consideration[i].toMemoryPointer();
                          // Compare the items and update the error buffer accordingly
                          // and ensure that the recipients are equal when provided.
                          errorBuffer |=
                              _cast(
                                  mPtrNew.offset(Common_amount_offset).readUint256() >
                                      mPtrOriginal
                                          .offset(Common_amount_offset)
                                          .readUint256()
                              ) |
                              _compareItems(mPtrOriginal, mPtrNew) |
                              _checkRecipients(
                                  mPtrOriginal
                                      .offset(ConsiderItem_recipient_offset)
                                      .readAddress(),
                                  mPtrNew
                                      .offset(ConsiderItem_recipient_offset)
                                      .readAddress()
                              );
                          // Increment the array (cannot overflow as index starts at 0).
                          unchecked {
                              ++i;
                          }
                      }
                      // Assign returned consideration item in place of the original item.
                      orderParameters.consideration = consideration;
                  }
                  // Revert if any item comparison failed.
                  if (errorBuffer != 0) {
                      _revertInvalidContractOrder(orderHash);
                  }
                  // Return order hash and full fill amount (numerator & denominator = 1).
                  return (orderHash, 1, 1);
              }
              /**
               * @dev Internal function to cancel an arbitrary number of orders. Note that
               *      only the offerer or the zone of a given order may cancel it. Callers
               *      should ensure that the intended order was cancelled by calling
               *      `getOrderStatus` and confirming that `isCancelled` returns `true`.
               *      Also note that contract orders are not cancellable.
               *
               * @param orders The orders to cancel.
               *
               * @return cancelled A boolean indicating whether the supplied orders were
               *                   successfully cancelled.
               */
              function _cancel(
                  OrderComponents[] calldata orders
              ) internal returns (bool cancelled) {
                  // Ensure that the reentrancy guard is not currently set.
                  _assertNonReentrant();
                  // Declare variables outside of the loop.
                  OrderStatus storage orderStatus;
                  // Declare a variable for tracking invariants in the loop.
                  bool anyInvalidCallerOrContractOrder;
                  // Skip overflow check as for loop is indexed starting at zero.
                  unchecked {
                      // Read length of the orders array from memory and place on stack.
                      uint256 totalOrders = orders.length;
                      // Iterate over each order.
                      for (uint256 i = 0; i < totalOrders; ) {
                          // Retrieve the order.
                          OrderComponents calldata order = orders[i];
                          address offerer = order.offerer;
                          address zone = order.zone;
                          OrderType orderType = order.orderType;
                          assembly {
                              // If caller is neither the offerer nor zone, or a contract
                              // order is present, flag anyInvalidCallerOrContractOrder.
                              anyInvalidCallerOrContractOrder := or(
                                  anyInvalidCallerOrContractOrder,
                                  // orderType == CONTRACT ||
                                  // !(caller == offerer || caller == zone)
                                  or(
                                      eq(orderType, 4),
                                      iszero(
                                          or(eq(caller(), offerer), eq(caller(), zone))
                                      )
                                  )
                              )
                          }
                          bytes32 orderHash = _deriveOrderHash(
                              _toOrderParametersReturnType(
                                  _decodeOrderComponentsAsOrderParameters
                              )(order.toCalldataPointer()),
                              order.counter
                          );
                          // Retrieve the order status using the derived order hash.
                          orderStatus = _orderStatus[orderHash];
                          // Update the order status as not valid and cancelled.
                          orderStatus.isValidated = false;
                          orderStatus.isCancelled = true;
                          // Emit an event signifying that the order has been cancelled.
                          emit OrderCancelled(orderHash, offerer, zone);
                          // Increment counter inside body of loop for gas efficiency.
                          ++i;
                      }
                  }
                  if (anyInvalidCallerOrContractOrder) {
                      _revertCannotCancelOrder();
                  }
                  // Return a boolean indicating that orders were successfully cancelled.
                  cancelled = true;
              }
              /**
               * @dev Internal function to validate an arbitrary number of orders, thereby
               *      registering their signatures as valid and allowing the fulfiller to
               *      skip signature verification on fulfillment. Note that validated
               *      orders may still be unfulfillable due to invalid item amounts or
               *      other factors; callers should determine whether validated orders are
               *      fulfillable by simulating the fulfillment call prior to execution.
               *      Also note that anyone can validate a signed order, but only the
               *      offerer can validate an order without supplying a signature.
               *
               * @param orders The orders to validate.
               *
               * @return validated A boolean indicating whether the supplied orders were
               *                   successfully validated.
               */
              function _validate(
                  Order[] memory orders
              ) internal returns (bool validated) {
                  // Ensure that the reentrancy guard is not currently set.
                  _assertNonReentrant();
                  // Declare variables outside of the loop.
                  OrderStatus storage orderStatus;
                  bytes32 orderHash;
                  address offerer;
                  // Skip overflow check as for loop is indexed starting at zero.
                  unchecked {
                      // Read length of the orders array from memory and place on stack.
                      uint256 totalOrders = orders.length;
                      // Iterate over each order.
                      for (uint256 i = 0; i < totalOrders; ++i) {
                          // Retrieve the order.
                          Order memory order = orders[i];
                          // Retrieve the order parameters.
                          OrderParameters memory orderParameters = order.parameters;
                          // Skip contract orders.
                          if (orderParameters.orderType == OrderType.CONTRACT) {
                              continue;
                          }
                          // Move offerer from memory to the stack.
                          offerer = orderParameters.offerer;
                          // Get current counter & use it w/ params to derive order hash.
                          orderHash = _assertConsiderationLengthAndGetOrderHash(
                              orderParameters
                          );
                          // Retrieve the order status using the derived order hash.
                          orderStatus = _orderStatus[orderHash];
                          // Ensure order is fillable and retrieve the filled amount.
                          _verifyOrderStatus(
                              orderHash,
                              orderStatus,
                              false, // Signifies that partially filled orders are valid.
                              true // Signifies to revert if the order is invalid.
                          );
                          // If the order has not already been validated...
                          if (!orderStatus.isValidated) {
                              // Ensure that consideration array length is equal to the
                              // total original consideration items value.
                              if (
                                  orderParameters.consideration.length !=
                                  orderParameters.totalOriginalConsiderationItems
                              ) {
                                  _revertConsiderationLengthNotEqualToTotalOriginal();
                              }
                              // Verify the supplied signature.
                              _verifySignature(offerer, orderHash, order.signature);
                              // Update order status to mark the order as valid.
                              orderStatus.isValidated = true;
                              // Emit an event signifying the order has been validated.
                              emit OrderValidated(orderHash, orderParameters);
                          }
                      }
                  }
                  // Return a boolean indicating that orders were successfully validated.
                  validated = true;
              }
              /**
               * @dev Internal view function to retrieve the status of a given order by
               *      hash, including whether the order has been cancelled or validated
               *      and the fraction of the order that has been filled.
               *
               * @param orderHash The order hash in question.
               *
               * @return isValidated A boolean indicating whether the order in question
               *                     has been validated (i.e. previously approved or
               *                     partially filled).
               * @return isCancelled A boolean indicating whether the order in question
               *                     has been cancelled.
               * @return totalFilled The total portion of the order that has been filled
               *                     (i.e. the "numerator").
               * @return totalSize   The total size of the order that is either filled or
               *                     unfilled (i.e. the "denominator").
               */
              function _getOrderStatus(
                  bytes32 orderHash
              )
                  internal
                  view
                  returns (
                      bool isValidated,
                      bool isCancelled,
                      uint256 totalFilled,
                      uint256 totalSize
                  )
              {
                  // Retrieve the order status using the order hash.
                  OrderStatus storage orderStatus = _orderStatus[orderHash];
                  // Return the fields on the order status.
                  return (
                      orderStatus.isValidated,
                      orderStatus.isCancelled,
                      orderStatus.numerator,
                      orderStatus.denominator
                  );
              }
              /**
               * @dev Internal pure function to either revert or return an empty tuple
               *      depending on the value of `revertOnInvalid`.
               *
               * @param revertOnInvalid   Whether to revert on invalid input.
               * @param contractOrderHash The contract order hash.
               *
               * @return orderHash   The order hash.
               * @return numerator   The numerator.
               * @return denominator The denominator.
               */
              function _revertOrReturnEmpty(
                  bool revertOnInvalid,
                  bytes32 contractOrderHash
              )
                  internal
                  pure
                  returns (bytes32 orderHash, uint256 numerator, uint256 denominator)
              {
                  if (revertOnInvalid) {
                      _revertInvalidContractOrder(contractOrderHash);
                  }
                  return (contractOrderHash, 0, 0);
              }
              /**
               * @dev Internal pure function to check whether a given order type indicates
               *      that partial fills are not supported (e.g. only "full fills" are
               *      allowed for the order in question).
               *
               * @param orderType   The order type in question.
               * @param numerator   The numerator in question.
               * @param denominator The denominator in question.
               *
               * @return isFullOrder A boolean indicating whether the order type only
               *                     supports full fills.
               */
              function _doesNotSupportPartialFills(
                  OrderType orderType,
                  uint256 numerator,
                  uint256 denominator
              ) internal pure returns (bool isFullOrder) {
                  // The "full" order types are even, while "partial" order types are odd.
                  // Bitwise and by 1 is equivalent to modulo by 2, but 2 gas cheaper. The
                  // check is only necessary if numerator is less than denominator.
                  assembly {
                      // Equivalent to `uint256(orderType) & 1 == 0`.
                      isFullOrder := and(
                          lt(numerator, denominator),
                          iszero(and(orderType, 1))
                      )
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
          import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol";
          import { ItemType } from "./ConsiderationEnums.sol";
          import { ReceivedItem } from "./ConsiderationStructs.sol";
          import { Verifiers } from "./Verifiers.sol";
          import { TokenTransferrer } from "./TokenTransferrer.sol";
          import {
              Accumulator_array_length_ptr,
              Accumulator_array_offset_ptr,
              Accumulator_array_offset,
              Accumulator_conduitKey_ptr,
              Accumulator_itemSizeOffsetDifference,
              Accumulator_selector_ptr,
              AccumulatorArmed,
              AccumulatorDisarmed,
              Conduit_transferItem_amount_ptr,
              Conduit_transferItem_from_ptr,
              Conduit_transferItem_identifier_ptr,
              Conduit_transferItem_size,
              Conduit_transferItem_to_ptr,
              Conduit_transferItem_token_ptr,
              FreeMemoryPointerSlot,
              OneWord,
              TwoWords
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              NativeTokenTransferGenericFailure_error_account_ptr,
              NativeTokenTransferGenericFailure_error_amount_ptr,
              NativeTokenTransferGenericFailure_error_length,
              NativeTokenTransferGenericFailure_error_selector
          } from "./ConsiderationErrorConstants.sol";
          import {
              _revertInvalidCallToConduit,
              _revertInvalidConduit,
              _revertInvalidERC721TransferAmount,
              _revertUnusedItemParameters
          } from "./ConsiderationErrors.sol";
          /**
           * @title Executor
           * @author 0age
           * @notice Executor contains functions related to processing executions (i.e.
           *         transferring items, either directly or via conduits).
           */
          contract Executor is Verifiers, TokenTransferrer {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) Verifiers(conduitController) {}
              /**
               * @dev Internal function to transfer a given item, either directly or via
               *      a corresponding conduit.
               *
               * @param item        The item to transfer, including an amount and a
               *                    recipient.
               * @param from        The account supplying the item.
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _transfer(
                  ReceivedItem memory item,
                  address from,
                  bytes32 conduitKey,
                  bytes memory accumulator
              ) internal {
                  // If the item type indicates Ether or a native token...
                  if (item.itemType == ItemType.NATIVE) {
                      // Ensure neither the token nor the identifier parameters are set.
                      if ((uint160(item.token) | item.identifier) != 0) {
                          _revertUnusedItemParameters();
                      }
                      // transfer the native tokens to the recipient.
                      _transferNativeTokens(item.recipient, item.amount);
                  } else if (item.itemType == ItemType.ERC20) {
                      // Ensure that no identifier is supplied.
                      if (item.identifier != 0) {
                          _revertUnusedItemParameters();
                      }
                      // Transfer ERC20 tokens from the source to the recipient.
                      _transferERC20(
                          item.token,
                          from,
                          item.recipient,
                          item.amount,
                          conduitKey,
                          accumulator
                      );
                  } else if (item.itemType == ItemType.ERC721) {
                      // Transfer ERC721 token from the source to the recipient.
                      _transferERC721(
                          item.token,
                          from,
                          item.recipient,
                          item.identifier,
                          item.amount,
                          conduitKey,
                          accumulator
                      );
                  } else {
                      // Transfer ERC1155 token from the source to the recipient.
                      _transferERC1155(
                          item.token,
                          from,
                          item.recipient,
                          item.identifier,
                          item.amount,
                          conduitKey,
                          accumulator
                      );
                  }
              }
              /**
               * @dev Internal function to transfer Ether or other native tokens to a
               *      given recipient.
               *
               * @param to     The recipient of the transfer.
               * @param amount The amount to transfer.
               */
              function _transferNativeTokens(
                  address payable to,
                  uint256 amount
              ) internal {
                  // Ensure that the supplied amount is non-zero.
                  _assertNonZeroAmount(amount);
                  // Declare a variable indicating whether the call was successful or not.
                  bool success;
                  assembly {
                      // Transfer the native token and store if it succeeded or not.
                      success := call(gas(), to, amount, 0, 0, 0, 0)
                  }
                  // If the call fails...
                  if (!success) {
                      // Revert and pass the revert reason along if one was returned.
                      _revertWithReasonIfOneIsReturned();
                      // Otherwise, revert with a generic error message.
                      assembly {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, NativeTokenTransferGenericFailure_error_selector)
                          // Write `to` and `amount` arguments.
                          mstore(NativeTokenTransferGenericFailure_error_account_ptr, to)
                          mstore(
                              NativeTokenTransferGenericFailure_error_amount_ptr,
                              amount
                          )
                          // revert(abi.encodeWithSignature(
                          //     "NativeTokenTransferGenericFailure(address,uint256)",
                          //     to,
                          //     amount
                          // ))
                          revert(
                              Error_selector_offset,
                              NativeTokenTransferGenericFailure_error_length
                          )
                      }
                  }
              }
              /**
               * @dev Internal function to transfer ERC20 tokens from a given originator
               *      to a given recipient using a given conduit if applicable. Sufficient
               *      approvals must be set on this contract or on a respective conduit.
               *
               * @param token       The ERC20 token to transfer.
               * @param from        The originator of the transfer.
               * @param to          The recipient of the transfer.
               * @param amount      The amount to transfer.
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _transferERC20(
                  address token,
                  address from,
                  address to,
                  uint256 amount,
                  bytes32 conduitKey,
                  bytes memory accumulator
              ) internal {
                  // Ensure that the supplied amount is non-zero.
                  _assertNonZeroAmount(amount);
                  // Trigger accumulated transfers if the conduits differ.
                  _triggerIfArmedAndNotAccumulatable(accumulator, conduitKey);
                  // If no conduit has been specified...
                  if (conduitKey == bytes32(0)) {
                      // Perform the token transfer directly.
                      _performERC20Transfer(token, from, to, amount);
                  } else {
                      // Insert the call to the conduit into the accumulator.
                      _insert(
                          conduitKey,
                          accumulator,
                          ConduitItemType.ERC20,
                          token,
                          from,
                          to,
                          uint256(0),
                          amount
                      );
                  }
              }
              /**
               * @dev Internal function to transfer a single ERC721 token from a given
               *      originator to a given recipient. Sufficient approvals must be set,
               *      either on the respective conduit or on this contract itself.
               *
               * @param token       The ERC721 token to transfer.
               * @param from        The originator of the transfer.
               * @param to          The recipient of the transfer.
               * @param identifier  The tokenId to transfer.
               * @param amount      The amount to transfer (must be 1 for ERC721).
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _transferERC721(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount,
                  bytes32 conduitKey,
                  bytes memory accumulator
              ) internal {
                  // Trigger accumulated transfers if the conduits differ.
                  _triggerIfArmedAndNotAccumulatable(accumulator, conduitKey);
                  // If no conduit has been specified...
                  if (conduitKey == bytes32(0)) {
                      // Ensure that exactly one 721 item is being transferred.
                      if (amount != 1) {
                          _revertInvalidERC721TransferAmount(amount);
                      }
                      // Perform transfer via the token contract directly.
                      _performERC721Transfer(token, from, to, identifier);
                  } else {
                      // Insert the call to the conduit into the accumulator.
                      _insert(
                          conduitKey,
                          accumulator,
                          ConduitItemType.ERC721,
                          token,
                          from,
                          to,
                          identifier,
                          amount
                      );
                  }
              }
              /**
               * @dev Internal function to transfer ERC1155 tokens from a given originator
               *      to a given recipient. Sufficient approvals must be set, either on
               *      the respective conduit or on this contract itself.
               *
               * @param token       The ERC1155 token to transfer.
               * @param from        The originator of the transfer.
               * @param to          The recipient of the transfer.
               * @param identifier  The id to transfer.
               * @param amount      The amount to transfer.
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _transferERC1155(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount,
                  bytes32 conduitKey,
                  bytes memory accumulator
              ) internal {
                  // Ensure that the supplied amount is non-zero.
                  _assertNonZeroAmount(amount);
                  // Trigger accumulated transfers if the conduits differ.
                  _triggerIfArmedAndNotAccumulatable(accumulator, conduitKey);
                  // If no conduit has been specified...
                  if (conduitKey == bytes32(0)) {
                      // Perform transfer via the token contract directly.
                      _performERC1155Transfer(token, from, to, identifier, amount);
                  } else {
                      // Insert the call to the conduit into the accumulator.
                      _insert(
                          conduitKey,
                          accumulator,
                          ConduitItemType.ERC1155,
                          token,
                          from,
                          to,
                          identifier,
                          amount
                      );
                  }
              }
              /**
               * @dev Internal function to trigger a call to the conduit currently held by
               *      the accumulator if the accumulator contains item transfers (i.e. it
               *      is "armed") and the supplied conduit key does not match the key held
               *      by the accumulator.
               *
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               */
              function _triggerIfArmedAndNotAccumulatable(
                  bytes memory accumulator,
                  bytes32 conduitKey
              ) internal {
                  // Retrieve the current conduit key from the accumulator.
                  bytes32 accumulatorConduitKey = _getAccumulatorConduitKey(accumulator);
                  // Perform conduit call if the set key does not match the supplied key.
                  if (accumulatorConduitKey != conduitKey) {
                      _triggerIfArmed(accumulator);
                  }
              }
              /**
               * @dev Internal function to trigger a call to the conduit currently held by
               *      the accumulator if the accumulator contains item transfers (i.e. it
               *      is "armed").
               *
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _triggerIfArmed(bytes memory accumulator) internal {
                  // Exit if the accumulator is not "armed".
                  if (accumulator.length != AccumulatorArmed) {
                      return;
                  }
                  // Retrieve the current conduit key from the accumulator.
                  bytes32 accumulatorConduitKey = _getAccumulatorConduitKey(accumulator);
                  // Perform conduit call.
                  _trigger(accumulatorConduitKey, accumulator);
              }
              /**
               * @dev Internal function to trigger a call to the conduit corresponding to
               *      a given conduit key, supplying all accumulated item transfers. The
               *      accumulator will be "disarmed" and reset in the process.
               *
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               */
              function _trigger(bytes32 conduitKey, bytes memory accumulator) internal {
                  // Declare variables for offset in memory & size of calldata to conduit.
                  uint256 callDataOffset;
                  uint256 callDataSize;
                  // Call the conduit with all the accumulated transfers.
                  assembly {
                      // Call begins at third word; the first is length or "armed" status,
                      // and the second is the current conduit key.
                      callDataOffset := add(accumulator, TwoWords)
                      // 68 + items * 192
                      callDataSize := add(
                          Accumulator_array_offset_ptr,
                          mul(
                              mload(add(accumulator, Accumulator_array_length_ptr)),
                              Conduit_transferItem_size
                          )
                      )
                  }
                  // Call conduit derived from conduit key & supply accumulated transfers.
                  _callConduitUsingOffsets(conduitKey, callDataOffset, callDataSize);
                  // Reset accumulator length to signal that it is now "disarmed".
                  assembly {
                      mstore(accumulator, AccumulatorDisarmed)
                  }
              }
              /**
               * @dev Internal function to perform a call to the conduit corresponding to
               *      a given conduit key based on the offset and size of the calldata in
               *      question in memory.
               *
               * @param conduitKey     A bytes32 value indicating what corresponding
               *                       conduit, if any, to source token approvals from.
               *                       The zero hash signifies that no conduit should be
               *                       used, with direct approvals set on this contract.
               * @param callDataOffset The memory pointer where calldata is contained.
               * @param callDataSize   The size of calldata in memory.
               */
              function _callConduitUsingOffsets(
                  bytes32 conduitKey,
                  uint256 callDataOffset,
                  uint256 callDataSize
              ) internal {
                  // Derive the address of the conduit using the conduit key.
                  address conduit = _deriveConduit(conduitKey);
                  bool success;
                  bytes4 result;
                  // call the conduit.
                  assembly {
                      // Ensure first word of scratch space is empty.
                      mstore(0, 0)
                      // Perform call, placing first word of return data in scratch space.
                      success := call(
                          gas(),
                          conduit,
                          0,
                          callDataOffset,
                          callDataSize,
                          0,
                          OneWord
                      )
                      // Take value from scratch space and place it on the stack.
                      result := mload(0)
                  }
                  // If the call failed...
                  if (!success) {
                      // Pass along whatever revert reason was given by the conduit.
                      _revertWithReasonIfOneIsReturned();
                      // Otherwise, revert with a generic error.
                      _revertInvalidCallToConduit(conduit);
                  }
                  // Ensure result was extracted and matches EIP-1271 magic value.
                  if (result != ConduitInterface.execute.selector) {
                      _revertInvalidConduit(conduitKey, conduit);
                  }
              }
              /**
               * @dev Internal pure function to retrieve the current conduit key set for
               *      the accumulator.
               *
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               *
               * @return accumulatorConduitKey The conduit key currently set for the
               *                               accumulator.
               */
              function _getAccumulatorConduitKey(
                  bytes memory accumulator
              ) internal pure returns (bytes32 accumulatorConduitKey) {
                  // Retrieve the current conduit key from the accumulator.
                  assembly {
                      accumulatorConduitKey := mload(
                          add(accumulator, Accumulator_conduitKey_ptr)
                      )
                  }
              }
              /**
               * @dev Internal pure function to place an item transfer into an accumulator
               *      that collects a series of transfers to execute against a given
               *      conduit in a single call.
               *
               * @param conduitKey  A bytes32 value indicating what corresponding conduit,
               *                    if any, to source token approvals from. The zero hash
               *                    signifies that no conduit should be used, with direct
               *                    approvals set on this contract.
               * @param accumulator An open-ended array that collects transfers to execute
               *                    against a given conduit in a single call.
               * @param itemType    The type of the item to transfer.
               * @param token       The token to transfer.
               * @param from        The originator of the transfer.
               * @param to          The recipient of the transfer.
               * @param identifier  The tokenId to transfer.
               * @param amount      The amount to transfer.
               */
              function _insert(
                  bytes32 conduitKey,
                  bytes memory accumulator,
                  ConduitItemType itemType,
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount
              ) internal pure {
                  uint256 elements;
                  // "Arm" and prime accumulator if it's not already armed. The sentinel
                  // value is held in the length of the accumulator array.
                  if (accumulator.length == AccumulatorDisarmed) {
                      elements = 1;
                      bytes4 selector = ConduitInterface.execute.selector;
                      assembly {
                          mstore(accumulator, AccumulatorArmed) // "arm" the accumulator.
                          mstore(add(accumulator, Accumulator_conduitKey_ptr), conduitKey)
                          mstore(add(accumulator, Accumulator_selector_ptr), selector)
                          mstore(
                              add(accumulator, Accumulator_array_offset_ptr),
                              Accumulator_array_offset
                          )
                          mstore(add(accumulator, Accumulator_array_length_ptr), elements)
                      }
                  } else {
                      // Otherwise, increase the number of elements by one.
                      assembly {
                          elements := add(
                              mload(add(accumulator, Accumulator_array_length_ptr)),
                              1
                          )
                          mstore(add(accumulator, Accumulator_array_length_ptr), elements)
                      }
                  }
                  // Insert the item.
                  assembly {
                      let itemPointer := sub(
                          add(accumulator, mul(elements, Conduit_transferItem_size)),
                          Accumulator_itemSizeOffsetDifference
                      )
                      mstore(itemPointer, itemType)
                      mstore(add(itemPointer, Conduit_transferItem_token_ptr), token)
                      mstore(add(itemPointer, Conduit_transferItem_from_ptr), from)
                      mstore(add(itemPointer, Conduit_transferItem_to_ptr), to)
                      mstore(
                          add(itemPointer, Conduit_transferItem_identifier_ptr),
                          identifier
                      )
                      mstore(add(itemPointer, Conduit_transferItem_amount_ptr), amount)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { OrderType } from "./ConsiderationEnums.sol";
          import {
              AdvancedOrder,
              BasicOrderParameters,
              OrderParameters
          } from "./ConsiderationStructs.sol";
          import { ZoneInteractionErrors } from "../interfaces/ZoneInteractionErrors.sol";
          import { LowLevelHelpers } from "./LowLevelHelpers.sol";
          import { ConsiderationEncoder } from "./ConsiderationEncoder.sol";
          import { MemoryPointer } from "../helpers/PointerLibraries.sol";
          import {
              ContractOrder_orderHash_offerer_shift,
              MaskOverFirstFourBytes,
              OneWord,
              OrderParameters_zone_offset
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              InvalidContractOrder_error_selector,
              InvalidRestrictedOrder_error_length,
              InvalidRestrictedOrder_error_orderHash_ptr,
              InvalidRestrictedOrder_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title ZoneInteraction
           * @author 0age
           * @notice ZoneInteraction contains logic related to interacting with zones.
           */
          contract ZoneInteraction is
              ConsiderationEncoder,
              ZoneInteractionErrors,
              LowLevelHelpers
          {
              /**
               * @dev Internal function to determine if an order has a restricted order
               *      type and, if so, to ensure that either the zone is the caller or
               *      that a call to `validateOrder` on the zone returns a magic value
               *      indicating that the order is currently valid. Note that contract
               *      orders are not accessible via the basic fulfillment method.
               *
               * @param orderHash  The hash of the order.
               * @param orderType  The order type.
               * @param parameters The parameters of the basic order.
               */
              function _assertRestrictedBasicOrderValidity(
                  bytes32 orderHash,
                  OrderType orderType,
                  BasicOrderParameters calldata parameters
              ) internal {
                  // Order type 2-3 require zone be caller or zone to approve.
                  // Note that in cases where fulfiller == zone, the restricted order
                  // validation will be skipped.
                  if (_isRestrictedAndCallerNotZone(orderType, parameters.zone)) {
                      // Encode the `validateOrder` call in memory.
                      (MemoryPointer callData, uint256 size) = _encodeValidateBasicOrder(
                          orderHash,
                          parameters
                      );
                      // Perform `validateOrder` call and ensure magic value was returned.
                      _callAndCheckStatus(
                          parameters.zone,
                          orderHash,
                          callData,
                          size,
                          InvalidRestrictedOrder_error_selector
                      );
                  }
              }
              /**
               * @dev Internal function to determine the post-execution validity of
               *      restricted and contract orders. Restricted orders where the caller
               *      is not the zone must successfully call `validateOrder` with the
               *      correct magic value returned. Contract orders must successfully call
               *      `ratifyOrder` with the correct magic value returned.
               *
               * @param advancedOrder The advanced order in question.
               * @param orderHashes   The order hashes of each order included as part of
               *                      the current fulfillment.
               * @param orderHash     The hash of the order.
               */
              function _assertRestrictedAdvancedOrderValidity(
                  AdvancedOrder memory advancedOrder,
                  bytes32[] memory orderHashes,
                  bytes32 orderHash
              ) internal {
                  // Declare variables that will be assigned based on the order type.
                  address target;
                  uint256 errorSelector;
                  MemoryPointer callData;
                  uint256 size;
                  // Retrieve the parameters of the order in question.
                  OrderParameters memory parameters = advancedOrder.parameters;
                  // OrderType 2-3 require zone to be caller or approve via validateOrder.
                  if (
                      _isRestrictedAndCallerNotZone(parameters.orderType, parameters.zone)
                  ) {
                      // Encode the `validateOrder` call in memory.
                      (callData, size) = _encodeValidateOrder(
                          orderHash,
                          parameters,
                          advancedOrder.extraData,
                          orderHashes
                      );
                      // Set the target to the zone.
                      target = (
                          parameters
                              .toMemoryPointer()
                              .offset(OrderParameters_zone_offset)
                              .readAddress()
                      );
                      // Set the restricted-order-specific error selector.
                      errorSelector = InvalidRestrictedOrder_error_selector;
                  } else if (parameters.orderType == OrderType.CONTRACT) {
                      // Set the target to the offerer (note the offerer has no offset).
                      target = parameters.toMemoryPointer().readAddress();
                      // Shift the target 96 bits to the left.
                      uint256 shiftedOfferer;
                      assembly {
                          shiftedOfferer := shl(
                              ContractOrder_orderHash_offerer_shift,
                              target
                          )
                      }
                      // Encode the `ratifyOrder` call in memory.
                      (callData, size) = _encodeRatifyOrder(
                          orderHash,
                          parameters,
                          advancedOrder.extraData,
                          orderHashes,
                          shiftedOfferer
                      );
                      // Set the contract-order-specific error selector.
                      errorSelector = InvalidContractOrder_error_selector;
                  } else {
                      return;
                  }
                  // Perform call and ensure a corresponding magic value was returned.
                  _callAndCheckStatus(target, orderHash, callData, size, errorSelector);
              }
              /**
               * @dev Determines whether the specified order type is restricted and the
               *      caller is not the specified zone.
               *
               * @param orderType     The type of the order to check.
               * @param zone          The address of the zone to check against.
               *
               * @return mustValidate True if the order type is restricted and the caller
               *                      is not the specified zone, false otherwise.
               */
              function _isRestrictedAndCallerNotZone(
                  OrderType orderType,
                  address zone
              ) internal view returns (bool mustValidate) {
                  assembly {
                      mustValidate := and(
                          // Note that this check requires that there are no order types
                          // beyond the current set (0-4).  It will need to be modified if
                          // more order types are added.
                          and(lt(orderType, 4), gt(orderType, 1)),
                          iszero(eq(caller(), zone))
                      )
                  }
              }
              /**
               * @dev Calls the specified target with the given data and checks the status
               *      of the call. Revert reasons will be "bubbled up" if one is returned,
               *      otherwise reverting calls will throw a generic error based on the
               *      supplied error handler.
               *
               * @param target        The address of the contract to call.
               * @param orderHash     The hash of the order associated with the call.
               * @param callData      The data to pass to the contract call.
               * @param size          The size of calldata.
               * @param errorSelector The error handling function to call if the call
               *                      fails or the magic value does not match.
               */
              function _callAndCheckStatus(
                  address target,
                  bytes32 orderHash,
                  MemoryPointer callData,
                  uint256 size,
                  uint256 errorSelector
              ) internal {
                  bool success;
                  bool magicMatch;
                  assembly {
                      // Get magic value from the selector at start of provided calldata.
                      let magic := and(mload(callData), MaskOverFirstFourBytes)
                      // Clear the start of scratch space.
                      mstore(0, 0)
                      // Perform call, placing result in the first word of scratch space.
                      success := call(gas(), target, 0, callData, size, 0, OneWord)
                      // Determine if returned magic value matches the calldata selector.
                      magicMatch := eq(magic, mload(0))
                  }
                  // Revert if the call was not successful.
                  if (!success) {
                      // Revert and pass reason along if one was returned.
                      _revertWithReasonIfOneIsReturned();
                      // If no reason was returned, revert with supplied error selector.
                      assembly {
                          mstore(0, errorSelector)
                          mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash)
                          // revert(abi.encodeWithSelector(
                          //     "InvalidRestrictedOrder(bytes32)",
                          //     orderHash
                          // ))
                          revert(
                              Error_selector_offset,
                              InvalidRestrictedOrder_error_length
                          )
                      }
                  }
                  // Revert if the correct magic value was not returned.
                  if (!magicMatch) {
                      // Revert with a generic error message.
                      assembly {
                          mstore(0, errorSelector)
                          mstore(InvalidRestrictedOrder_error_orderHash_ptr, orderHash)
                          // revert(abi.encodeWithSelector(
                          //     "InvalidRestrictedOrder(bytes32)",
                          //     orderHash
                          // ))
                          revert(
                              Error_selector_offset,
                              InvalidRestrictedOrder_error_length
                          )
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {
              ConduitBatch1155Transfer,
              ConduitTransfer
          } from "../conduit/lib/ConduitStructs.sol";
          /**
           * @title ConduitInterface
           * @author 0age
           * @notice ConduitInterface contains all external function interfaces, events,
           *         and errors for conduit contracts.
           */
          interface ConduitInterface {
              /**
               * @dev Revert with an error when attempting to execute transfers using a
               *      caller that does not have an open channel.
               */
              error ChannelClosed(address channel);
              /**
               * @dev Revert with an error when attempting to update a channel to the
               *      current status of that channel.
               */
              error ChannelStatusAlreadySet(address channel, bool isOpen);
              /**
               * @dev Revert with an error when attempting to execute a transfer for an
               *      item that does not have an ERC20/721/1155 item type.
               */
              error InvalidItemType();
              /**
               * @dev Revert with an error when attempting to update the status of a
               *      channel from a caller that is not the conduit controller.
               */
              error InvalidController();
              /**
               * @dev Emit an event whenever a channel is opened or closed.
               *
               * @param channel The channel that has been updated.
               * @param open    A boolean indicating whether the conduit is open or not.
               */
              event ChannelUpdated(address indexed channel, bool open);
              /**
               * @notice Execute a sequence of ERC20/721/1155 transfers. Only a caller
               *         with an open channel can call this function.
               *
               * @param transfers The ERC20/721/1155 transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function execute(
                  ConduitTransfer[] calldata transfers
              ) external returns (bytes4 magicValue);
              /**
               * @notice Execute a sequence of batch 1155 transfers. Only a caller with an
               *         open channel can call this function.
               *
               * @param batch1155Transfers The 1155 batch transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function executeBatch1155(
                  ConduitBatch1155Transfer[] calldata batch1155Transfers
              ) external returns (bytes4 magicValue);
              /**
               * @notice Execute a sequence of transfers, both single and batch 1155. Only
               *         a caller with an open channel can call this function.
               *
               * @param standardTransfers  The ERC20/721/1155 transfers to perform.
               * @param batch1155Transfers The 1155 batch transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function executeWithBatch1155(
                  ConduitTransfer[] calldata standardTransfers,
                  ConduitBatch1155Transfer[] calldata batch1155Transfers
              ) external returns (bytes4 magicValue);
              /**
               * @notice Open or close a given channel. Only callable by the controller.
               *
               * @param channel The channel to open or close.
               * @param isOpen  The status of the channel (either open or closed).
               */
              function updateChannel(address channel, bool isOpen) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { OrderStatus } from "./ConsiderationStructs.sol";
          import { Assertions } from "./Assertions.sol";
          import { SignatureVerification } from "./SignatureVerification.sol";
          import {
              _revertInvalidTime,
              _revertOrderAlreadyFilled,
              _revertOrderIsCancelled,
              _revertOrderPartiallyFilled
          } from "./ConsiderationErrors.sol";
          import {
              BulkOrderProof_keyShift,
              BulkOrderProof_keySize,
              BulkOrderProof_lengthAdjustmentBeforeMask,
              BulkOrderProof_lengthRangeAfterMask,
              BulkOrderProof_minSize,
              BulkOrderProof_rangeSize,
              ECDSA_MaxLength,
              OneWord,
              OneWordShift,
              ThirtyOneBytes,
              TwoWords
          } from "./ConsiderationConstants.sol";
          /**
           * @title Verifiers
           * @author 0age
           * @notice Verifiers contains functions for performing verifications.
           */
          contract Verifiers is Assertions, SignatureVerification {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) Assertions(conduitController) {}
              /**
               * @dev Internal view function to ensure that the current time falls within
               *      an order's valid timespan.
               *
               * @param startTime       The time at which the order becomes active.
               * @param endTime         The time at which the order becomes inactive.
               * @param revertOnInvalid A boolean indicating whether to revert if the
               *                        order is not active.
               *
               * @return valid A boolean indicating whether the order is active.
               */
              function _verifyTime(
                  uint256 startTime,
                  uint256 endTime,
                  bool revertOnInvalid
              ) internal view returns (bool valid) {
                  // Mark as valid if order has started and has not already ended.
                  assembly {
                      valid := and(
                          iszero(gt(startTime, timestamp())),
                          gt(endTime, timestamp())
                      )
                  }
                  // Only revert on invalid if revertOnInvalid has been supplied as true.
                  if (revertOnInvalid && !valid) {
                      _revertInvalidTime(startTime, endTime);
                  }
              }
              /**
               * @dev Internal view function to verify the signature of an order. An
               *      ERC-1271 fallback will be attempted if either the signature length
               *      is not 64 or 65 bytes or if the recovered signer does not match the
               *      supplied offerer. Note that in cases where a 64 or 65 byte signature
               *      is supplied, only standard ECDSA signatures that recover to a
               *      non-zero address are supported.
               *
               * @param offerer   The offerer for the order.
               * @param orderHash The order hash.
               * @param signature A signature from the offerer indicating that the order
               *                  has been approved.
               */
              function _verifySignature(
                  address offerer,
                  bytes32 orderHash,
                  bytes memory signature
              ) internal view {
                  // Determine whether the offerer is the caller.
                  bool offererIsCaller;
                  assembly {
                      offererIsCaller := eq(offerer, caller())
                  }
                  // Skip signature verification if the offerer is the caller.
                  if (offererIsCaller) {
                      return;
                  }
                  // Derive the EIP-712 domain separator.
                  bytes32 domainSeparator = _domainSeparator();
                  // Derive original EIP-712 digest using domain separator and order hash.
                  bytes32 originalDigest = _deriveEIP712Digest(
                      domainSeparator,
                      orderHash
                  );
                  // Read the length of the signature from memory and place on the stack.
                  uint256 originalSignatureLength = signature.length;
                  // Determine effective digest if signature has a valid bulk order size.
                  bytes32 digest;
                  if (_isValidBulkOrderSize(originalSignatureLength)) {
                      // Rederive order hash and digest using bulk order proof.
                      (orderHash) = _computeBulkOrderProof(signature, orderHash);
                      digest = _deriveEIP712Digest(domainSeparator, orderHash);
                  } else {
                      // Supply the original digest as the effective digest.
                      digest = originalDigest;
                  }
                  // Ensure that the signature for the digest is valid for the offerer.
                  _assertValidSignature(
                      offerer,
                      digest,
                      originalDigest,
                      originalSignatureLength,
                      signature
                  );
              }
              /**
               * @dev Determines whether the specified bulk order size is valid.
               *
               * @param signatureLength The signature length of the bulk order to check.
               *
               * @return validLength True if bulk order size is valid, false otherwise.
               */
              function _isValidBulkOrderSize(
                  uint256 signatureLength
              ) internal pure returns (bool validLength) {
                  // Utilize assembly to validate the length; the equivalent logic is
                  // (64 + x) + 3 + 32y where (0 <= x <= 1) and (1 <= y <= 24).
                  assembly {
                      validLength := and(
                          lt(
                              sub(signatureLength, BulkOrderProof_minSize),
                              BulkOrderProof_rangeSize
                          ),
                          lt(
                              and(
                                  add(
                                      signatureLength,
                                      BulkOrderProof_lengthAdjustmentBeforeMask
                                  ),
                                  ThirtyOneBytes
                              ),
                              BulkOrderProof_lengthRangeAfterMask
                          )
                      )
                  }
              }
              /**
               * @dev Computes the bulk order hash for the specified proof and leaf. Note
               *      that if an index that exceeds the number of orders in the bulk order
               *      payload will instead "wrap around" and refer to an earlier index.
               *
               * @param proofAndSignature The proof and signature of the bulk order.
               * @param leaf              The leaf of the bulk order tree.
               *
               * @return bulkOrderHash The bulk order hash.
               */
              function _computeBulkOrderProof(
                  bytes memory proofAndSignature,
                  bytes32 leaf
              ) internal pure returns (bytes32 bulkOrderHash) {
                  // Declare arguments for the root hash and the height of the proof.
                  bytes32 root;
                  uint256 height;
                  // Utilize assembly to efficiently derive the root hash using the proof.
                  assembly {
                      // Retrieve the length of the proof, key, and signature combined.
                      let fullLength := mload(proofAndSignature)
                      // If proofAndSignature has odd length, it is a compact signature
                      // with 64 bytes.
                      let signatureLength := sub(ECDSA_MaxLength, and(fullLength, 1))
                      // Derive height (or depth of tree) with signature and proof length.
                      height := shr(OneWordShift, sub(fullLength, signatureLength))
                      // Update the length in memory to only include the signature.
                      mstore(proofAndSignature, signatureLength)
                      // Derive the pointer for the key using the signature length.
                      let keyPtr := add(proofAndSignature, add(OneWord, signatureLength))
                      // Retrieve the three-byte key using the derived pointer.
                      let key := shr(BulkOrderProof_keyShift, mload(keyPtr))
                      /// Retrieve pointer to first proof element by applying a constant
                      // for the key size to the derived key pointer.
                      let proof := add(keyPtr, BulkOrderProof_keySize)
                      // Compute level 1.
                      let scratchPtr1 := shl(OneWordShift, and(key, 1))
                      mstore(scratchPtr1, leaf)
                      mstore(xor(scratchPtr1, OneWord), mload(proof))
                      // Compute remaining proofs.
                      for {
                          let i := 1
                      } lt(i, height) {
                          i := add(i, 1)
                      } {
                          proof := add(proof, OneWord)
                          let scratchPtr := shl(OneWordShift, and(shr(i, key), 1))
                          mstore(scratchPtr, keccak256(0, TwoWords))
                          mstore(xor(scratchPtr, OneWord), mload(proof))
                      }
                      // Compute root hash.
                      root := keccak256(0, TwoWords)
                  }
                  // Retrieve appropriate typehash constant based on height.
                  bytes32 rootTypeHash = _lookupBulkOrderTypehash(height);
                  // Use the typehash and the root hash to derive final bulk order hash.
                  assembly {
                      mstore(0, rootTypeHash)
                      mstore(OneWord, root)
                      bulkOrderHash := keccak256(0, TwoWords)
                  }
              }
              /**
               * @dev Internal view function to validate that a given order is fillable
               *      and not cancelled based on the order status.
               *
               * @param orderHash       The order hash.
               * @param orderStatus     The status of the order, including whether it has
               *                        been cancelled and the fraction filled.
               * @param onlyAllowUnused A boolean flag indicating whether partial fills
               *                        are supported by the calling function.
               * @param revertOnInvalid A boolean indicating whether to revert if the
               *                        order has been cancelled or filled beyond the
               *                        allowable amount.
               *
               * @return valid A boolean indicating whether the order is valid.
               */
              function _verifyOrderStatus(
                  bytes32 orderHash,
                  OrderStatus storage orderStatus,
                  bool onlyAllowUnused,
                  bool revertOnInvalid
              ) internal view returns (bool valid) {
                  // Ensure that the order has not been cancelled.
                  if (orderStatus.isCancelled) {
                      // Only revert if revertOnInvalid has been supplied as true.
                      if (revertOnInvalid) {
                          _revertOrderIsCancelled(orderHash);
                      }
                      // Return false as the order status is invalid.
                      return false;
                  }
                  // Read order status numerator from storage and place on stack.
                  uint256 orderStatusNumerator = orderStatus.numerator;
                  // If the order is not entirely unused...
                  if (orderStatusNumerator != 0) {
                      // ensure the order has not been partially filled when not allowed.
                      if (onlyAllowUnused) {
                          // Always revert on partial fills when onlyAllowUnused is true.
                          _revertOrderPartiallyFilled(orderHash);
                      }
                      // Otherwise, ensure that order has not been entirely filled.
                      else if (orderStatusNumerator >= orderStatus.denominator) {
                          // Only revert if revertOnInvalid has been supplied as true.
                          if (revertOnInvalid) {
                              _revertOrderAlreadyFilled(orderHash);
                          }
                          // Return false as the order status is invalid.
                          return false;
                      }
                  }
                  // Return true as the order status is valid.
                  valid = true;
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {
              BadReturnValueFromERC20OnTransfer_error_amount_ptr,
              BadReturnValueFromERC20OnTransfer_error_from_ptr,
              BadReturnValueFromERC20OnTransfer_error_length,
              BadReturnValueFromERC20OnTransfer_error_selector,
              BadReturnValueFromERC20OnTransfer_error_to_ptr,
              BadReturnValueFromERC20OnTransfer_error_token_ptr,
              BatchTransfer1155Params_amounts_head_ptr,
              BatchTransfer1155Params_calldata_baseSize,
              BatchTransfer1155Params_data_head_ptr,
              BatchTransfer1155Params_data_length_basePtr,
              BatchTransfer1155Params_ids_head_ptr,
              BatchTransfer1155Params_ids_length_offset,
              BatchTransfer1155Params_ids_length_ptr,
              BatchTransfer1155Params_ptr,
              ConduitBatch1155Transfer_amounts_length_baseOffset,
              ConduitBatch1155Transfer_from_offset,
              ConduitBatch1155Transfer_ids_head_offset,
              ConduitBatch1155Transfer_ids_length_offset,
              ConduitBatch1155Transfer_usable_head_size,
              ConduitBatchTransfer_amounts_head_offset,
              CostPerWord,
              DefaultFreeMemoryPointer,
              ERC1155_safeBatchTransferFrom_signature,
              ERC1155_safeTransferFrom_amount_ptr,
              ERC1155_safeTransferFrom_data_length_offset,
              ERC1155_safeTransferFrom_data_length_ptr,
              ERC1155_safeTransferFrom_data_offset_ptr,
              ERC1155_safeTransferFrom_from_ptr,
              ERC1155_safeTransferFrom_id_ptr,
              ERC1155_safeTransferFrom_length,
              ERC1155_safeTransferFrom_sig_ptr,
              ERC1155_safeTransferFrom_signature,
              ERC1155_safeTransferFrom_to_ptr,
              ERC1155BatchTransferGenericFailure_error_signature,
              ERC1155BatchTransferGenericFailure_ids_offset,
              ERC1155BatchTransferGenericFailure_token_ptr,
              ERC20_transferFrom_amount_ptr,
              ERC20_transferFrom_from_ptr,
              ERC20_transferFrom_length,
              ERC20_transferFrom_sig_ptr,
              ERC20_transferFrom_signature,
              ERC20_transferFrom_to_ptr,
              ERC721_transferFrom_from_ptr,
              ERC721_transferFrom_id_ptr,
              ERC721_transferFrom_length,
              ERC721_transferFrom_sig_ptr,
              ERC721_transferFrom_signature,
              ERC721_transferFrom_to_ptr,
              ExtraGasBuffer,
              FreeMemoryPointerSlot,
              Generic_error_selector_offset,
              Invalid1155BatchTransferEncoding_length,
              Invalid1155BatchTransferEncoding_ptr,
              Invalid1155BatchTransferEncoding_selector,
              MemoryExpansionCoefficientShift,
              NoContract_error_account_ptr,
              NoContract_error_length,
              NoContract_error_selector,
              OneWord,
              OneWordShift,
              Slot0x80,
              Slot0xA0,
              Slot0xC0,
              ThirtyOneBytes,
              TokenTransferGenericFailure_err_identifier_ptr,
              TokenTransferGenericFailure_error_amount_ptr,
              TokenTransferGenericFailure_error_from_ptr,
              TokenTransferGenericFailure_error_identifier_ptr,
              TokenTransferGenericFailure_error_length,
              TokenTransferGenericFailure_error_selector,
              TokenTransferGenericFailure_error_to_ptr,
              TokenTransferGenericFailure_error_token_ptr,
              TwoWords,
              TwoWordsShift,
              ZeroSlot
          } from "./TokenTransferrerConstants.sol";
          import {
              TokenTransferrerErrors
          } from "../interfaces/TokenTransferrerErrors.sol";
          import { ConduitBatch1155Transfer } from "../conduit/lib/ConduitStructs.sol";
          /**
           * @title TokenTransferrer
           * @author 0age
           * @custom:coauthor d1ll0n
           * @custom:coauthor transmissions11
           * @notice TokenTransferrer is a library for performing optimized ERC20, ERC721,
           *         ERC1155, and batch ERC1155 transfers, used by both Seaport as well as
           *         by conduits deployed by the ConduitController. Use great caution when
           *         considering these functions for use in other codebases, as there are
           *         significant side effects and edge cases that need to be thoroughly
           *         understood and carefully addressed.
           */
          contract TokenTransferrer is TokenTransferrerErrors {
              /**
               * @dev Internal function to transfer ERC20 tokens from a given originator
               *      to a given recipient. Sufficient approvals must be set on the
               *      contract performing the transfer.
               *
               * @param token      The ERC20 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param amount     The amount to transfer.
               */
              function _performERC20Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 amount
              ) internal {
                  // Utilize assembly to perform an optimized ERC20 token transfer.
                  assembly {
                      // The free memory pointer memory slot will be used when populating
                      // call data for the transfer; read the value and restore it later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      // Write call data into memory, starting with function selector.
                      mstore(ERC20_transferFrom_sig_ptr, ERC20_transferFrom_signature)
                      mstore(ERC20_transferFrom_from_ptr, from)
                      mstore(ERC20_transferFrom_to_ptr, to)
                      mstore(ERC20_transferFrom_amount_ptr, amount)
                      // Make call & copy up to 32 bytes of return data to scratch space.
                      // Scratch space does not need to be cleared ahead of time, as the
                      // subsequent check will ensure that either at least a full word of
                      // return data is received (in which case it will be overwritten) or
                      // that no data is received (in which case scratch space will be
                      // ignored) on a successful call to the given token.
                      let callStatus := call(
                          gas(),
                          token,
                          0,
                          ERC20_transferFrom_sig_ptr,
                          ERC20_transferFrom_length,
                          0,
                          OneWord
                      )
                      // Determine whether transfer was successful using status & result.
                      let success := and(
                          // Set success to whether the call reverted, if not check it
                          // either returned exactly 1 (can't just be non-zero data), or
                          // had no return data.
                          or(
                              and(eq(mload(0), 1), gt(returndatasize(), 31)),
                              iszero(returndatasize())
                          ),
                          callStatus
                      )
                      // Handle cases where either the transfer failed or no data was
                      // returned. Group these, as most transfers will succeed with data.
                      // Equivalent to `or(iszero(success), iszero(returndatasize()))`
                      // but after it's inverted for JUMPI this expression is cheaper.
                      if iszero(and(success, iszero(iszero(returndatasize())))) {
                          // If the token has no code or the transfer failed: Equivalent
                          // to `or(iszero(success), iszero(extcodesize(token)))` but
                          // after it's inverted for JUMPI this expression is cheaper.
                          if iszero(and(iszero(iszero(extcodesize(token))), success)) {
                              // If the transfer failed:
                              if iszero(success) {
                                  // If it was due to a revert:
                                  if iszero(callStatus) {
                                      // If it returned a message, bubble it up as long as
                                      // sufficient gas remains to do so:
                                      if returndatasize() {
                                          // Ensure that sufficient gas is available to
                                          // copy returndata while expanding memory where
                                          // necessary. Start by computing the word size
                                          // of returndata and allocated memory. Round up
                                          // to the nearest full word.
                                          let returnDataWords := shr(
                                              OneWordShift,
                                              add(returndatasize(), ThirtyOneBytes)
                                          )
                                          // Note: use the free memory pointer in place of
                                          // msize() to work around a Yul warning that
                                          // prevents accessing msize directly when the IR
                                          // pipeline is activated.
                                          let msizeWords := shr(OneWordShift, memPointer)
                                          // Next, compute the cost of the returndatacopy.
                                          let cost := mul(CostPerWord, returnDataWords)
                                          // Then, compute cost of new memory allocation.
                                          if gt(returnDataWords, msizeWords) {
                                              cost := add(
                                                  cost,
                                                  add(
                                                      mul(
                                                          sub(
                                                              returnDataWords,
                                                              msizeWords
                                                          ),
                                                          CostPerWord
                                                      ),
                                                      shr(
                                                          MemoryExpansionCoefficientShift,
                                                          sub(
                                                              mul(
                                                                  returnDataWords,
                                                                  returnDataWords
                                                              ),
                                                              mul(msizeWords, msizeWords)
                                                          )
                                                      )
                                                  )
                                              )
                                          }
                                          // Finally, add a small constant and compare to
                                          // gas remaining; bubble up the revert data if
                                          // enough gas is still available.
                                          if lt(add(cost, ExtraGasBuffer), gas()) {
                                              // Copy returndata to memory; overwrite
                                              // existing memory.
                                              returndatacopy(0, 0, returndatasize())
                                              // Revert, specifying memory region with
                                              // copied returndata.
                                              revert(0, returndatasize())
                                          }
                                      }
                                      // Store left-padded selector with push4, mem[28:32]
                                      mstore(
                                          0,
                                          TokenTransferGenericFailure_error_selector
                                      )
                                      mstore(
                                          TokenTransferGenericFailure_error_token_ptr,
                                          token
                                      )
                                      mstore(
                                          TokenTransferGenericFailure_error_from_ptr,
                                          from
                                      )
                                      mstore(TokenTransferGenericFailure_error_to_ptr, to)
                                      mstore(
                                          TokenTransferGenericFailure_err_identifier_ptr,
                                          0
                                      )
                                      mstore(
                                          TokenTransferGenericFailure_error_amount_ptr,
                                          amount
                                      )
                                      // revert(abi.encodeWithSignature(
                                      //     "TokenTransferGenericFailure(
                                      //         address,address,address,uint256,uint256
                                      //     )", token, from, to, identifier, amount
                                      // ))
                                      revert(
                                          Generic_error_selector_offset,
                                          TokenTransferGenericFailure_error_length
                                      )
                                  }
                                  // Otherwise revert with a message about the token
                                  // returning false or non-compliant return values.
                                  // Store left-padded selector with push4, mem[28:32]
                                  mstore(
                                      0,
                                      BadReturnValueFromERC20OnTransfer_error_selector
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_token_ptr,
                                      token
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_from_ptr,
                                      from
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_to_ptr,
                                      to
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_amount_ptr,
                                      amount
                                  )
                                  // revert(abi.encodeWithSignature(
                                  //     "BadReturnValueFromERC20OnTransfer(
                                  //         address,address,address,uint256
                                  //     )", token, from, to, amount
                                  // ))
                                  revert(
                                      Generic_error_selector_offset,
                                      BadReturnValueFromERC20OnTransfer_error_length
                                  )
                              }
                              // Otherwise, revert with error about token not having code:
                              // Store left-padded selector with push4, mem[28:32]
                              mstore(0, NoContract_error_selector)
                              mstore(NoContract_error_account_ptr, token)
                              // revert(abi.encodeWithSignature(
                              //      "NoContract(address)", account
                              // ))
                              revert(
                                  Generic_error_selector_offset,
                                  NoContract_error_length
                              )
                          }
                          // Otherwise, the token just returned no data despite the call
                          // having succeeded; no need to optimize for this as it's not
                          // technically ERC20 compliant.
                      }
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer an ERC721 token from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer. Note that this function does
               *      not check whether the receiver can accept the ERC721 token (i.e. it
               *      does not use `safeTransferFrom`).
               *
               * @param token      The ERC721 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param identifier The tokenId to transfer.
               */
              function _performERC721Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 identifier
              ) internal {
                  // Utilize assembly to perform an optimized ERC721 token transfer.
                  assembly {
                      // If the token has no code, revert.
                      if iszero(extcodesize(token)) {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, NoContract_error_selector)
                          mstore(NoContract_error_account_ptr, token)
                          // revert(abi.encodeWithSignature(
                          //     "NoContract(address)", account
                          // ))
                          revert(Generic_error_selector_offset, NoContract_error_length)
                      }
                      // The free memory pointer memory slot will be used when populating
                      // call data for the transfer; read the value and restore it later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      // Write call data to memory starting with function selector.
                      mstore(ERC721_transferFrom_sig_ptr, ERC721_transferFrom_signature)
                      mstore(ERC721_transferFrom_from_ptr, from)
                      mstore(ERC721_transferFrom_to_ptr, to)
                      mstore(ERC721_transferFrom_id_ptr, identifier)
                      // Perform the call, ignoring return data.
                      let success := call(
                          gas(),
                          token,
                          0,
                          ERC721_transferFrom_sig_ptr,
                          ERC721_transferFrom_length,
                          0,
                          0
                      )
                      // If the transfer reverted:
                      if iszero(success) {
                          // If it returned a message, bubble it up as long as sufficient
                          // gas remains to do so:
                          if returndatasize() {
                              // Ensure that sufficient gas is available to copy
                              // returndata while expanding memory where necessary. Start
                              // by computing word size of returndata & allocated memory.
                              // Round up to the nearest full word.
                              let returnDataWords := shr(
                                  OneWordShift,
                                  add(returndatasize(), ThirtyOneBytes)
                              )
                              // Note: use the free memory pointer in place of msize() to
                              // work around a Yul warning that prevents accessing msize
                              // directly when the IR pipeline is activated.
                              let msizeWords := shr(OneWordShift, memPointer)
                              // Next, compute the cost of the returndatacopy.
                              let cost := mul(CostPerWord, returnDataWords)
                              // Then, compute cost of new memory allocation.
                              if gt(returnDataWords, msizeWords) {
                                  cost := add(
                                      cost,
                                      add(
                                          mul(
                                              sub(returnDataWords, msizeWords),
                                              CostPerWord
                                          ),
                                          shr(
                                              MemoryExpansionCoefficientShift,
                                              sub(
                                                  mul(returnDataWords, returnDataWords),
                                                  mul(msizeWords, msizeWords)
                                              )
                                          )
                                      )
                                  )
                              }
                              // Finally, add a small constant and compare to gas
                              // remaining; bubble up the revert data if enough gas is
                              // still available.
                              if lt(add(cost, ExtraGasBuffer), gas()) {
                                  // Copy returndata to memory; overwrite existing memory.
                                  returndatacopy(0, 0, returndatasize())
                                  // Revert, giving memory region with copied returndata.
                                  revert(0, returndatasize())
                              }
                          }
                          // Otherwise revert with a generic error message.
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, TokenTransferGenericFailure_error_selector)
                          mstore(TokenTransferGenericFailure_error_token_ptr, token)
                          mstore(TokenTransferGenericFailure_error_from_ptr, from)
                          mstore(TokenTransferGenericFailure_error_to_ptr, to)
                          mstore(
                              TokenTransferGenericFailure_error_identifier_ptr,
                              identifier
                          )
                          mstore(TokenTransferGenericFailure_error_amount_ptr, 1)
                          // revert(abi.encodeWithSignature(
                          //     "TokenTransferGenericFailure(
                          //         address,address,address,uint256,uint256
                          //     )", token, from, to, identifier, amount
                          // ))
                          revert(
                              Generic_error_selector_offset,
                              TokenTransferGenericFailure_error_length
                          )
                      }
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer ERC1155 tokens from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer and contract recipients must
               *      implement the ERC1155TokenReceiver interface to indicate that they
               *      are willing to accept the transfer.
               *
               * @param token      The ERC1155 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param identifier The id to transfer.
               * @param amount     The amount to transfer.
               */
              function _performERC1155Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount
              ) internal {
                  // Utilize assembly to perform an optimized ERC1155 token transfer.
                  assembly {
                      // If the token has no code, revert.
                      if iszero(extcodesize(token)) {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, NoContract_error_selector)
                          mstore(NoContract_error_account_ptr, token)
                          // revert(abi.encodeWithSignature(
                          //     "NoContract(address)", account
                          // ))
                          revert(Generic_error_selector_offset, NoContract_error_length)
                      }
                      // The following memory slots will be used when populating call data
                      // for the transfer; read the values and restore them later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      let slot0x80 := mload(Slot0x80)
                      let slot0xA0 := mload(Slot0xA0)
                      let slot0xC0 := mload(Slot0xC0)
                      // Write call data into memory, beginning with function selector.
                      mstore(
                          ERC1155_safeTransferFrom_sig_ptr,
                          ERC1155_safeTransferFrom_signature
                      )
                      mstore(ERC1155_safeTransferFrom_from_ptr, from)
                      mstore(ERC1155_safeTransferFrom_to_ptr, to)
                      mstore(ERC1155_safeTransferFrom_id_ptr, identifier)
                      mstore(ERC1155_safeTransferFrom_amount_ptr, amount)
                      mstore(
                          ERC1155_safeTransferFrom_data_offset_ptr,
                          ERC1155_safeTransferFrom_data_length_offset
                      )
                      mstore(ERC1155_safeTransferFrom_data_length_ptr, 0)
                      // Perform the call, ignoring return data.
                      let success := call(
                          gas(),
                          token,
                          0,
                          ERC1155_safeTransferFrom_sig_ptr,
                          ERC1155_safeTransferFrom_length,
                          0,
                          0
                      )
                      // If the transfer reverted:
                      if iszero(success) {
                          // If it returned a message, bubble it up as long as sufficient
                          // gas remains to do so:
                          if returndatasize() {
                              // Ensure that sufficient gas is available to copy
                              // returndata while expanding memory where necessary. Start
                              // by computing word size of returndata & allocated memory.
                              // Round up to the nearest full word.
                              let returnDataWords := shr(
                                  OneWordShift,
                                  add(returndatasize(), ThirtyOneBytes)
                              )
                              // Note: use the free memory pointer in place of msize() to
                              // work around a Yul warning that prevents accessing msize
                              // directly when the IR pipeline is activated.
                              let msizeWords := shr(OneWordShift, memPointer)
                              // Next, compute the cost of the returndatacopy.
                              let cost := mul(CostPerWord, returnDataWords)
                              // Then, compute cost of new memory allocation.
                              if gt(returnDataWords, msizeWords) {
                                  cost := add(
                                      cost,
                                      add(
                                          mul(
                                              sub(returnDataWords, msizeWords),
                                              CostPerWord
                                          ),
                                          shr(
                                              MemoryExpansionCoefficientShift,
                                              sub(
                                                  mul(returnDataWords, returnDataWords),
                                                  mul(msizeWords, msizeWords)
                                              )
                                          )
                                      )
                                  )
                              }
                              // Finally, add a small constant and compare to gas
                              // remaining; bubble up the revert data if enough gas is
                              // still available.
                              if lt(add(cost, ExtraGasBuffer), gas()) {
                                  // Copy returndata to memory; overwrite existing memory.
                                  returndatacopy(0, 0, returndatasize())
                                  // Revert, giving memory region with copied returndata.
                                  revert(0, returndatasize())
                              }
                          }
                          // Otherwise revert with a generic error message.
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, TokenTransferGenericFailure_error_selector)
                          mstore(TokenTransferGenericFailure_error_token_ptr, token)
                          mstore(TokenTransferGenericFailure_error_from_ptr, from)
                          mstore(TokenTransferGenericFailure_error_to_ptr, to)
                          mstore(
                              TokenTransferGenericFailure_error_identifier_ptr,
                              identifier
                          )
                          mstore(TokenTransferGenericFailure_error_amount_ptr, amount)
                          // revert(abi.encodeWithSignature(
                          //     "TokenTransferGenericFailure(
                          //         address,address,address,uint256,uint256
                          //     )", token, from, to, identifier, amount
                          // ))
                          revert(
                              Generic_error_selector_offset,
                              TokenTransferGenericFailure_error_length
                          )
                      }
                      mstore(Slot0x80, slot0x80) // Restore slot 0x80.
                      mstore(Slot0xA0, slot0xA0) // Restore slot 0xA0.
                      mstore(Slot0xC0, slot0xC0) // Restore slot 0xC0.
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer ERC1155 tokens from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer and contract recipients must
               *      implement the ERC1155TokenReceiver interface to indicate that they
               *      are willing to accept the transfer. NOTE: this function is not
               *      memory-safe; it will overwrite existing memory, restore the free
               *      memory pointer to the default value, and overwrite the zero slot.
               *      This function should only be called once memory is no longer
               *      required and when uninitialized arrays are not utilized, and memory
               *      should be considered fully corrupted (aside from the existence of a
               *      default-value free memory pointer) after calling this function.
               *
               * @param batchTransfers The group of 1155 batch transfers to perform.
               */
              function _performERC1155BatchTransfers(
                  ConduitBatch1155Transfer[] calldata batchTransfers
              ) internal {
                  // Utilize assembly to perform optimized batch 1155 transfers.
                  assembly {
                      let len := batchTransfers.length
                      // Pointer to first head in the array, which is offset to the struct
                      // at each index. This gets incremented after each loop to avoid
                      // multiplying by 32 to get the offset for each element.
                      let nextElementHeadPtr := batchTransfers.offset
                      // Pointer to beginning of the head of the array. This is the
                      // reference position each offset references. It's held static to
                      // let each loop calculate the data position for an element.
                      let arrayHeadPtr := nextElementHeadPtr
                      // Write the function selector, which will be reused for each call:
                      // safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)
                      mstore(
                          ConduitBatch1155Transfer_from_offset,
                          ERC1155_safeBatchTransferFrom_signature
                      )
                      // Iterate over each batch transfer.
                      for {
                          let i := 0
                      } lt(i, len) {
                          i := add(i, 1)
                      } {
                          // Read the offset to the beginning of the element and add
                          // it to pointer to the beginning of the array head to get
                          // the absolute position of the element in calldata.
                          let elementPtr := add(
                              arrayHeadPtr,
                              calldataload(nextElementHeadPtr)
                          )
                          // Retrieve the token from calldata.
                          let token := calldataload(elementPtr)
                          // If the token has no code, revert.
                          if iszero(extcodesize(token)) {
                              // Store left-padded selector with push4, mem[28:32]
                              mstore(0, NoContract_error_selector)
                              mstore(NoContract_error_account_ptr, token)
                              // revert(abi.encodeWithSignature(
                              //     "NoContract(address)", account
                              // ))
                              revert(
                                  Generic_error_selector_offset,
                                  NoContract_error_length
                              )
                          }
                          // Get the total number of supplied ids.
                          let idsLength := calldataload(
                              add(elementPtr, ConduitBatch1155Transfer_ids_length_offset)
                          )
                          // Determine the expected offset for the amounts array.
                          let expectedAmountsOffset := add(
                              ConduitBatch1155Transfer_amounts_length_baseOffset,
                              shl(OneWordShift, idsLength)
                          )
                          // Validate struct encoding.
                          let invalidEncoding := iszero(
                              and(
                                  // ids.length == amounts.length
                                  eq(
                                      idsLength,
                                      calldataload(add(elementPtr, expectedAmountsOffset))
                                  ),
                                  and(
                                      // ids_offset == 0xa0
                                      eq(
                                          calldataload(
                                              add(
                                                  elementPtr,
                                                  ConduitBatch1155Transfer_ids_head_offset
                                              )
                                          ),
                                          ConduitBatch1155Transfer_ids_length_offset
                                      ),
                                      // amounts_offset == 0xc0 + ids.length*32
                                      eq(
                                          calldataload(
                                              add(
                                                  elementPtr,
                                                  ConduitBatchTransfer_amounts_head_offset
                                              )
                                          ),
                                          expectedAmountsOffset
                                      )
                                  )
                              )
                          )
                          // Revert with an error if the encoding is not valid.
                          if invalidEncoding {
                              // Store left-padded selector with push4, mem[28:32]
                              mstore(
                                  Invalid1155BatchTransferEncoding_ptr,
                                  Invalid1155BatchTransferEncoding_selector
                              )
                              // revert(abi.encodeWithSignature(
                              //     "Invalid1155BatchTransferEncoding()"
                              // ))
                              revert(
                                  Invalid1155BatchTransferEncoding_ptr,
                                  Invalid1155BatchTransferEncoding_length
                              )
                          }
                          // Update the offset position for the next loop
                          nextElementHeadPtr := add(nextElementHeadPtr, OneWord)
                          // Copy the first section of calldata (before dynamic values).
                          calldatacopy(
                              BatchTransfer1155Params_ptr,
                              add(elementPtr, ConduitBatch1155Transfer_from_offset),
                              ConduitBatch1155Transfer_usable_head_size
                          )
                          // Determine size of calldata required for ids and amounts. Note
                          // that the size includes both lengths as well as the data.
                          let idsAndAmountsSize := add(
                              TwoWords,
                              shl(TwoWordsShift, idsLength)
                          )
                          // Update the offset for the data array in memory.
                          mstore(
                              BatchTransfer1155Params_data_head_ptr,
                              add(
                                  BatchTransfer1155Params_ids_length_offset,
                                  idsAndAmountsSize
                              )
                          )
                          // Set the length of the data array in memory to zero.
                          mstore(
                              add(
                                  BatchTransfer1155Params_data_length_basePtr,
                                  idsAndAmountsSize
                              ),
                              0
                          )
                          // Determine the total calldata size for the call to transfer.
                          let transferDataSize := add(
                              BatchTransfer1155Params_calldata_baseSize,
                              idsAndAmountsSize
                          )
                          // Copy second section of calldata (including dynamic values).
                          calldatacopy(
                              BatchTransfer1155Params_ids_length_ptr,
                              add(elementPtr, ConduitBatch1155Transfer_ids_length_offset),
                              idsAndAmountsSize
                          )
                          // Perform the call to transfer 1155 tokens.
                          let success := call(
                              gas(),
                              token,
                              0,
                              ConduitBatch1155Transfer_from_offset, // Data portion start.
                              transferDataSize, // Location of the length of callData.
                              0,
                              0
                          )
                          // If the transfer reverted:
                          if iszero(success) {
                              // If it returned a message, bubble it up as long as
                              // sufficient gas remains to do so:
                              if returndatasize() {
                                  // Ensure that sufficient gas is available to copy
                                  // returndata while expanding memory where necessary.
                                  // Start by computing word size of returndata and
                                  // allocated memory. Round up to the nearest full word.
                                  let returnDataWords := shr(
                                      OneWordShift,
                                      add(returndatasize(), ThirtyOneBytes)
                                  )
                                  // Note: use transferDataSize in place of msize() to
                                  // work around a Yul warning that prevents accessing
                                  // msize directly when the IR pipeline is activated.
                                  // The free memory pointer is not used here because
                                  // this function does almost all memory management
                                  // manually and does not update it, and transferDataSize
                                  // should be the largest memory value used (unless a
                                  // previous batch was larger).
                                  let msizeWords := shr(OneWordShift, transferDataSize)
                                  // Next, compute the cost of the returndatacopy.
                                  let cost := mul(CostPerWord, returnDataWords)
                                  // Then, compute cost of new memory allocation.
                                  if gt(returnDataWords, msizeWords) {
                                      cost := add(
                                          cost,
                                          add(
                                              mul(
                                                  sub(returnDataWords, msizeWords),
                                                  CostPerWord
                                              ),
                                              shr(
                                                  MemoryExpansionCoefficientShift,
                                                  sub(
                                                      mul(
                                                          returnDataWords,
                                                          returnDataWords
                                                      ),
                                                      mul(msizeWords, msizeWords)
                                                  )
                                              )
                                          )
                                      )
                                  }
                                  // Finally, add a small constant and compare to gas
                                  // remaining; bubble up the revert data if enough gas is
                                  // still available.
                                  if lt(add(cost, ExtraGasBuffer), gas()) {
                                      // Copy returndata to memory; overwrite existing.
                                      returndatacopy(0, 0, returndatasize())
                                      // Revert with memory region containing returndata.
                                      revert(0, returndatasize())
                                  }
                              }
                              // Set the error signature.
                              mstore(
                                  0,
                                  ERC1155BatchTransferGenericFailure_error_signature
                              )
                              // Write the token.
                              mstore(ERC1155BatchTransferGenericFailure_token_ptr, token)
                              // Increase the offset to ids by 32.
                              mstore(
                                  BatchTransfer1155Params_ids_head_ptr,
                                  ERC1155BatchTransferGenericFailure_ids_offset
                              )
                              // Increase the offset to amounts by 32.
                              mstore(
                                  BatchTransfer1155Params_amounts_head_ptr,
                                  add(
                                      OneWord,
                                      mload(BatchTransfer1155Params_amounts_head_ptr)
                                  )
                              )
                              // Return modified region. The total size stays the same as
                              // `token` uses the same number of bytes as `data.length`.
                              revert(0, transferDataSize)
                          }
                      }
                      // Reset the free memory pointer to the default value; memory must
                      // be assumed to be dirtied and not reused from this point forward.
                      // Also note that the zero slot is not reset to zero, meaning empty
                      // arrays cannot be safely created or utilized until it is restored.
                      mstore(FreeMemoryPointerSlot, DefaultFreeMemoryPointer)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          enum ConduitItemType {
              NATIVE, // unused
              ERC20,
              ERC721,
              ERC1155
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import { ConduitItemType } from "./ConduitEnums.sol";
          /**
           * @dev A ConduitTransfer is a struct that contains the information needed for a
           *      conduit to transfer an item from one address to another.
           */
          struct ConduitTransfer {
              ConduitItemType itemType;
              address token;
              address from;
              address to;
              uint256 identifier;
              uint256 amount;
          }
          /**
           * @dev A ConduitBatch1155Transfer is a struct that contains the information
           *      needed for a conduit to transfer a batch of ERC-1155 tokens from one
           *      address to another.
           */
          struct ConduitBatch1155Transfer {
              address token;
              address from;
              address to;
              uint256[] ids;
              uint256[] amounts;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { OrderParameters } from "./ConsiderationStructs.sol";
          import { GettersAndDerivers } from "./GettersAndDerivers.sol";
          import {
              TokenTransferrerErrors
          } from "../interfaces/TokenTransferrerErrors.sol";
          import { CounterManager } from "./CounterManager.sol";
          import {
              AdditionalRecipient_size_shift,
              AddressDirtyUpperBitThreshold,
              BasicOrder_additionalRecipients_head_cdPtr,
              BasicOrder_additionalRecipients_head_ptr,
              BasicOrder_additionalRecipients_length_cdPtr,
              BasicOrder_basicOrderType_cdPtr,
              BasicOrder_basicOrderType_range,
              BasicOrder_considerationToken_cdPtr,
              BasicOrder_offerer_cdPtr,
              BasicOrder_offerToken_cdPtr,
              BasicOrder_parameters_cdPtr,
              BasicOrder_parameters_ptr,
              BasicOrder_signature_cdPtr,
              BasicOrder_signature_ptr,
              BasicOrder_zone_cdPtr
          } from "./ConsiderationConstants.sol";
          import {
              Error_selector_offset,
              MissingItemAmount_error_length,
              MissingItemAmount_error_selector
          } from "./ConsiderationErrorConstants.sol";
          import {
              _revertInvalidBasicOrderParameterEncoding,
              _revertMissingOriginalConsiderationItems
          } from "./ConsiderationErrors.sol";
          /**
           * @title Assertions
           * @author 0age
           * @notice Assertions contains logic for making various assertions that do not
           *         fit neatly within a dedicated semantic scope.
           */
          contract Assertions is
              GettersAndDerivers,
              CounterManager,
              TokenTransferrerErrors
          {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(
                  address conduitController
              ) GettersAndDerivers(conduitController) {}
              /**
               * @dev Internal view function to ensure that the supplied consideration
               *      array length on a given set of order parameters is not less than the
               *      original consideration array length for that order and to retrieve
               *      the current counter for a given order's offerer and zone and use it
               *      to derive the order hash.
               *
               * @param orderParameters The parameters of the order to hash.
               *
               * @return The hash.
               */
              function _assertConsiderationLengthAndGetOrderHash(
                  OrderParameters memory orderParameters
              ) internal view returns (bytes32) {
                  // Ensure supplied consideration array length is not less than original.
                  _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength(
                      orderParameters.consideration.length,
                      orderParameters.totalOriginalConsiderationItems
                  );
                  // Derive and return order hash using current counter for the offerer.
                  return
                      _deriveOrderHash(
                          orderParameters,
                          _getCounter(orderParameters.offerer)
                      );
              }
              /**
               * @dev Internal pure function to ensure that the supplied consideration
               *      array length for an order to be fulfilled is not less than the
               *      original consideration array length for that order.
               *
               * @param suppliedConsiderationItemTotal The number of consideration items
               *                                       supplied when fulfilling the order.
               * @param originalConsiderationItemTotal The number of consideration items
               *                                       supplied on initial order creation.
               */
              function _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength(
                  uint256 suppliedConsiderationItemTotal,
                  uint256 originalConsiderationItemTotal
              ) internal pure {
                  // Ensure supplied consideration array length is not less than original.
                  if (suppliedConsiderationItemTotal < originalConsiderationItemTotal) {
                      _revertMissingOriginalConsiderationItems();
                  }
              }
              /**
               * @dev Internal pure function to ensure that a given item amount is not
               *      zero.
               *
               * @param amount The amount to check.
               */
              function _assertNonZeroAmount(uint256 amount) internal pure {
                  assembly {
                      if iszero(amount) {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, MissingItemAmount_error_selector)
                          // revert(abi.encodeWithSignature("MissingItemAmount()"))
                          revert(Error_selector_offset, MissingItemAmount_error_length)
                      }
                  }
              }
              /**
               * @dev Internal pure function to validate calldata offsets for dynamic
               *      types in BasicOrderParameters and other parameters. This ensures
               *      that functions using the calldata object normally will be using the
               *      same data as the assembly functions and that values that are bound
               *      to a given range are within that range. Note that no parameters are
               *      supplied as all basic order functions use the same calldata
               *      encoding.
               */
              function _assertValidBasicOrderParameters() internal pure {
                  // Declare a boolean designating basic order parameter offset validity.
                  bool validOffsets;
                  // Utilize assembly in order to read offset data directly from calldata.
                  assembly {
                      /*
                       * Checks:
                       * 1. Order parameters struct offset == 0x20
                       * 2. Additional recipients arr offset == 0x240
                       * 3. Signature offset == 0x260 + (recipients.length * 0x40)
                       * 4. BasicOrderType between 0 and 23 (i.e. < 24)
                       * 5. Offerer, zone, offer token, and consideration token have no
                       *    upper dirty bits — each argument is type(uint160).max or less
                       */
                      validOffsets := and(
                          and(
                              and(
                                  // Order parameters at cd 0x04 must have offset of 0x20.
                                  eq(
                                      calldataload(BasicOrder_parameters_cdPtr),
                                      BasicOrder_parameters_ptr
                                  ),
                                  // Additional recipients (cd 0x224) arr offset == 0x240.
                                  eq(
                                      calldataload(
                                          BasicOrder_additionalRecipients_head_cdPtr
                                      ),
                                      BasicOrder_additionalRecipients_head_ptr
                                  )
                              ),
                              // Signature offset == 0x260 + (recipients.length * 0x40).
                              eq(
                                  // Load signature offset from calldata 0x244.
                                  calldataload(BasicOrder_signature_cdPtr),
                                  // Expected offset is start of recipients + len * 64.
                                  add(
                                      BasicOrder_signature_ptr,
                                      shl(
                                          // Each additional recipient has length of 0x40.
                                          AdditionalRecipient_size_shift,
                                          // Additional recipients length at cd 0x264.
                                          calldataload(
                                              BasicOrder_additionalRecipients_length_cdPtr
                                          )
                                      )
                                  )
                              )
                          ),
                          and(
                              // Ensure BasicOrderType parameter is less than 0x18.
                              lt(
                                  // BasicOrderType parameter at calldata offset 0x124.
                                  calldataload(BasicOrder_basicOrderType_cdPtr),
                                  // Value should be less than 24.
                                  BasicOrder_basicOrderType_range
                              ),
                              // Ensure no dirty upper bits are present on offerer, zone,
                              // offer token, or consideration token.
                              lt(
                                  or(
                                      or(
                                          // Offerer parameter at calldata offset 0x84.
                                          calldataload(BasicOrder_offerer_cdPtr),
                                          // Zone parameter at calldata offset 0xa4.
                                          calldataload(BasicOrder_zone_cdPtr)
                                      ),
                                      or(
                                          // Offer token parameter at cd offset 0xc4.
                                          calldataload(BasicOrder_offerToken_cdPtr),
                                          // Consideration token parameter at offset 0x24.
                                          calldataload(
                                              BasicOrder_considerationToken_cdPtr
                                          )
                                      )
                                  ),
                                  AddressDirtyUpperBitThreshold
                              )
                          )
                      )
                  }
                  // Revert with an error if basic order parameter offsets are invalid.
                  if (!validOffsets) {
                      _revertInvalidBasicOrderParameterEncoding();
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              SignatureVerificationErrors
          } from "../interfaces/SignatureVerificationErrors.sol";
          import { LowLevelHelpers } from "./LowLevelHelpers.sol";
          import {
              ECDSA_MaxLength,
              ECDSA_signature_s_offset,
              ECDSA_signature_v_offset,
              ECDSA_twentySeventhAndTwentyEighthBytesSet,
              Ecrecover_args_size,
              Ecrecover_precompile,
              EIP1271_isValidSignature_calldata_baseLength,
              EIP1271_isValidSignature_digest_negativeOffset,
              EIP1271_isValidSignature_selector_negativeOffset,
              EIP1271_isValidSignature_selector,
              EIP1271_isValidSignature_signature_head_offset,
              EIP2098_allButHighestBitMask,
              MaxUint8,
              OneWord,
              Signature_lower_v
          } from "./ConsiderationConstants.sol";
          import {
              BadContractSignature_error_length,
              BadContractSignature_error_selector,
              BadSignatureV_error_length,
              BadSignatureV_error_selector,
              BadSignatureV_error_v_ptr,
              Error_selector_offset,
              InvalidSignature_error_length,
              InvalidSignature_error_selector,
              InvalidSigner_error_length,
              InvalidSigner_error_selector
          } from "./ConsiderationErrorConstants.sol";
          /**
           * @title SignatureVerification
           * @author 0age
           * @notice SignatureVerification contains logic for verifying signatures.
           */
          contract SignatureVerification is SignatureVerificationErrors, LowLevelHelpers {
              /**
               * @dev Internal view function to verify the signature of an order. An
               *      ERC-1271 fallback will be attempted if either the signature length
               *      is not 64 or 65 bytes or if the recovered signer does not match the
               *      supplied signer.
               *
               * @param signer                  The signer for the order.
               * @param digest                  The digest to verify signature against.
               * @param originalDigest          The original digest to verify signature
               *                                against.
               * @param originalSignatureLength The original signature length.
               * @param signature               A signature from the signer indicating
               *                                that the order has been approved.
               */
              function _assertValidSignature(
                  address signer,
                  bytes32 digest,
                  bytes32 originalDigest,
                  uint256 originalSignatureLength,
                  bytes memory signature
              ) internal view {
                  // Declare value for ecrecover equality or 1271 call success status.
                  bool success;
                  // Utilize assembly to perform optimized signature verification check.
                  assembly {
                      // Ensure that first word of scratch space is empty.
                      mstore(0, 0)
                      // Get the length of the signature.
                      let signatureLength := mload(signature)
                      // Get the pointer to the value preceding the signature length.
                      // This will be used for temporary memory overrides - either the
                      // signature head for isValidSignature or the digest for ecrecover.
                      let wordBeforeSignaturePtr := sub(signature, OneWord)
                      // Cache the current value behind the signature to restore it later.
                      let cachedWordBeforeSignature := mload(wordBeforeSignaturePtr)
                      // Declare lenDiff + recoveredSigner scope to manage stack pressure.
                      {
                          // Take the difference between the max ECDSA signature length
                          // and the actual signature length. Overflow desired for any
                          // values > 65. If the diff is not 0 or 1, it is not a valid
                          // ECDSA signature - move on to EIP1271 check.
                          let lenDiff := sub(ECDSA_MaxLength, signatureLength)
                          // Declare variable for recovered signer.
                          let recoveredSigner
                          // If diff is 0 or 1, it may be an ECDSA signature.
                          // Try to recover signer.
                          if iszero(gt(lenDiff, 1)) {
                              // Read the signature `s` value.
                              let originalSignatureS := mload(
                                  add(signature, ECDSA_signature_s_offset)
                              )
                              // Read the first byte of the word after `s`. If the
                              // signature is 65 bytes, this will be the real `v` value.
                              // If not, it will need to be modified - doing it this way
                              // saves an extra condition.
                              let v := byte(
                                  0,
                                  mload(add(signature, ECDSA_signature_v_offset))
                              )
                              // If lenDiff is 1, parse 64-byte signature as ECDSA.
                              if lenDiff {
                                  // Extract yParity from highest bit of vs and add 27 to
                                  // get v.
                                  v := add(
                                      shr(MaxUint8, originalSignatureS),
                                      Signature_lower_v
                                  )
                                  // Extract canonical s from vs, all but the highest bit.
                                  // Temporarily overwrite the original `s` value in the
                                  // signature.
                                  mstore(
                                      add(signature, ECDSA_signature_s_offset),
                                      and(
                                          originalSignatureS,
                                          EIP2098_allButHighestBitMask
                                      )
                                  )
                              }
                              // Temporarily overwrite the signature length with `v` to
                              // conform to the expected input for ecrecover.
                              mstore(signature, v)
                              // Temporarily overwrite the word before the length with
                              // `digest` to conform to the expected input for ecrecover.
                              mstore(wordBeforeSignaturePtr, digest)
                              // Attempt to recover the signer for the given signature. Do
                              // not check the call status as ecrecover will return a null
                              // address if the signature is invalid.
                              pop(
                                  staticcall(
                                      gas(),
                                      Ecrecover_precompile, // Call ecrecover precompile.
                                      wordBeforeSignaturePtr, // Use data memory location.
                                      Ecrecover_args_size, // Size of digest, v, r, and s.
                                      0, // Write result to scratch space.
                                      OneWord // Provide size of returned result.
                                  )
                              )
                              // Restore cached word before signature.
                              mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
                              // Restore cached signature length.
                              mstore(signature, signatureLength)
                              // Restore cached signature `s` value.
                              mstore(
                                  add(signature, ECDSA_signature_s_offset),
                                  originalSignatureS
                              )
                              // Read the recovered signer from the buffer given as return
                              // space for ecrecover.
                              recoveredSigner := mload(0)
                          }
                          // Set success to true if the signature provided was a valid
                          // ECDSA signature and the signer is not the null address. Use
                          // gt instead of direct as success is used outside of assembly.
                          success := and(eq(signer, recoveredSigner), gt(signer, 0))
                      }
                      // If the signature was not verified with ecrecover, try EIP1271.
                      if iszero(success) {
                          // Reset the original signature length.
                          mstore(signature, originalSignatureLength)
                          // Temporarily overwrite the word before the signature length
                          // and use it as the head of the signature input to
                          // `isValidSignature`, which has a value of 64.
                          mstore(
                              wordBeforeSignaturePtr,
                              EIP1271_isValidSignature_signature_head_offset
                          )
                          // Get pointer to use for the selector of `isValidSignature`.
                          let selectorPtr := sub(
                              signature,
                              EIP1271_isValidSignature_selector_negativeOffset
                          )
                          // Cache the value currently stored at the selector pointer.
                          let cachedWordOverwrittenBySelector := mload(selectorPtr)
                          // Cache the value currently stored at the digest pointer.
                          let cachedWordOverwrittenByDigest := mload(
                              sub(
                                  signature,
                                  EIP1271_isValidSignature_digest_negativeOffset
                              )
                          )
                          // Write the selector first, since it overlaps the digest.
                          mstore(selectorPtr, EIP1271_isValidSignature_selector)
                          // Next, write the original digest.
                          mstore(
                              sub(
                                  signature,
                                  EIP1271_isValidSignature_digest_negativeOffset
                              ),
                              originalDigest
                          )
                          // Call signer with `isValidSignature` to validate signature.
                          success := staticcall(
                              gas(),
                              signer,
                              selectorPtr,
                              add(
                                  originalSignatureLength,
                                  EIP1271_isValidSignature_calldata_baseLength
                              ),
                              0,
                              OneWord
                          )
                          // Determine if the signature is valid on successful calls.
                          if success {
                              // If first word of scratch space does not contain EIP-1271
                              // signature selector, revert.
                              if iszero(eq(mload(0), EIP1271_isValidSignature_selector)) {
                                  // Revert with bad 1271 signature if signer has code.
                                  if extcodesize(signer) {
                                      // Bad contract signature.
                                      // Store left-padded selector with push4, mem[28:32]
                                      mstore(0, BadContractSignature_error_selector)
                                      // revert(abi.encodeWithSignature(
                                      //     "BadContractSignature()"
                                      // ))
                                      revert(
                                          Error_selector_offset,
                                          BadContractSignature_error_length
                                      )
                                  }
                                  // Check if signature length was invalid.
                                  if gt(sub(ECDSA_MaxLength, signatureLength), 1) {
                                      // Revert with generic invalid signature error.
                                      // Store left-padded selector with push4, mem[28:32]
                                      mstore(0, InvalidSignature_error_selector)
                                      // revert(abi.encodeWithSignature(
                                      //     "InvalidSignature()"
                                      // ))
                                      revert(
                                          Error_selector_offset,
                                          InvalidSignature_error_length
                                      )
                                  }
                                  // Check if v was invalid.
                                  if and(
                                      eq(signatureLength, ECDSA_MaxLength),
                                      iszero(
                                          byte(
                                              byte(
                                                  0,
                                                  mload(
                                                      add(
                                                          signature,
                                                          ECDSA_signature_v_offset
                                                      )
                                                  )
                                              ),
                                              ECDSA_twentySeventhAndTwentyEighthBytesSet
                                          )
                                      )
                                  ) {
                                      // Revert with invalid v value.
                                      // Store left-padded selector with push4, mem[28:32]
                                      mstore(0, BadSignatureV_error_selector)
                                      mstore(
                                          BadSignatureV_error_v_ptr,
                                          byte(
                                              0,
                                              mload(
                                                  add(signature, ECDSA_signature_v_offset)
                                              )
                                          )
                                      )
                                      // revert(abi.encodeWithSignature(
                                      //     "BadSignatureV(uint8)", v
                                      // ))
                                      revert(
                                          Error_selector_offset,
                                          BadSignatureV_error_length
                                      )
                                  }
                                  // Revert with generic invalid signer error message.
                                  // Store left-padded selector with push4, mem[28:32]
                                  mstore(0, InvalidSigner_error_selector)
                                  // revert(abi.encodeWithSignature("InvalidSigner()"))
                                  revert(
                                      Error_selector_offset,
                                      InvalidSigner_error_length
                                  )
                              }
                          }
                          // Restore the cached values overwritten by selector, digest and
                          // signature head.
                          mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
                          mstore(selectorPtr, cachedWordOverwrittenBySelector)
                          mstore(
                              sub(
                                  signature,
                                  EIP1271_isValidSignature_digest_negativeOffset
                              ),
                              cachedWordOverwrittenByDigest
                          )
                      }
                  }
                  // If the call failed...
                  if (!success) {
                      // Revert and pass reason along if one was returned.
                      _revertWithReasonIfOneIsReturned();
                      // Otherwise, revert with error indicating bad contract signature.
                      assembly {
                          // Store left-padded selector with push4, mem[28:32] = selector
                          mstore(0, BadContractSignature_error_selector)
                          // revert(abi.encodeWithSignature("BadContractSignature()"))
                          revert(Error_selector_offset, BadContractSignature_error_length)
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ConsiderationEventsAndErrors
          } from "../interfaces/ConsiderationEventsAndErrors.sol";
          import { ReentrancyGuard } from "./ReentrancyGuard.sol";
          import {
              Counter_blockhash_shift,
              OneWord,
              TwoWords
          } from "./ConsiderationConstants.sol";
          /**
           * @title CounterManager
           * @author 0age
           * @notice CounterManager contains a storage mapping and related functionality
           *         for retrieving and incrementing a per-offerer counter.
           */
          contract CounterManager is ConsiderationEventsAndErrors, ReentrancyGuard {
              // Only orders signed using an offerer's current counter are fulfillable.
              mapping(address => uint256) private _counters;
              /**
               * @dev Internal function to cancel all orders from a given offerer in bulk
               *      by incrementing a counter by a large, quasi-random interval. Note
               *      that only the offerer may increment the counter. Note that the
               *      counter is incremented by a large, quasi-random interval, which
               *      makes it infeasible to "activate" signed orders by incrementing the
               *      counter.  This activation functionality can be achieved instead with
               *      restricted orders or contract orders.
               *
               * @return newCounter The new counter.
               */
              function _incrementCounter() internal returns (uint256 newCounter) {
                  // Ensure that the reentrancy guard is not currently set.
                  _assertNonReentrant();
                  // Utilize assembly to access counters storage mapping directly. Skip
                  // overflow check as counter cannot be incremented that far.
                  assembly {
                      // Use second half of previous block hash as a quasi-random number.
                      let quasiRandomNumber := shr(
                          Counter_blockhash_shift,
                          blockhash(sub(number(), 1))
                      )
                      // Write the caller to scratch space.
                      mstore(0, caller())
                      // Write the storage slot for _counters to scratch space.
                      mstore(OneWord, _counters.slot)
                      // Derive the storage pointer for the counter value.
                      let storagePointer := keccak256(0, TwoWords)
                      // Derive new counter value using random number and original value.
                      newCounter := add(quasiRandomNumber, sload(storagePointer))
                      // Store the updated counter value.
                      sstore(storagePointer, newCounter)
                  }
                  // Emit an event containing the new counter.
                  emit CounterIncremented(newCounter, msg.sender);
              }
              /**
               * @dev Internal view function to retrieve the current counter for a given
               *      offerer.
               *
               * @param offerer The offerer in question.
               *
               * @return currentCounter The current counter.
               */
              function _getCounter(
                  address offerer
              ) internal view returns (uint256 currentCounter) {
                  // Return the counter for the supplied offerer.
                  currentCounter = _counters[offerer];
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title TokenTransferrerErrors
           */
          interface TokenTransferrerErrors {
              /**
               * @dev Revert with an error when an ERC721 transfer with amount other than
               *      one is attempted.
               *
               * @param amount The amount of the ERC721 tokens to transfer.
               */
              error InvalidERC721TransferAmount(uint256 amount);
              /**
               * @dev Revert with an error when attempting to fulfill an order where an
               *      item has an amount of zero.
               */
              error MissingItemAmount();
              /**
               * @dev Revert with an error when attempting to fulfill an order where an
               *      item has unused parameters. This includes both the token and the
               *      identifier parameters for native transfers as well as the identifier
               *      parameter for ERC20 transfers. Note that the conduit does not
               *      perform this check, leaving it up to the calling channel to enforce
               *      when desired.
               */
              error UnusedItemParameters();
              /**
               * @dev Revert with an error when an ERC20, ERC721, or ERC1155 token
               *      transfer reverts.
               *
               * @param token      The token for which the transfer was attempted.
               * @param from       The source of the attempted transfer.
               * @param to         The recipient of the attempted transfer.
               * @param identifier The identifier for the attempted transfer.
               * @param amount     The amount for the attempted transfer.
               */
              error TokenTransferGenericFailure(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount
              );
              /**
               * @dev Revert with an error when a batch ERC1155 token transfer reverts.
               *
               * @param token       The token for which the transfer was attempted.
               * @param from        The source of the attempted transfer.
               * @param to          The recipient of the attempted transfer.
               * @param identifiers The identifiers for the attempted transfer.
               * @param amounts     The amounts for the attempted transfer.
               */
              error ERC1155BatchTransferGenericFailure(
                  address token,
                  address from,
                  address to,
                  uint256[] identifiers,
                  uint256[] amounts
              );
              /**
               * @dev Revert with an error when an ERC20 token transfer returns a falsey
               *      value.
               *
               * @param token      The token for which the ERC20 transfer was attempted.
               * @param from       The source of the attempted ERC20 transfer.
               * @param to         The recipient of the attempted ERC20 transfer.
               * @param amount     The amount for the attempted ERC20 transfer.
               */
              error BadReturnValueFromERC20OnTransfer(
                  address token,
                  address from,
                  address to,
                  uint256 amount
              );
              /**
               * @dev Revert with an error when an account being called as an assumed
               *      contract does not have code and returns no data.
               *
               * @param account The account that should contain code.
               */
              error NoContract(address account);
              /**
               * @dev Revert with an error when attempting to execute an 1155 batch
               *      transfer using calldata not produced by default ABI encoding or with
               *      different lengths for ids and amounts arrays.
               */
              error Invalid1155BatchTransferEncoding();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { OrderParameters } from "./ConsiderationStructs.sol";
          import { ConsiderationBase } from "./ConsiderationBase.sol";
          import {
              Create2AddressDerivation_length,
              Create2AddressDerivation_ptr,
              EIP_712_PREFIX,
              EIP712_ConsiderationItem_size,
              EIP712_DigestPayload_size,
              EIP712_DomainSeparator_offset,
              EIP712_OfferItem_size,
              EIP712_Order_size,
              EIP712_OrderHash_offset,
              FreeMemoryPointerSlot,
              information_conduitController_offset,
              information_domainSeparator_offset,
              information_length,
              information_version_cd_offset,
              information_version_offset,
              information_versionLengthPtr,
              information_versionWithLength,
              MaskOverByteTwelve,
              MaskOverLastTwentyBytes,
              OneWord,
              OneWordShift,
              OrderParameters_consideration_head_offset,
              OrderParameters_counter_offset,
              OrderParameters_offer_head_offset,
              TwoWords
          } from "./ConsiderationConstants.sol";
          /**
           * @title GettersAndDerivers
           * @author 0age
           * @notice ConsiderationInternal contains pure and internal view functions
           *         related to getting or deriving various values.
           */
          contract GettersAndDerivers is ConsiderationBase {
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(
                  address conduitController
              ) ConsiderationBase(conduitController) {}
              /**
               * @dev Internal view function to derive the order hash for a given order.
               *      Note that only the original consideration items are included in the
               *      order hash, as additional consideration items may be supplied by the
               *      caller.
               *
               * @param orderParameters The parameters of the order to hash.
               * @param counter         The counter of the order to hash.
               *
               * @return orderHash The hash.
               */
              function _deriveOrderHash(
                  OrderParameters memory orderParameters,
                  uint256 counter
              ) internal view returns (bytes32 orderHash) {
                  // Get length of original consideration array and place it on the stack.
                  uint256 originalConsiderationLength = (
                      orderParameters.totalOriginalConsiderationItems
                  );
                  /*
                   * Memory layout for an array of structs (dynamic or not) is similar
                   * to ABI encoding of dynamic types, with a head segment followed by
                   * a data segment. The main difference is that the head of an element
                   * is a memory pointer rather than an offset.
                   */
                  // Declare a variable for the derived hash of the offer array.
                  bytes32 offerHash;
                  // Read offer item EIP-712 typehash from runtime code & place on stack.
                  bytes32 typeHash = _OFFER_ITEM_TYPEHASH;
                  // Utilize assembly so that memory regions can be reused across hashes.
                  assembly {
                      // Retrieve the free memory pointer and place on the stack.
                      let hashArrPtr := mload(FreeMemoryPointerSlot)
                      // Get the pointer to the offers array.
                      let offerArrPtr := mload(
                          add(orderParameters, OrderParameters_offer_head_offset)
                      )
                      // Load the length.
                      let offerLength := mload(offerArrPtr)
                      // Set the pointer to the first offer's head.
                      offerArrPtr := add(offerArrPtr, OneWord)
                      // Iterate over the offer items.
                      for { let i := 0 } lt(i, offerLength) {
                          i := add(i, 1)
                      } {
                          // Read the pointer to the offer data and subtract one word
                          // to get typeHash pointer.
                          let ptr := sub(mload(offerArrPtr), OneWord)
                          // Read the current value before the offer data.
                          let value := mload(ptr)
                          // Write the type hash to the previous word.
                          mstore(ptr, typeHash)
                          // Take the EIP712 hash and store it in the hash array.
                          mstore(hashArrPtr, keccak256(ptr, EIP712_OfferItem_size))
                          // Restore the previous word.
                          mstore(ptr, value)
                          // Increment the array pointers by one word.
                          offerArrPtr := add(offerArrPtr, OneWord)
                          hashArrPtr := add(hashArrPtr, OneWord)
                      }
                      // Derive the offer hash using the hashes of each item.
                      offerHash := keccak256(
                          mload(FreeMemoryPointerSlot),
                          shl(OneWordShift, offerLength)
                      )
                  }
                  // Declare a variable for the derived hash of the consideration array.
                  bytes32 considerationHash;
                  // Read consideration item typehash from runtime code & place on stack.
                  typeHash = _CONSIDERATION_ITEM_TYPEHASH;
                  // Utilize assembly so that memory regions can be reused across hashes.
                  assembly {
                      // Retrieve the free memory pointer and place on the stack.
                      let hashArrPtr := mload(FreeMemoryPointerSlot)
                      // Get the pointer to the consideration array.
                      let considerationArrPtr := add(
                          mload(
                              add(
                                  orderParameters,
                                  OrderParameters_consideration_head_offset
                              )
                          ),
                          OneWord
                      )
                      // Iterate over the consideration items (not including tips).
                      for { let i := 0 } lt(i, originalConsiderationLength) {
                          i := add(i, 1)
                      } {
                          // Read the pointer to the consideration data and subtract one
                          // word to get typeHash pointer.
                          let ptr := sub(mload(considerationArrPtr), OneWord)
                          // Read the current value before the consideration data.
                          let value := mload(ptr)
                          // Write the type hash to the previous word.
                          mstore(ptr, typeHash)
                          // Take the EIP712 hash and store it in the hash array.
                          mstore(
                              hashArrPtr,
                              keccak256(ptr, EIP712_ConsiderationItem_size)
                          )
                          // Restore the previous word.
                          mstore(ptr, value)
                          // Increment the array pointers by one word.
                          considerationArrPtr := add(considerationArrPtr, OneWord)
                          hashArrPtr := add(hashArrPtr, OneWord)
                      }
                      // Derive the consideration hash using the hashes of each item.
                      considerationHash := keccak256(
                          mload(FreeMemoryPointerSlot),
                          shl(OneWordShift, originalConsiderationLength)
                      )
                  }
                  // Read order item EIP-712 typehash from runtime code & place on stack.
                  typeHash = _ORDER_TYPEHASH;
                  // Utilize assembly to access derived hashes & other arguments directly.
                  assembly {
                      // Retrieve pointer to the region located just behind parameters.
                      let typeHashPtr := sub(orderParameters, OneWord)
                      // Store the value at that pointer location to restore later.
                      let previousValue := mload(typeHashPtr)
                      // Store the order item EIP-712 typehash at the typehash location.
                      mstore(typeHashPtr, typeHash)
                      // Retrieve the pointer for the offer array head.
                      let offerHeadPtr := add(
                          orderParameters,
                          OrderParameters_offer_head_offset
                      )
                      // Retrieve the data pointer referenced by the offer head.
                      let offerDataPtr := mload(offerHeadPtr)
                      // Store the offer hash at the retrieved memory location.
                      mstore(offerHeadPtr, offerHash)
                      // Retrieve the pointer for the consideration array head.
                      let considerationHeadPtr := add(
                          orderParameters,
                          OrderParameters_consideration_head_offset
                      )
                      // Retrieve the data pointer referenced by the consideration head.
                      let considerationDataPtr := mload(considerationHeadPtr)
                      // Store the consideration hash at the retrieved memory location.
                      mstore(considerationHeadPtr, considerationHash)
                      // Retrieve the pointer for the counter.
                      let counterPtr := add(
                          orderParameters,
                          OrderParameters_counter_offset
                      )
                      // Store the counter at the retrieved memory location.
                      mstore(counterPtr, counter)
                      // Derive the order hash using the full range of order parameters.
                      orderHash := keccak256(typeHashPtr, EIP712_Order_size)
                      // Restore the value previously held at typehash pointer location.
                      mstore(typeHashPtr, previousValue)
                      // Restore offer data pointer at the offer head pointer location.
                      mstore(offerHeadPtr, offerDataPtr)
                      // Restore consideration data pointer at the consideration head ptr.
                      mstore(considerationHeadPtr, considerationDataPtr)
                      // Restore consideration item length at the counter pointer.
                      mstore(counterPtr, originalConsiderationLength)
                  }
              }
              /**
               * @dev Internal view function to derive the address of a given conduit
               *      using a corresponding conduit key.
               *
               * @param conduitKey A bytes32 value indicating what corresponding conduit,
               *                   if any, to source token approvals from. This value is
               *                   the "salt" parameter supplied by the deployer (i.e. the
               *                   conduit controller) when deploying the given conduit.
               *
               * @return conduit The address of the conduit associated with the given
               *                 conduit key.
               */
              function _deriveConduit(
                  bytes32 conduitKey
              ) internal view returns (address conduit) {
                  // Read conduit controller address from runtime and place on the stack.
                  address conduitController = address(_CONDUIT_CONTROLLER);
                  // Read conduit creation code hash from runtime and place on the stack.
                  bytes32 conduitCreationCodeHash = _CONDUIT_CREATION_CODE_HASH;
                  // Leverage scratch space to perform an efficient hash.
                  assembly {
                      // Retrieve the free memory pointer; it will be replaced afterwards.
                      let freeMemoryPointer := mload(FreeMemoryPointerSlot)
                      // Place the control character and the conduit controller in scratch
                      // space; note that eleven bytes at the beginning are left unused.
                      mstore(0, or(MaskOverByteTwelve, conduitController))
                      // Place the conduit key in the next region of scratch space.
                      mstore(OneWord, conduitKey)
                      // Place conduit creation code hash in free memory pointer location.
                      mstore(TwoWords, conduitCreationCodeHash)
                      // Derive conduit by hashing and applying a mask over last 20 bytes.
                      conduit := and(
                          // Hash the relevant region.
                          keccak256(
                              // The region starts at memory pointer 11.
                              Create2AddressDerivation_ptr,
                              // The region is 85 bytes long (1 + 20 + 32 + 32).
                              Create2AddressDerivation_length
                          ),
                          // The address equals the last twenty bytes of the hash.
                          MaskOverLastTwentyBytes
                      )
                      // Restore the free memory pointer.
                      mstore(FreeMemoryPointerSlot, freeMemoryPointer)
                  }
              }
              /**
               * @dev Internal view function to get the EIP-712 domain separator. If the
               *      chainId matches the chainId set on deployment, the cached domain
               *      separator will be returned; otherwise, it will be derived from
               *      scratch.
               *
               * @return The domain separator.
               */
              function _domainSeparator() internal view returns (bytes32) {
                  return block.chainid == _CHAIN_ID
                      ? _DOMAIN_SEPARATOR
                      : _deriveDomainSeparator();
              }
              /**
               * @dev Internal view function to retrieve configuration information for
               *      this contract.
               *
               * @return The contract version.
               * @return The domain separator for this contract.
               * @return The conduit Controller set for this contract.
               */
              function _information()
                  internal
                  view
                  returns (
                      string memory /* version */,
                      bytes32 /* domainSeparator */,
                      address /* conduitController */
                  )
              {
                  // Derive the domain separator.
                  bytes32 domainSeparator = _domainSeparator();
                  // Declare variable as immutables cannot be accessed within assembly.
                  address conduitController = address(_CONDUIT_CONTROLLER);
                  // Return the version, domain separator, and conduit controller.
                  assembly {
                      mstore(information_version_offset, information_version_cd_offset)
                      mstore(information_domainSeparator_offset, domainSeparator)
                      mstore(information_conduitController_offset, conduitController)
                      mstore(information_versionLengthPtr, information_versionWithLength)
                      return(information_version_offset, information_length)
                  }
              }
              /**
               * @dev Internal pure function to efficiently derive an digest to sign for
               *      an order in accordance with EIP-712.
               *
               * @param domainSeparator The domain separator.
               * @param orderHash       The order hash.
               *
               * @return value The hash.
               */
              function _deriveEIP712Digest(
                  bytes32 domainSeparator,
                  bytes32 orderHash
              ) internal pure returns (bytes32 value) {
                  // Leverage scratch space to perform an efficient hash.
                  assembly {
                      // Place the EIP-712 prefix at the start of scratch space.
                      mstore(0, EIP_712_PREFIX)
                      // Place the domain separator in the next region of scratch space.
                      mstore(EIP712_DomainSeparator_offset, domainSeparator)
                      // Place the order hash in scratch space, spilling into the first
                      // two bytes of the free memory pointer — this should never be set
                      // as memory cannot be expanded to that size, and will be zeroed out
                      // after the hash is performed.
                      mstore(EIP712_OrderHash_offset, orderHash)
                      // Hash the relevant region (65 bytes).
                      value := keccak256(0, EIP712_DigestPayload_size)
                      // Clear out the dirtied bits in the memory pointer.
                      mstore(EIP712_OrderHash_offset, 0)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {
              OrderParameters,
              ReceivedItem,
              SpentItem
          } from "../lib/ConsiderationStructs.sol";
          /**
           * @title ConsiderationEventsAndErrors
           * @author 0age
           * @notice ConsiderationEventsAndErrors contains all events and errors.
           */
          interface ConsiderationEventsAndErrors {
              /**
               * @dev Emit an event whenever an order is successfully fulfilled.
               *
               * @param orderHash     The hash of the fulfilled order.
               * @param offerer       The offerer of the fulfilled order.
               * @param zone          The zone of the fulfilled order.
               * @param recipient     The recipient of each spent item on the fulfilled
               *                      order, or the null address if there is no specific
               *                      fulfiller (i.e. the order is part of a group of
               *                      orders). Defaults to the caller unless explicitly
               *                      specified otherwise by the fulfiller.
               * @param offer         The offer items spent as part of the order.
               * @param consideration The consideration items received as part of the
               *                      order along with the recipients of each item.
               */
              event OrderFulfilled(
                  bytes32 orderHash,
                  address indexed offerer,
                  address indexed zone,
                  address recipient,
                  SpentItem[] offer,
                  ReceivedItem[] consideration
              );
              /**
               * @dev Emit an event whenever an order is successfully cancelled.
               *
               * @param orderHash The hash of the cancelled order.
               * @param offerer   The offerer of the cancelled order.
               * @param zone      The zone of the cancelled order.
               */
              event OrderCancelled(
                  bytes32 orderHash,
                  address indexed offerer,
                  address indexed zone
              );
              /**
               * @dev Emit an event whenever an order is explicitly validated. Note that
               *      this event will not be emitted on partial fills even though they do
               *      validate the order as part of partial fulfillment.
               *
               * @param orderHash        The hash of the validated order.
               * @param orderParameters  The parameters of the validated order.
               */
              event OrderValidated(bytes32 orderHash, OrderParameters orderParameters);
              /**
               * @dev Emit an event whenever one or more orders are matched using either
               *      matchOrders or matchAdvancedOrders.
               *
               * @param orderHashes The order hashes of the matched orders.
               */
              event OrdersMatched(bytes32[] orderHashes);
              /**
               * @dev Emit an event whenever a counter for a given offerer is incremented.
               *
               * @param newCounter The new counter for the offerer.
               * @param offerer    The offerer in question.
               */
              event CounterIncremented(uint256 newCounter, address indexed offerer);
              /**
               * @dev Revert with an error when attempting to fill an order that has
               *      already been fully filled.
               *
               * @param orderHash The order hash on which a fill was attempted.
               */
              error OrderAlreadyFilled(bytes32 orderHash);
              /**
               * @dev Revert with an error when attempting to fill an order outside the
               *      specified start time and end time.
               *
               * @param startTime The time at which the order becomes active.
               * @param endTime   The time at which the order becomes inactive.
               */
              error InvalidTime(uint256 startTime, uint256 endTime);
              /**
               * @dev Revert with an error when attempting to fill an order referencing an
               *      invalid conduit (i.e. one that has not been deployed).
               */
              error InvalidConduit(bytes32 conduitKey, address conduit);
              /**
               * @dev Revert with an error when an order is supplied for fulfillment with
               *      a consideration array that is shorter than the original array.
               */
              error MissingOriginalConsiderationItems();
              /**
               * @dev Revert with an error when an order is validated and the length of
               *      the consideration array is not equal to the supplied total original
               *      consideration items value. This error is also thrown when contract
               *      orders supply a total original consideration items value that does
               *      not match the supplied consideration array length.
               */
              error ConsiderationLengthNotEqualToTotalOriginal();
              /**
               * @dev Revert with an error when a call to a conduit fails with revert data
               *      that is too expensive to return.
               */
              error InvalidCallToConduit(address conduit);
              /**
               * @dev Revert with an error if a consideration amount has not been fully
               *      zeroed out after applying all fulfillments.
               *
               * @param orderIndex         The index of the order with the consideration
               *                           item with a shortfall.
               * @param considerationIndex The index of the consideration item on the
               *                           order.
               * @param shortfallAmount    The unfulfilled consideration amount.
               */
              error ConsiderationNotMet(
                  uint256 orderIndex,
                  uint256 considerationIndex,
                  uint256 shortfallAmount
              );
              /**
               * @dev Revert with an error when insufficient native tokens are supplied as
               *      part of msg.value when fulfilling orders.
               */
              error InsufficientNativeTokensSupplied();
              /**
               * @dev Revert with an error when a native token transfer reverts.
               */
              error NativeTokenTransferGenericFailure(address account, uint256 amount);
              /**
               * @dev Revert with an error when a partial fill is attempted on an order
               *      that does not specify partial fill support in its order type.
               */
              error PartialFillsNotEnabledForOrder();
              /**
               * @dev Revert with an error when attempting to fill an order that has been
               *      cancelled.
               *
               * @param orderHash The hash of the cancelled order.
               */
              error OrderIsCancelled(bytes32 orderHash);
              /**
               * @dev Revert with an error when attempting to fill a basic order that has
               *      been partially filled.
               *
               * @param orderHash The hash of the partially used order.
               */
              error OrderPartiallyFilled(bytes32 orderHash);
              /**
               * @dev Revert with an error when attempting to cancel an order as a caller
               *      other than the indicated offerer or zone or when attempting to
               *      cancel a contract order.
               */
              error CannotCancelOrder();
              /**
               * @dev Revert with an error when supplying a fraction with a value of zero
               *      for the numerator or denominator, or one where the numerator exceeds
               *      the denominator.
               */
              error BadFraction();
              /**
               * @dev Revert with an error when a caller attempts to supply callvalue to a
               *      non-payable basic order route or does not supply any callvalue to a
               *      payable basic order route.
               */
              error InvalidMsgValue(uint256 value);
              /**
               * @dev Revert with an error when attempting to fill a basic order using
               *      calldata not produced by default ABI encoding.
               */
              error InvalidBasicOrderParameterEncoding();
              /**
               * @dev Revert with an error when attempting to fulfill any number of
               *      available orders when none are fulfillable.
               */
              error NoSpecifiedOrdersAvailable();
              /**
               * @dev Revert with an error when attempting to fulfill an order with an
               *      offer for a native token outside of matching orders.
               */
              error InvalidNativeOfferItem();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import { ReentrancyErrors } from "../interfaces/ReentrancyErrors.sol";
          import { LowLevelHelpers } from "./LowLevelHelpers.sol";
          import {
              _revertInvalidMsgValue,
              _revertNoReentrantCalls
          } from "./ConsiderationErrors.sol";
          import {
              _ENTERED_AND_ACCEPTING_NATIVE_TOKENS,
              _ENTERED,
              _NOT_ENTERED
          } from "./ConsiderationConstants.sol";
          /**
           * @title ReentrancyGuard
           * @author 0age
           * @notice ReentrancyGuard contains a storage variable and related functionality
           *         for protecting against reentrancy.
           */
          contract ReentrancyGuard is ReentrancyErrors, LowLevelHelpers {
              // Prevent reentrant calls on protected functions.
              uint256 private _reentrancyGuard;
              /**
               * @dev Initialize the reentrancy guard during deployment.
               */
              constructor() {
                  // Initialize the reentrancy guard in a cleared state.
                  _reentrancyGuard = _NOT_ENTERED;
              }
              /**
               * @dev Internal function to ensure that a sentinel value for the reentrancy
               *      guard is not currently set and, if not, to set a sentinel value for
               *      the reentrancy guard based on whether or not native tokens may be
               *      received during execution or not.
               *
               * @param acceptNativeTokens A boolean indicating whether native tokens may
               *                           be received during execution or not.
               */
              function _setReentrancyGuard(bool acceptNativeTokens) internal {
                  // Ensure that the reentrancy guard is not already set.
                  _assertNonReentrant();
                  // Set the reentrancy guard. A value of 2 indicates that native tokens
                  // may not be accepted during execution, whereas a value of 3 indicates
                  // that they will be accepted (with any remaining native tokens returned
                  // to the caller).
                  unchecked {
                      _reentrancyGuard = _ENTERED + _cast(acceptNativeTokens);
                  }
              }
              /**
               * @dev Internal function to unset the reentrancy guard sentinel value.
               */
              function _clearReentrancyGuard() internal {
                  // Clear the reentrancy guard.
                  _reentrancyGuard = _NOT_ENTERED;
              }
              /**
               * @dev Internal view function to ensure that a sentinel value for the
                      reentrancy guard is not currently set.
               */
              function _assertNonReentrant() internal view {
                  // Ensure that the reentrancy guard is not currently set.
                  if (_reentrancyGuard != _NOT_ENTERED) {
                      _revertNoReentrantCalls();
                  }
              }
              /**
               * @dev Internal view function to ensure that the sentinel value indicating
               *      native tokens may be received during execution is currently set.
               */
              function _assertAcceptingNativeTokens() internal view {
                  // Ensure that the reentrancy guard is not currently set.
                  if (_reentrancyGuard != _ENTERED_AND_ACCEPTING_NATIVE_TOKENS) {
                      _revertInvalidMsgValue(msg.value);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              CostPerWord,
              ExtraGasBuffer,
              FreeMemoryPointerSlot,
              MemoryExpansionCoefficientShift,
              OneWord,
              OneWordShift,
              ThirtyOneBytes
          } from "./ConsiderationConstants.sol";
          /**
           * @title LowLevelHelpers
           * @author 0age
           * @notice LowLevelHelpers contains logic for performing various low-level
           *         operations.
           */
          contract LowLevelHelpers {
              /**
               * @dev Internal view function to revert and pass along the revert reason if
               *      data was returned by the last call and that the size of that data
               *      does not exceed the currently allocated memory size.
               */
              function _revertWithReasonIfOneIsReturned() internal view {
                  assembly {
                      // If it returned a message, bubble it up as long as sufficient gas
                      // remains to do so:
                      if returndatasize() {
                          // Ensure that sufficient gas is available to copy returndata
                          // while expanding memory where necessary. Start by computing
                          // the word size of returndata and allocated memory.
                          let returnDataWords := shr(
                              OneWordShift,
                              add(returndatasize(), ThirtyOneBytes)
                          )
                          // Note: use the free memory pointer in place of msize() to work
                          // around a Yul warning that prevents accessing msize directly
                          // when the IR pipeline is activated.
                          let msizeWords := shr(
                              OneWordShift,
                              mload(FreeMemoryPointerSlot)
                          )
                          // Next, compute the cost of the returndatacopy.
                          let cost := mul(CostPerWord, returnDataWords)
                          // Then, compute cost of new memory allocation.
                          if gt(returnDataWords, msizeWords) {
                              cost := add(
                                  cost,
                                  add(
                                      mul(sub(returnDataWords, msizeWords), CostPerWord),
                                      shr(
                                          MemoryExpansionCoefficientShift,
                                          sub(
                                              mul(returnDataWords, returnDataWords),
                                              mul(msizeWords, msizeWords)
                                          )
                                      )
                                  )
                              )
                          }
                          // Finally, add a small constant and compare to gas remaining;
                          // bubble up the revert data if enough gas is still available.
                          if lt(add(cost, ExtraGasBuffer), gas()) {
                              // Copy returndata to memory; overwrite existing memory.
                              returndatacopy(0, 0, returndatasize())
                              // Revert, specifying memory region with copied returndata.
                              revert(0, returndatasize())
                          }
                      }
                  }
              }
              /**
               * @dev Internal view function to branchlessly select either the caller (if
               *      a supplied recipient is equal to zero) or the supplied recipient (if
               *      that recipient is a nonzero value).
               *
               * @param recipient The supplied recipient.
               *
               * @return updatedRecipient The updated recipient.
               */
              function _substituteCallerForEmptyRecipient(
                  address recipient
              ) internal view returns (address updatedRecipient) {
                  // Utilize assembly to perform a branchless operation on the recipient.
                  assembly {
                      // Add caller to recipient if recipient equals 0; otherwise add 0.
                      updatedRecipient := add(recipient, mul(iszero(recipient), caller()))
                  }
              }
              /**
               * @dev Internal pure function to cast a `bool` value to a `uint256` value.
               *
               * @param b The `bool` value to cast.
               *
               * @return u The `uint256` value.
               */
              function _cast(bool b) internal pure returns (uint256 u) {
                  assembly {
                      u := b
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title ReentrancyErrors
           * @author 0age
           * @notice ReentrancyErrors contains errors related to reentrancy.
           */
          interface ReentrancyErrors {
              /**
               * @dev Revert with an error when a caller attempts to reenter a protected
               *      function.
               */
              error NoReentrantCalls();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              ConduitControllerInterface
          } from "../interfaces/ConduitControllerInterface.sol";
          import {
              ConsiderationEventsAndErrors
          } from "../interfaces/ConsiderationEventsAndErrors.sol";
          import {
              BulkOrder_Typehash_Height_One,
              BulkOrder_Typehash_Height_Two,
              BulkOrder_Typehash_Height_Three,
              BulkOrder_Typehash_Height_Four,
              BulkOrder_Typehash_Height_Five,
              BulkOrder_Typehash_Height_Six,
              BulkOrder_Typehash_Height_Seven,
              BulkOrder_Typehash_Height_Eight,
              BulkOrder_Typehash_Height_Nine,
              BulkOrder_Typehash_Height_Ten,
              BulkOrder_Typehash_Height_Eleven,
              BulkOrder_Typehash_Height_Twelve,
              BulkOrder_Typehash_Height_Thirteen,
              BulkOrder_Typehash_Height_Fourteen,
              BulkOrder_Typehash_Height_Fifteen,
              BulkOrder_Typehash_Height_Sixteen,
              BulkOrder_Typehash_Height_Seventeen,
              BulkOrder_Typehash_Height_Eighteen,
              BulkOrder_Typehash_Height_Nineteen,
              BulkOrder_Typehash_Height_Twenty,
              BulkOrder_Typehash_Height_TwentyOne,
              BulkOrder_Typehash_Height_TwentyTwo,
              BulkOrder_Typehash_Height_TwentyThree,
              BulkOrder_Typehash_Height_TwentyFour,
              EIP712_domainData_chainId_offset,
              EIP712_domainData_nameHash_offset,
              EIP712_domainData_size,
              EIP712_domainData_verifyingContract_offset,
              EIP712_domainData_versionHash_offset,
              FreeMemoryPointerSlot,
              NameLengthPtr,
              NameWithLength,
              OneWord,
              Slot0x80,
              ThreeWords,
              ZeroSlot
          } from "./ConsiderationConstants.sol";
          import { ConsiderationDecoder } from "./ConsiderationDecoder.sol";
          import { ConsiderationEncoder } from "./ConsiderationEncoder.sol";
          /**
           * @title ConsiderationBase
           * @author 0age
           * @notice ConsiderationBase contains immutable constants and constructor logic.
           */
          contract ConsiderationBase is
              ConsiderationDecoder,
              ConsiderationEncoder,
              ConsiderationEventsAndErrors
          {
              // Precompute hashes, original chainId, and domain separator on deployment.
              bytes32 internal immutable _NAME_HASH;
              bytes32 internal immutable _VERSION_HASH;
              bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH;
              bytes32 internal immutable _OFFER_ITEM_TYPEHASH;
              bytes32 internal immutable _CONSIDERATION_ITEM_TYPEHASH;
              bytes32 internal immutable _ORDER_TYPEHASH;
              uint256 internal immutable _CHAIN_ID;
              bytes32 internal immutable _DOMAIN_SEPARATOR;
              // Allow for interaction with the conduit controller.
              ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER;
              // Cache the conduit creation code hash used by the conduit controller.
              bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
              /**
               * @dev Derive and set hashes, reference chainId, and associated domain
               *      separator during deployment.
               *
               * @param conduitController A contract that deploys conduits, or proxies
               *                          that may optionally be used to transfer approved
               *                          ERC20/721/1155 tokens.
               */
              constructor(address conduitController) {
                  // Derive name and version hashes alongside required EIP-712 typehashes.
                  (
                      _NAME_HASH,
                      _VERSION_HASH,
                      _EIP_712_DOMAIN_TYPEHASH,
                      _OFFER_ITEM_TYPEHASH,
                      _CONSIDERATION_ITEM_TYPEHASH,
                      _ORDER_TYPEHASH
                  ) = _deriveTypehashes();
                  // Store the current chainId and derive the current domain separator.
                  _CHAIN_ID = block.chainid;
                  _DOMAIN_SEPARATOR = _deriveDomainSeparator();
                  // Set the supplied conduit controller.
                  _CONDUIT_CONTROLLER = ConduitControllerInterface(conduitController);
                  // Retrieve the conduit creation code hash from the supplied controller.
                  (_CONDUIT_CREATION_CODE_HASH, ) = (
                      _CONDUIT_CONTROLLER.getConduitCodeHashes()
                  );
              }
              /**
               * @dev Internal view function to derive the EIP-712 domain separator.
               *
               * @return domainSeparator The derived domain separator.
               */
              function _deriveDomainSeparator()
                  internal
                  view
                  returns (bytes32 domainSeparator)
              {
                  bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
                  bytes32 nameHash = _NAME_HASH;
                  bytes32 versionHash = _VERSION_HASH;
                  // Leverage scratch space and other memory to perform an efficient hash.
                  assembly {
                      // Retrieve the free memory pointer; it will be replaced afterwards.
                      let freeMemoryPointer := mload(FreeMemoryPointerSlot)
                      // Retrieve value at 0x80; it will also be replaced afterwards.
                      let slot0x80 := mload(Slot0x80)
                      // Place typehash, name hash, and version hash at start of memory.
                      mstore(0, typehash)
                      mstore(EIP712_domainData_nameHash_offset, nameHash)
                      mstore(EIP712_domainData_versionHash_offset, versionHash)
                      // Place chainId in the next memory location.
                      mstore(EIP712_domainData_chainId_offset, chainid())
                      // Place the address of this contract in the next memory location.
                      mstore(EIP712_domainData_verifyingContract_offset, address())
                      // Hash relevant region of memory to derive the domain separator.
                      domainSeparator := keccak256(0, EIP712_domainData_size)
                      // Restore the free memory pointer.
                      mstore(FreeMemoryPointerSlot, freeMemoryPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                      // Restore the value at 0x80.
                      mstore(Slot0x80, slot0x80)
                  }
              }
              /**
               * @dev Internal pure function to retrieve the default name of this
               *      contract and return.
               *
               * @return The name of this contract.
               */
              function _name() internal pure virtual returns (string memory) {
                  // Return the name of the contract.
                  assembly {
                      // First element is the offset for the returned string. Offset the
                      // value in memory by one word so that the free memory pointer will
                      // be overwritten by the next write.
                      mstore(OneWord, OneWord)
                      // Name is right padded, so it touches the length which is left
                      // padded. This enables writing both values at once. The free memory
                      // pointer will be overwritten in the process.
                      mstore(NameLengthPtr, NameWithLength)
                      // Standard ABI encoding pads returned data to the nearest word. Use
                      // the already empty zero slot memory region for this purpose and
                      // return the final name string, offset by the original single word.
                      return(OneWord, ThreeWords)
                  }
              }
              /**
               * @dev Internal pure function to retrieve the default name of this contract
               *      as a string that can be used internally.
               *
               * @return The name of this contract.
               */
              function _nameString() internal pure virtual returns (string memory) {
                  // Return the name of the contract.
                  return "Consideration";
              }
              /**
               * @dev Internal pure function to derive required EIP-712 typehashes and
               *      other hashes during contract creation.
               *
               * @return nameHash                  The hash of the name of the contract.
               * @return versionHash               The hash of the version string of the
               *                                   contract.
               * @return eip712DomainTypehash      The primary EIP-712 domain typehash.
               * @return offerItemTypehash         The EIP-712 typehash for OfferItem
               *                                   types.
               * @return considerationItemTypehash The EIP-712 typehash for
               *                                   ConsiderationItem types.
               * @return orderTypehash             The EIP-712 typehash for Order types.
               */
              function _deriveTypehashes()
                  internal
                  pure
                  returns (
                      bytes32 nameHash,
                      bytes32 versionHash,
                      bytes32 eip712DomainTypehash,
                      bytes32 offerItemTypehash,
                      bytes32 considerationItemTypehash,
                      bytes32 orderTypehash
                  )
              {
                  // Derive hash of the name of the contract.
                  nameHash = keccak256(bytes(_nameString()));
                  // Derive hash of the version string of the contract.
                  versionHash = keccak256(bytes("1.5"));
                  // Construct the OfferItem type string.
                  bytes memory offerItemTypeString = bytes(
                      "OfferItem("
                      "uint8 itemType,"
                      "address token,"
                      "uint256 identifierOrCriteria,"
                      "uint256 startAmount,"
                      "uint256 endAmount"
                      ")"
                  );
                  // Construct the ConsiderationItem type string.
                  bytes memory considerationItemTypeString = bytes(
                      "ConsiderationItem("
                      "uint8 itemType,"
                      "address token,"
                      "uint256 identifierOrCriteria,"
                      "uint256 startAmount,"
                      "uint256 endAmount,"
                      "address recipient"
                      ")"
                  );
                  // Construct the OrderComponents type string, not including the above.
                  bytes memory orderComponentsPartialTypeString = bytes(
                      "OrderComponents("
                      "address offerer,"
                      "address zone,"
                      "OfferItem[] offer,"
                      "ConsiderationItem[] consideration,"
                      "uint8 orderType,"
                      "uint256 startTime,"
                      "uint256 endTime,"
                      "bytes32 zoneHash,"
                      "uint256 salt,"
                      "bytes32 conduitKey,"
                      "uint256 counter"
                      ")"
                  );
                  // Construct the primary EIP-712 domain type string.
                  eip712DomainTypehash = keccak256(
                      bytes(
                          "EIP712Domain("
                          "string name,"
                          "string version,"
                          "uint256 chainId,"
                          "address verifyingContract"
                          ")"
                      )
                  );
                  // Derive the OfferItem type hash using the corresponding type string.
                  offerItemTypehash = keccak256(offerItemTypeString);
                  // Derive ConsiderationItem type hash using corresponding type string.
                  considerationItemTypehash = keccak256(considerationItemTypeString);
                  bytes memory orderTypeString = bytes.concat(
                      orderComponentsPartialTypeString,
                      considerationItemTypeString,
                      offerItemTypeString
                  );
                  // Derive OrderItem type hash via combination of relevant type strings.
                  orderTypehash = keccak256(orderTypeString);
              }
              /**
               * @dev Internal pure function to look up one of twenty-four potential bulk
               *      order typehash constants based on the height of the bulk order tree.
               *      Note that values between one and twenty-four are supported, which is
               *      enforced by _isValidBulkOrderSize.
               *
               * @param _treeHeight The height of the bulk order tree. The value must be
               *                    between one and twenty-four.
               *
               * @return _typeHash The EIP-712 typehash for the bulk order type with the
               *                   given height.
               */
              function _lookupBulkOrderTypehash(
                  uint256 _treeHeight
              ) internal pure returns (bytes32 _typeHash) {
                  // Utilize assembly to efficiently retrieve correct bulk order typehash.
                  assembly {
                      // Use a Yul function to enable use of the `leave` keyword
                      // to stop searching once the appropriate type hash is found.
                      function lookupTypeHash(treeHeight) -> typeHash {
                          // Handle tree heights one through eight.
                          if lt(treeHeight, 9) {
                              // Handle tree heights one through four.
                              if lt(treeHeight, 5) {
                                  // Handle tree heights one and two.
                                  if lt(treeHeight, 3) {
                                      // Utilize branchless logic to determine typehash.
                                      typeHash := ternary(
                                          eq(treeHeight, 1),
                                          BulkOrder_Typehash_Height_One,
                                          BulkOrder_Typehash_Height_Two
                                      )
                                      // Exit the function once typehash has been located.
                                      leave
                                  }
                                  // Handle height three and four via branchless logic.
                                  typeHash := ternary(
                                      eq(treeHeight, 3),
                                      BulkOrder_Typehash_Height_Three,
                                      BulkOrder_Typehash_Height_Four
                                  )
                                  // Exit the function once typehash has been located.
                                  leave
                              }
                              // Handle tree height five and six.
                              if lt(treeHeight, 7) {
                                  // Utilize branchless logic to determine typehash.
                                  typeHash := ternary(
                                      eq(treeHeight, 5),
                                      BulkOrder_Typehash_Height_Five,
                                      BulkOrder_Typehash_Height_Six
                                  )
                                  // Exit the function once typehash has been located.
                                  leave
                              }
                              // Handle height seven and eight via branchless logic.
                              typeHash := ternary(
                                  eq(treeHeight, 7),
                                  BulkOrder_Typehash_Height_Seven,
                                  BulkOrder_Typehash_Height_Eight
                              )
                              // Exit the function once typehash has been located.
                              leave
                          }
                          // Handle tree height nine through sixteen.
                          if lt(treeHeight, 17) {
                              // Handle tree height nine through twelve.
                              if lt(treeHeight, 13) {
                                  // Handle tree height nine and ten.
                                  if lt(treeHeight, 11) {
                                      // Utilize branchless logic to determine typehash.
                                      typeHash := ternary(
                                          eq(treeHeight, 9),
                                          BulkOrder_Typehash_Height_Nine,
                                          BulkOrder_Typehash_Height_Ten
                                      )
                                      // Exit the function once typehash has been located.
                                      leave
                                  }
                                  // Handle height eleven and twelve via branchless logic.
                                  typeHash := ternary(
                                      eq(treeHeight, 11),
                                      BulkOrder_Typehash_Height_Eleven,
                                      BulkOrder_Typehash_Height_Twelve
                                  )
                                  // Exit the function once typehash has been located.
                                  leave
                              }
                              // Handle tree height thirteen and fourteen.
                              if lt(treeHeight, 15) {
                                  // Utilize branchless logic to determine typehash.
                                  typeHash := ternary(
                                      eq(treeHeight, 13),
                                      BulkOrder_Typehash_Height_Thirteen,
                                      BulkOrder_Typehash_Height_Fourteen
                                  )
                                  // Exit the function once typehash has been located.
                                  leave
                              }
                              // Handle height fifteen and sixteen via branchless logic.
                              typeHash := ternary(
                                  eq(treeHeight, 15),
                                  BulkOrder_Typehash_Height_Fifteen,
                                  BulkOrder_Typehash_Height_Sixteen
                              )
                              // Exit the function once typehash has been located.
                              leave
                          }
                          // Handle tree height seventeen through twenty.
                          if lt(treeHeight, 21) {
                              // Handle tree height seventeen and eighteen.
                              if lt(treeHeight, 19) {
                                  // Utilize branchless logic to determine typehash.
                                  typeHash := ternary(
                                      eq(treeHeight, 17),
                                      BulkOrder_Typehash_Height_Seventeen,
                                      BulkOrder_Typehash_Height_Eighteen
                                  )
                                  // Exit the function once typehash has been located.
                                  leave
                              }
                              // Handle height nineteen and twenty via branchless logic.
                              typeHash := ternary(
                                  eq(treeHeight, 19),
                                  BulkOrder_Typehash_Height_Nineteen,
                                  BulkOrder_Typehash_Height_Twenty
                              )
                              // Exit the function once typehash has been located.
                              leave
                          }
                          // Handle tree height twenty-one and twenty-two.
                          if lt(treeHeight, 23) {
                              // Utilize branchless logic to determine typehash.
                              typeHash := ternary(
                                  eq(treeHeight, 21),
                                  BulkOrder_Typehash_Height_TwentyOne,
                                  BulkOrder_Typehash_Height_TwentyTwo
                              )
                              // Exit the function once typehash has been located.
                              leave
                          }
                          // Handle height twenty-three & twenty-four w/ branchless logic.
                          typeHash := ternary(
                              eq(treeHeight, 23),
                              BulkOrder_Typehash_Height_TwentyThree,
                              BulkOrder_Typehash_Height_TwentyFour
                          )
                          // Exit the function once typehash has been located.
                          leave
                      }
                      // Implement ternary conditional using branchless logic.
                      function ternary(cond, ifTrue, ifFalse) -> c {
                          c := xor(ifFalse, mul(cond, xor(ifFalse, ifTrue)))
                      }
                      // Look up the typehash using the supplied tree height.
                      _typeHash := lookupTypeHash(_treeHeight)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              BasicOrder_additionalRecipients_length_cdPtr,
              BasicOrder_common_params_size,
              BasicOrder_startTime_cdPtr,
              BasicOrder_startTimeThroughZoneHash_size,
              Common_amount_offset,
              Common_identifier_offset,
              Common_token_offset,
              generateOrder_base_tail_offset,
              generateOrder_context_head_offset,
              generateOrder_head_offset,
              generateOrder_maximumSpent_head_offset,
              generateOrder_minimumReceived_head_offset,
              generateOrder_selector_offset,
              generateOrder_selector,
              OneWord,
              OneWordShift,
              OnlyFullWordMask,
              OrderFulfilled_baseDataSize,
              OrderFulfilled_offer_length_baseOffset,
              OrderParameters_consideration_head_offset,
              OrderParameters_endTime_offset,
              OrderParameters_offer_head_offset,
              OrderParameters_startTime_offset,
              OrderParameters_zoneHash_offset,
              ratifyOrder_base_tail_offset,
              ratifyOrder_consideration_head_offset,
              ratifyOrder_context_head_offset,
              ratifyOrder_contractNonce_offset,
              ratifyOrder_head_offset,
              ratifyOrder_orderHashes_head_offset,
              ratifyOrder_selector_offset,
              ratifyOrder_selector,
              ReceivedItem_size,
              Selector_length,
              SixtyThreeBytes,
              SpentItem_size_shift,
              SpentItem_size,
              validateOrder_head_offset,
              validateOrder_selector_offset,
              validateOrder_selector,
              validateOrder_zoneParameters_offset,
              ZoneParameters_base_tail_offset,
              ZoneParameters_basicOrderFixedElements_length,
              ZoneParameters_consideration_head_offset,
              ZoneParameters_endTime_offset,
              ZoneParameters_extraData_head_offset,
              ZoneParameters_fulfiller_offset,
              ZoneParameters_offer_head_offset,
              ZoneParameters_offerer_offset,
              ZoneParameters_orderHashes_head_offset,
              ZoneParameters_selectorAndPointer_length,
              ZoneParameters_startTime_offset,
              ZoneParameters_zoneHash_offset
          } from "./ConsiderationConstants.sol";
          import {
              BasicOrderParameters,
              OrderParameters
          } from "./ConsiderationStructs.sol";
          import {
              CalldataPointer,
              getFreeMemoryPointer,
              MemoryPointer
          } from "../helpers/PointerLibraries.sol";
          contract ConsiderationEncoder {
              /**
               * @dev Takes a bytes array and casts it to a memory pointer.
               *
               * @param obj A bytes array in memory.
               *
               * @return ptr A memory pointer to the start of the bytes array in memory.
               */
              function toMemoryPointer(
                  bytes memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Takes an array of bytes32 types and casts it to a memory pointer.
               *
               * @param obj An array of bytes32 types in memory.
               *
               * @return ptr A memory pointer to the start of the array of bytes32 types
               *             in memory.
               */
              function toMemoryPointer(
                  bytes32[] memory obj
              ) internal pure returns (MemoryPointer ptr) {
                  assembly {
                      ptr := obj
                  }
              }
              /**
               * @dev Takes a bytes array in memory and copies it to a new location in
               *      memory.
               *
               * @param src A memory pointer referencing the bytes array to be copied (and
               *            pointing to the length of the bytes array).
               * @param src A memory pointer referencing the location in memory to copy
               *            the bytes array to (and pointing to the length of the copied
               *            bytes array).
               *
               * @return size The size of the bytes array.
               */
              function _encodeBytes(
                  MemoryPointer src,
                  MemoryPointer dst
              ) internal view returns (uint256 size) {
                  unchecked {
                      // Mask the length of the bytes array to protect against overflow
                      // and round up to the nearest word.
                      // Note: `size` also includes the 1 word that stores the length.
                      size = (src.readUint256() + SixtyThreeBytes) & OnlyFullWordMask;
                      // Copy the bytes array to the new memory location.
                      src.copy(dst, size);
                  }
              }
              /**
               * @dev Takes an OrderParameters struct and a context bytes array in memory
               *      and encodes it as `generateOrder` calldata.
               *
               * @param orderParameters The OrderParameters struct used to construct the
               *                        encoded `generateOrder` calldata.
               * @param context         The context bytes array used to construct the
               *                        encoded `generateOrder` calldata.
               *
               * @return dst  A memory pointer referencing the encoded `generateOrder`
               *              calldata.
               * @return size The size of the bytes array.
               */
              function _encodeGenerateOrder(
                  OrderParameters memory orderParameters,
                  bytes memory context
              ) internal view returns (MemoryPointer dst, uint256 size) {
                  // Get the memory pointer for the OrderParameters struct.
                  MemoryPointer src = orderParameters.toMemoryPointer();
                  // Get free memory pointer to write calldata to.
                  dst = getFreeMemoryPointer();
                  // Write generateOrder selector and get pointer to start of calldata.
                  dst.write(generateOrder_selector);
                  dst = dst.offset(generateOrder_selector_offset);
                  // Get pointer to the beginning of the encoded data.
                  MemoryPointer dstHead = dst.offset(generateOrder_head_offset);
                  // Write `fulfiller` to calldata.
                  dstHead.write(msg.sender);
                  // Initialize tail offset, used to populate the minimumReceived array.
                  uint256 tailOffset = generateOrder_base_tail_offset;
                  // Write offset to minimumReceived.
                  dstHead.offset(generateOrder_minimumReceived_head_offset).write(
                      tailOffset
                  );
                  // Get memory pointer to `orderParameters.offer.length`.
                  MemoryPointer srcOfferPointer = src
                      .offset(OrderParameters_offer_head_offset)
                      .readMemoryPointer();
                  // Encode the offer array as a `SpentItem[]`.
                  uint256 minimumReceivedSize = _encodeSpentItems(
                      srcOfferPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate maximumSpent array.
                      tailOffset += minimumReceivedSize;
                  }
                  // Write offset to maximumSpent.
                  dstHead.offset(generateOrder_maximumSpent_head_offset).write(
                      tailOffset
                  );
                  // Get memory pointer to `orderParameters.consideration.length`.
                  MemoryPointer srcConsiderationPointer = src
                      .offset(OrderParameters_consideration_head_offset)
                      .readMemoryPointer();
                  // Encode the consideration array as a `SpentItem[]`.
                  uint256 maximumSpentSize = _encodeSpentItems(
                      srcConsiderationPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate context array.
                      tailOffset += maximumSpentSize;
                  }
                  // Write offset to context.
                  dstHead.offset(generateOrder_context_head_offset).write(tailOffset);
                  // Get memory pointer to context.
                  MemoryPointer srcContext = toMemoryPointer(context);
                  // Encode context as a bytes array.
                  uint256 contextSize = _encodeBytes(
                      srcContext,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment the tail offset, now used to determine final size.
                      tailOffset += contextSize;
                      // Derive the final size by including the selector.
                      size = Selector_length + tailOffset;
                  }
              }
              /**
               * @dev Takes an order hash (e.g. offerer shifted 96 bits to the left XOR'd
               *      with the contract nonce in the case of contract orders), an
               *      OrderParameters struct, context bytes array, and an array of order
               *      hashes for each order included as part of the current fulfillment
               *      and encodes it as `ratifyOrder` calldata.
               *
               * @param orderHash       The order hash (e.g. shl(0x60, offerer) ^ nonce).
               * @param orderParameters The OrderParameters struct used to construct the
               *                        encoded `ratifyOrder` calldata.
               * @param context         The context bytes array used to construct the
               *                        encoded `ratifyOrder` calldata.
               * @param orderHashes     An array of bytes32 values representing the order
               *                        hashes of all orders included as part of the
               *                        current fulfillment.
               * @param shiftedOfferer  The offerer for the order, shifted 96 bits to the
               *                        left.
               *
               * @return dst  A memory pointer referencing the encoded `ratifyOrder`
               *              calldata.
               * @return size The size of the bytes array.
               */
              function _encodeRatifyOrder(
                  bytes32 orderHash, // e.g. shl(0x60, offerer) ^ contract nonce
                  OrderParameters memory orderParameters,
                  bytes memory context, // encoded based on the schemaID
                  bytes32[] memory orderHashes,
                  uint256 shiftedOfferer
              ) internal view returns (MemoryPointer dst, uint256 size) {
                  // Get free memory pointer to write calldata to. This isn't allocated as
                  // it is only used for a single function call.
                  dst = getFreeMemoryPointer();
                  // Write ratifyOrder selector and get pointer to start of calldata.
                  dst.write(ratifyOrder_selector);
                  dst = dst.offset(ratifyOrder_selector_offset);
                  // Get pointer to the beginning of the encoded data.
                  MemoryPointer dstHead = dst.offset(ratifyOrder_head_offset);
                  // Write contractNonce to calldata via xor(orderHash, shiftedOfferer).
                  dstHead.offset(ratifyOrder_contractNonce_offset).write(
                      uint256(orderHash) ^ shiftedOfferer
                  );
                  // Initialize tail offset, used to populate the offer array.
                  uint256 tailOffset = ratifyOrder_base_tail_offset;
                  MemoryPointer src = orderParameters.toMemoryPointer();
                  // Write offset to `offer`.
                  dstHead.write(tailOffset);
                  // Get memory pointer to `orderParameters.offer.length`.
                  MemoryPointer srcOfferPointer = src
                      .offset(OrderParameters_offer_head_offset)
                      .readMemoryPointer();
                  // Encode the offer array as a `SpentItem[]`.
                  uint256 offerSize = _encodeSpentItems(
                      srcOfferPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate consideration array.
                      tailOffset += offerSize;
                  }
                  // Write offset to consideration.
                  dstHead.offset(ratifyOrder_consideration_head_offset).write(tailOffset);
                  // Get pointer to `orderParameters.consideration.length`.
                  MemoryPointer srcConsiderationPointer = src
                      .offset(OrderParameters_consideration_head_offset)
                      .readMemoryPointer();
                  // Encode the consideration array as a `ReceivedItem[]`.
                  uint256 considerationSize = _encodeConsiderationAsReceivedItems(
                      srcConsiderationPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate context array.
                      tailOffset += considerationSize;
                  }
                  // Write offset to context.
                  dstHead.offset(ratifyOrder_context_head_offset).write(tailOffset);
                  // Encode context.
                  uint256 contextSize = _encodeBytes(
                      toMemoryPointer(context),
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate orderHashes array.
                      tailOffset += contextSize;
                  }
                  // Write offset to orderHashes.
                  dstHead.offset(ratifyOrder_orderHashes_head_offset).write(tailOffset);
                  // Encode orderHashes.
                  uint256 orderHashesSize = _encodeOrderHashes(
                      toMemoryPointer(orderHashes),
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment the tail offset, now used to determine final size.
                      tailOffset += orderHashesSize;
                      // Derive the final size by including the selector.
                      size = Selector_length + tailOffset;
                  }
              }
              /**
               * @dev Takes an order hash, OrderParameters struct, extraData bytes array,
               *      and array of order hashes for each order included as part of the
               *      current fulfillment and encodes it as `validateOrder` calldata.
               *      Note that future, new versions of this contract may end up writing
               *      to a memory region that might have been potentially dirtied by the
               *      accumulator. Since the book-keeping for the accumulator does not
               *      update the free memory pointer, it will be necessary to ensure that
               *      all bytes in the memory in the range [dst, dst+size) are fully
               *      updated/written to in this function.
               *
               * @param orderHash       The order hash.
               * @param orderParameters The OrderParameters struct used to construct the
               *                        encoded `validateOrder` calldata.
               * @param extraData       The extraData bytes array used to construct the
               *                        encoded `validateOrder` calldata.
               * @param orderHashes     An array of bytes32 values representing the order
               *                        hashes of all orders included as part of the
               *                        current fulfillment.
               *
               * @return dst  A memory pointer referencing the encoded `validateOrder`
               *              calldata.
               * @return size The size of the bytes array.
               */
              function _encodeValidateOrder(
                  bytes32 orderHash,
                  OrderParameters memory orderParameters,
                  bytes memory extraData,
                  bytes32[] memory orderHashes
              ) internal view returns (MemoryPointer dst, uint256 size) {
                  // Get free memory pointer to write calldata to. This isn't allocated as
                  // it is only used for a single function call.
                  dst = getFreeMemoryPointer();
                  // Write validateOrder selector and get pointer to start of calldata.
                  dst.write(validateOrder_selector);
                  dst = dst.offset(validateOrder_selector_offset);
                  // Get pointer to the beginning of the encoded data.
                  MemoryPointer dstHead = dst.offset(validateOrder_head_offset);
                  // Write offset to zoneParameters to start of calldata.
                  dstHead.write(validateOrder_zoneParameters_offset);
                  // Reuse `dstHead` as pointer to zoneParameters.
                  dstHead = dstHead.offset(validateOrder_zoneParameters_offset);
                  // Write orderHash and fulfiller to zoneParameters.
                  dstHead.writeBytes32(orderHash);
                  dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender);
                  // Get the memory pointer to the order parameters struct.
                  MemoryPointer src = orderParameters.toMemoryPointer();
                  // Copy offerer, startTime, endTime and zoneHash to zoneParameters.
                  dstHead.offset(ZoneParameters_offerer_offset).write(src.readUint256());
                  dstHead.offset(ZoneParameters_startTime_offset).write(
                      src.offset(OrderParameters_startTime_offset).readUint256()
                  );
                  dstHead.offset(ZoneParameters_endTime_offset).write(
                      src.offset(OrderParameters_endTime_offset).readUint256()
                  );
                  dstHead.offset(ZoneParameters_zoneHash_offset).write(
                      src.offset(OrderParameters_zoneHash_offset).readUint256()
                  );
                  // Initialize tail offset, used to populate the offer array.
                  uint256 tailOffset = ZoneParameters_base_tail_offset;
                  // Write offset to `offer`.
                  dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset);
                  // Get pointer to `orderParameters.offer.length`.
                  MemoryPointer srcOfferPointer = src
                      .offset(OrderParameters_offer_head_offset)
                      .readMemoryPointer();
                  // Encode the offer array as a `SpentItem[]`.
                  uint256 offerSize = _encodeSpentItems(
                      srcOfferPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate consideration array.
                      tailOffset += offerSize;
                  }
                  // Write offset to consideration.
                  dstHead.offset(ZoneParameters_consideration_head_offset).write(
                      tailOffset
                  );
                  // Get pointer to `orderParameters.consideration.length`.
                  MemoryPointer srcConsiderationPointer = src
                      .offset(OrderParameters_consideration_head_offset)
                      .readMemoryPointer();
                  // Encode the consideration array as a `ReceivedItem[]`.
                  uint256 considerationSize = _encodeConsiderationAsReceivedItems(
                      srcConsiderationPointer,
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate extraData array.
                      tailOffset += considerationSize;
                  }
                  // Write offset to extraData.
                  dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset);
                  // Copy extraData.
                  uint256 extraDataSize = _encodeBytes(
                      toMemoryPointer(extraData),
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment tail offset, now used to populate orderHashes array.
                      tailOffset += extraDataSize;
                  }
                  // Write offset to orderHashes.
                  dstHead.offset(ZoneParameters_orderHashes_head_offset).write(
                      tailOffset
                  );
                  // Encode the order hashes array.
                  uint256 orderHashesSize = _encodeOrderHashes(
                      toMemoryPointer(orderHashes),
                      dstHead.offset(tailOffset)
                  );
                  unchecked {
                      // Increment the tail offset, now used to determine final size.
                      tailOffset += orderHashesSize;
                      // Derive final size including selector and ZoneParameters pointer.
                      size = ZoneParameters_selectorAndPointer_length + tailOffset;
                  }
              }
              /**
               * @dev Takes an order hash and BasicOrderParameters struct (from calldata)
               *      and encodes it as `validateOrder` calldata.
               *
               * @param orderHash  The order hash.
               * @param parameters The BasicOrderParameters struct used to construct the
               *                   encoded `validateOrder` calldata.
               *
               * @return dst  A memory pointer referencing the encoded `validateOrder`
               *              calldata.
               * @return size The size of the bytes array.
               */
              function _encodeValidateBasicOrder(
                  bytes32 orderHash,
                  BasicOrderParameters calldata parameters
              ) internal view returns (MemoryPointer dst, uint256 size) {
                  // Get free memory pointer to write calldata to. This isn't allocated as
                  // it is only used for a single function call.
                  dst = getFreeMemoryPointer();
                  // Write validateOrder selector and get pointer to start of calldata.
                  dst.write(validateOrder_selector);
                  dst = dst.offset(validateOrder_selector_offset);
                  // Get pointer to the beginning of the encoded data.
                  MemoryPointer dstHead = dst.offset(validateOrder_head_offset);
                  // Write offset to zoneParameters to start of calldata.
                  dstHead.write(validateOrder_zoneParameters_offset);
                  // Reuse `dstHead` as pointer to zoneParameters.
                  dstHead = dstHead.offset(validateOrder_zoneParameters_offset);
                  // Write offerer, orderHash and fulfiller to zoneParameters.
                  dstHead.writeBytes32(orderHash);
                  dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender);
                  dstHead.offset(ZoneParameters_offerer_offset).write(parameters.offerer);
                  // Copy startTime, endTime and zoneHash to zoneParameters.
                  CalldataPointer.wrap(BasicOrder_startTime_cdPtr).copy(
                      dstHead.offset(ZoneParameters_startTime_offset),
                      BasicOrder_startTimeThroughZoneHash_size
                  );
                  // Initialize tail offset, used for the offer + consideration arrays.
                  uint256 tailOffset = ZoneParameters_base_tail_offset;
                  // Write offset to offer from event data into target calldata.
                  dstHead.offset(ZoneParameters_offer_head_offset).write(tailOffset);
                  unchecked {
                      // Write consideration offset next (located 5 words after offer).
                      dstHead.offset(ZoneParameters_consideration_head_offset).write(
                          tailOffset + BasicOrder_common_params_size
                      );
                      // Retrieve the offset to the length of additional recipients.
                      uint256 additionalRecipientsLength = CalldataPointer
                          .wrap(BasicOrder_additionalRecipients_length_cdPtr)
                          .readUint256();
                      // Derive offset to event data using base offset & total recipients.
                      uint256 offerDataOffset = OrderFulfilled_offer_length_baseOffset +
                          additionalRecipientsLength *
                          OneWord;
                      // Derive size of offer and consideration data.
                      // 2 words (lengths) + 4 (offer data) + 5 (consideration 1) + 5 * ar
                      uint256 offerAndConsiderationSize = OrderFulfilled_baseDataSize +
                          (additionalRecipientsLength * ReceivedItem_size);
                      // Copy offer and consideration data from event data to calldata.
                      MemoryPointer.wrap(offerDataOffset).copy(
                          dstHead.offset(tailOffset),
                          offerAndConsiderationSize
                      );
                      // Increment tail offset, now used to populate extraData array.
                      tailOffset += offerAndConsiderationSize;
                  }
                  // Write empty bytes for extraData.
                  dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset);
                  dstHead.offset(tailOffset).write(0);
                  unchecked {
                      // Increment tail offset, now used to populate orderHashes array.
                      tailOffset += OneWord;
                  }
                  // Write offset to orderHashes.
                  dstHead.offset(ZoneParameters_orderHashes_head_offset).write(
                      tailOffset
                  );
                  // Write length = 1 to the orderHashes array.
                  dstHead.offset(tailOffset).write(1);
                  unchecked {
                      // Write the single order hash to the orderHashes array.
                      dstHead.offset(tailOffset + OneWord).writeBytes32(orderHash);
                      // Final size: selector, ZoneParameters pointer, orderHashes & tail.
                      size = ZoneParameters_basicOrderFixedElements_length + tailOffset;
                  }
              }
              /**
               * @dev Takes a memory pointer to an array of bytes32 values representing
               *      the order hashes included as part of the fulfillment and a memory
               *      pointer to a location to copy it to, and copies the source data to
               *      the destination in memory.
               *
               * @param srcLength A memory pointer referencing the order hashes array to
               *                  be copied (and pointing to the length of the array).
               * @param dstLength A memory pointer referencing the location in memory to
               *                  copy the orderHashes array to (and pointing to the
               *                  length of the copied array).
               *
               * @return size The size of the order hashes array (including the length).
               */
              function _encodeOrderHashes(
                  MemoryPointer srcLength,
                  MemoryPointer dstLength
              ) internal view returns (uint256 size) {
                  // Read length of the array from source and write to destination.
                  uint256 length = srcLength.readUint256();
                  dstLength.write(length);
                  unchecked {
                      // Determine head & tail size as one word per element in the array.
                      uint256 headAndTailSize = length << OneWordShift;
                      // Copy the tail starting from the next element of the source to the
                      // next element of the destination.
                      srcLength.next().copy(dstLength.next(), headAndTailSize);
                      // Set size to the length of the tail plus one word for length.
                      size = headAndTailSize + OneWord;
                  }
              }
              /**
               * @dev Takes a memory pointer to an offer or consideration array and a
               *      memory pointer to a location to copy it to, and copies the source
               *      data to the destination in memory as a SpentItem array.
               *
               * @param srcLength A memory pointer referencing the offer or consideration
               *                  array to be copied as a SpentItem array (and pointing to
               *                  the length of the original array).
               * @param dstLength A memory pointer referencing the location in memory to
               *                  copy the offer array to (and pointing to the length of
               *                  the copied array).
               *
               * @return size The size of the SpentItem array (including the length).
               */
              function _encodeSpentItems(
                  MemoryPointer srcLength,
                  MemoryPointer dstLength
              ) internal pure returns (uint256 size) {
                  assembly {
                      // Read length of the array from source and write to destination.
                      let length := mload(srcLength)
                      mstore(dstLength, length)
                      // Get pointer to first item's head position in the array,
                      // containing the item's pointer in memory. The head pointer will be
                      // incremented until it reaches the tail position (start of the
                      // array data).
                      let mPtrHead := add(srcLength, OneWord)
                      // Position in memory to write next item for calldata. Since
                      // SpentItem has a fixed length, the array elements do not contain
                      // head elements in calldata, they are concatenated together after
                      // the array length.
                      let cdPtrData := add(dstLength, OneWord)
                      // Pointer to end of array head in memory.
                      let mPtrHeadEnd := add(mPtrHead, shl(OneWordShift, length))
                      for {
                      } lt(mPtrHead, mPtrHeadEnd) {
                      } {
                          // Read pointer to data for array element from head position.
                          let mPtrTail := mload(mPtrHead)
                          // Copy itemType, token, identifier, amount to calldata.
                          mstore(cdPtrData, mload(mPtrTail))
                          mstore(
                              add(cdPtrData, Common_token_offset),
                              mload(add(mPtrTail, Common_token_offset))
                          )
                          mstore(
                              add(cdPtrData, Common_identifier_offset),
                              mload(add(mPtrTail, Common_identifier_offset))
                          )
                          mstore(
                              add(cdPtrData, Common_amount_offset),
                              mload(add(mPtrTail, Common_amount_offset))
                          )
                          mPtrHead := add(mPtrHead, OneWord)
                          cdPtrData := add(cdPtrData, SpentItem_size)
                      }
                      size := add(OneWord, shl(SpentItem_size_shift, length))
                  }
              }
              /**
               * @dev Takes a memory pointer to an consideration array and a memory
               *      pointer to a location to copy it to, and copies the source data to
               *      the destination in memory as a ReceivedItem array.
               *
               * @param srcLength A memory pointer referencing the consideration array to
               *                  be copied as a ReceivedItem array (and pointing to the
               *                  length of the original array).
               * @param dstLength A memory pointer referencing the location in memory to
               *                  copy the consideration array to as a ReceivedItem array
               *                  (and pointing to the length of the new array).
               *
               * @return size The size of the ReceivedItem array (including the length).
               */
              function _encodeConsiderationAsReceivedItems(
                  MemoryPointer srcLength,
                  MemoryPointer dstLength
              ) internal view returns (uint256 size) {
                  unchecked {
                      // Read length of the array from source and write to destination.
                      uint256 length = srcLength.readUint256();
                      dstLength.write(length);
                      // Get pointer to first item's head position in the array,
                      // containing the item's pointer in memory. The head pointer will be
                      // incremented until it reaches the tail position (start of the
                      // array data).
                      MemoryPointer srcHead = srcLength.next();
                      MemoryPointer srcHeadEnd = srcHead.offset(length << OneWordShift);
                      // Position in memory to write next item for calldata. Since
                      // ReceivedItem has a fixed length, the array elements do not
                      // contain offsets in calldata, they are concatenated together after
                      // the array length.
                      MemoryPointer dstHead = dstLength.next();
                      while (srcHead.lt(srcHeadEnd)) {
                          MemoryPointer srcTail = srcHead.pptr();
                          srcTail.copy(dstHead, ReceivedItem_size);
                          srcHead = srcHead.next();
                          dstHead = dstHead.offset(ReceivedItem_size);
                      }
                      size = OneWord + (length * ReceivedItem_size);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.17;
          import {
              AdvancedOrder,
              ConsiderationItem,
              CriteriaResolver,
              Fulfillment,
              FulfillmentComponent,
              OfferItem,
              Order,
              OrderParameters,
              ReceivedItem
          } from "./ConsiderationStructs.sol";
          import {
              AdvancedOrder_denominator_offset,
              AdvancedOrder_extraData_offset,
              AdvancedOrder_fixed_segment_0,
              AdvancedOrder_head_size,
              AdvancedOrder_numerator_offset,
              AdvancedOrder_signature_offset,
              AdvancedOrderPlusOrderParameters_head_size,
              Common_amount_offset,
              Common_endAmount_offset,
              ConsiderationItem_size_with_length,
              ConsiderationItem_size,
              CriteriaResolver_criteriaProof_offset,
              CriteriaResolver_fixed_segment_0,
              CriteriaResolver_head_size,
              FourWords,
              FreeMemoryPointerSlot,
              Fulfillment_considerationComponents_offset,
              Fulfillment_head_size,
              FulfillmentComponent_mem_tail_size_shift,
              FulfillmentComponent_mem_tail_size,
              generateOrder_maximum_returndatasize,
              OfferItem_size_with_length,
              OfferItem_size,
              OneWord,
              OneWordShift,
              OnlyFullWordMask,
              Order_head_size,
              Order_signature_offset,
              OrderComponents_OrderParameters_common_head_size,
              OrderParameters_consideration_head_offset,
              OrderParameters_head_size,
              OrderParameters_offer_head_offset,
              OrderParameters_totalOriginalConsiderationItems_offset,
              ReceivedItem_recipient_offset,
              ReceivedItem_size,
              ReceivedItem_size_excluding_recipient,
              SpentItem_size_shift,
              SpentItem_size,
              ThirtyOneBytes,
              TwoWords
          } from "./ConsiderationConstants.sol";
          import {
              CalldataPointer,
              malloc,
              MemoryPointer,
              OffsetOrLengthMask
          } from "../helpers/PointerLibraries.sol";
          contract ConsiderationDecoder {
              /**
               * @dev Takes a bytes array from calldata and copies it into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the bytes array in
               *                    calldata which contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the bytes array in
               *                    memory which contains the length of the array.
               */
              function _decodeBytes(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  assembly {
                      // Get the current free memory pointer.
                      mPtrLength := mload(FreeMemoryPointerSlot)
                      // Derive the size of the bytes array, rounding up to nearest word
                      // and adding a word for the length field. Note: masking
                      // `calldataload(cdPtrLength)` is redundant here.
                      let size := add(
                          and(
                              add(calldataload(cdPtrLength), ThirtyOneBytes),
                              OnlyFullWordMask
                          ),
                          OneWord
                      )
                      // Copy bytes from calldata into memory based on pointers and size.
                      calldatacopy(mPtrLength, cdPtrLength, size)
                      // Store the masked value in memory. Note: the value of `size` is at
                      // least 32, meaning the calldatacopy above will at least write to
                      // `[mPtrLength, mPtrLength + 32)`.
                      mstore(
                          mPtrLength,
                          and(calldataload(cdPtrLength), OffsetOrLengthMask)
                      )
                      // Update free memory pointer based on the size of the bytes array.
                      mstore(FreeMemoryPointerSlot, add(mPtrLength, size))
                  }
              }
              /**
               * @dev Takes an offer array from calldata and copies it into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the offer array
               *                    in calldata which contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the offer array in
               *                    memory which contains the length of the array.
               */
              function _decodeOffer(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  assembly {
                      // Retrieve length of array, masking to prevent potential overflow.
                      let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
                      // Get the current free memory pointer.
                      mPtrLength := mload(FreeMemoryPointerSlot)
                      // Write the array length to memory.
                      mstore(mPtrLength, arrLength)
                      // Derive the head by adding one word to the length pointer.
                      let mPtrHead := add(mPtrLength, OneWord)
                      // Derive the tail by adding one word per element (note that structs
                      // are written to memory with an offset per struct element).
                      let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
                      // Track the next tail, beginning with the initial tail value.
                      let mPtrTailNext := mPtrTail
                      // Copy all offer array data into memory at the tail pointer.
                      calldatacopy(
                          mPtrTail,
                          add(cdPtrLength, OneWord),
                          mul(arrLength, OfferItem_size)
                      )
                      // Track the next head pointer, starting with initial head value.
                      let mPtrHeadNext := mPtrHead
                      // Iterate over each head pointer until it reaches the tail.
                      for {
                      } lt(mPtrHeadNext, mPtrTail) {
                      } {
                          // Write the next tail pointer to next head pointer in memory.
                          mstore(mPtrHeadNext, mPtrTailNext)
                          // Increment the next head pointer by one word.
                          mPtrHeadNext := add(mPtrHeadNext, OneWord)
                          // Increment the next tail pointer by the size of an offer item.
                          mPtrTailNext := add(mPtrTailNext, OfferItem_size)
                      }
                      // Update free memory pointer to allocate memory up to end of tail.
                      mstore(FreeMemoryPointerSlot, mPtrTailNext)
                  }
              }
              /**
               * @dev Takes a consideration array from calldata and copies it into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the consideration
               *                    array in calldata which contains the length of the
               *                    array.
               *
               * @return mPtrLength A memory pointer to the start of the consideration
               *                    array in memory which contains the length of the
               *                    array.
               */
              function _decodeConsideration(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  assembly {
                      // Retrieve length of array, masking to prevent potential overflow.
                      let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
                      // Get the current free memory pointer.
                      mPtrLength := mload(FreeMemoryPointerSlot)
                      // Write the array length to memory.
                      mstore(mPtrLength, arrLength)
                      // Derive the head by adding one word to the length pointer.
                      let mPtrHead := add(mPtrLength, OneWord)
                      // Derive the tail by adding one word per element (note that structs
                      // are written to memory with an offset per struct element).
                      let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
                      // Track the next tail, beginning with the initial tail value.
                      let mPtrTailNext := mPtrTail
                      // Copy all consideration array data into memory at tail pointer.
                      calldatacopy(
                          mPtrTail,
                          add(cdPtrLength, OneWord),
                          mul(arrLength, ConsiderationItem_size)
                      )
                      // Track the next head pointer, starting with initial head value.
                      let mPtrHeadNext := mPtrHead
                      // Iterate over each head pointer until it reaches the tail.
                      for {
                      } lt(mPtrHeadNext, mPtrTail) {
                      } {
                          // Write the next tail pointer to next head pointer in memory.
                          mstore(mPtrHeadNext, mPtrTailNext)
                          // Increment the next head pointer by one word.
                          mPtrHeadNext := add(mPtrHeadNext, OneWord)
                          // Increment next tail pointer by size of a consideration item.
                          mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size)
                      }
                      // Update free memory pointer to allocate memory up to end of tail.
                      mstore(FreeMemoryPointerSlot, mPtrTailNext)
                  }
              }
              /**
               * @dev Takes a calldata pointer and memory pointer and copies a referenced
               *      OrderParameters struct and associated offer and consideration data
               *      to memory.
               *
               * @param cdPtr A calldata pointer for the OrderParameters struct.
               * @param mPtr A memory pointer to the OrderParameters struct head.
               */
              function _decodeOrderParametersTo(
                  CalldataPointer cdPtr,
                  MemoryPointer mPtr
              ) internal pure {
                  // Copy the full OrderParameters head from calldata to memory.
                  cdPtr.copy(mPtr, OrderParameters_head_size);
                  // Resolve the offer calldata offset, use that to decode and copy offer
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(OrderParameters_offer_head_offset).write(
                      _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset))
                  );
                  // Resolve consideration calldata offset, use that to copy consideration
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(OrderParameters_consideration_head_offset).write(
                      _decodeConsideration(
                          cdPtr.pptr(OrderParameters_consideration_head_offset)
                      )
                  );
              }
              /**
               * @dev Takes a calldata pointer to an OrderParameters struct and copies the
               *      decoded struct to memory.
               *
               * @param cdPtr A calldata pointer for the OrderParameters struct.
               *
               * @return mPtr A memory pointer to the OrderParameters struct head.
               */
              function _decodeOrderParameters(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate required memory for the OrderParameters head (offer and
                  // consideration are allocated independently).
                  mPtr = malloc(OrderParameters_head_size);
                  // Decode and copy the order parameters to the newly allocated memory.
                  _decodeOrderParametersTo(cdPtr, mPtr);
              }
              /**
               * @dev Takes a calldata pointer to an Order struct and copies the decoded
               *      struct to memory.
               *
               * @param cdPtr A calldata pointer for the Order struct.
               *
               * @return mPtr A memory pointer to the Order struct head.
               */
              function _decodeOrder(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate required memory for the Order head (OrderParameters and
                  // signature are allocated independently).
                  mPtr = malloc(Order_head_size);
                  // Resolve OrderParameters calldata offset, use it to decode and copy
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.write(_decodeOrderParameters(cdPtr.pptr()));
                  // Resolve signature calldata offset, use that to decode and copy from
                  // calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(Order_signature_offset).write(
                      _decodeBytes(cdPtr.pptr(Order_signature_offset))
                  );
              }
              /**
               * @dev Takes a calldata pointer to an AdvancedOrder struct and copies the
               *      decoded struct to memory.
               *
               * @param cdPtr A calldata pointer for the AdvancedOrder struct.
               *
               * @return mPtr A memory pointer to the AdvancedOrder struct head.
               */
              function _decodeAdvancedOrder(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate memory for AdvancedOrder head and OrderParameters head.
                  mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size);
                  // Use numerator + denominator calldata offset to decode and copy
                  // from calldata and write resultant memory offset to head in memory.
                  cdPtr.offset(AdvancedOrder_numerator_offset).copy(
                      mPtr.offset(AdvancedOrder_numerator_offset),
                      AdvancedOrder_fixed_segment_0
                  );
                  // Get pointer to memory immediately after advanced order.
                  MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size);
                  // Write pptr for advanced order parameters to memory.
                  mPtr.write(mPtrParameters);
                  // Resolve OrderParameters calldata pointer & write to allocated region.
                  _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters);
                  // Resolve signature calldata offset, use that to decode and copy from
                  // calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(AdvancedOrder_signature_offset).write(
                      _decodeBytes(cdPtr.pptr(AdvancedOrder_signature_offset))
                  );
                  // Resolve extraData calldata offset, use that to decode and copy from
                  // calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(AdvancedOrder_extraData_offset).write(
                      _decodeBytes(cdPtr.pptr(AdvancedOrder_extraData_offset))
                  );
              }
              /**
               * @dev Allocates a single word of empty bytes in memory and returns the
               *      pointer to that memory region.
               *
               * @return mPtr The memory pointer to the new empty word in memory.
               */
              function _getEmptyBytesOrArray()
                  internal
                  pure
                  returns (MemoryPointer mPtr)
              {
                  mPtr = malloc(OneWord);
                  mPtr.write(0);
              }
              /**
               * @dev Takes a calldata pointer to an Order struct and copies the decoded
               *      struct to memory as an AdvancedOrder.
               *
               * @param cdPtr A calldata pointer for the Order struct.
               *
               * @return mPtr A memory pointer to the AdvancedOrder struct head.
               */
              function _decodeOrderAsAdvancedOrder(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate memory for AdvancedOrder head and OrderParameters head.
                  mPtr = malloc(AdvancedOrderPlusOrderParameters_head_size);
                  // Get pointer to memory immediately after advanced order.
                  MemoryPointer mPtrParameters = mPtr.offset(AdvancedOrder_head_size);
                  // Write pptr for advanced order parameters.
                  mPtr.write(mPtrParameters);
                  // Resolve OrderParameters calldata pointer & write to allocated region.
                  _decodeOrderParametersTo(cdPtr.pptr(), mPtrParameters);
                  // Write default Order numerator and denominator values (i.e. 1/1).
                  mPtr.offset(AdvancedOrder_numerator_offset).write(1);
                  mPtr.offset(AdvancedOrder_denominator_offset).write(1);
                  // Resolve signature calldata offset, use that to decode and copy from
                  // calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(AdvancedOrder_signature_offset).write(
                      _decodeBytes(cdPtr.pptr(Order_signature_offset))
                  );
                  // Resolve extraData calldata offset, use that to decode and copy from
                  // calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(AdvancedOrder_extraData_offset).write(
                      _getEmptyBytesOrArray()
                  );
              }
              /**
               * @dev Takes a calldata pointer to an array of Order structs and copies the
               *      decoded array to memory as an array of AdvancedOrder structs.
               *
               * @param cdPtrLength A calldata pointer to the start of the orders array in
               *                    calldata which contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the array of advanced
               *                    orders in memory which contains length of the array.
               */
              function _decodeOrdersAsAdvancedOrders(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve Order calldata offset, use it to decode and copy from
                          // calldata, and write resultant AdvancedOrder offset to memory.
                          mPtrHead.offset(offset).write(
                              _decodeOrderAsAdvancedOrder(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes a calldata pointer to a criteria proof, or an array bytes32
               *      types, and copies the decoded proof to memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the criteria proof
               *                    in calldata which contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the criteria proof
               *                    in memory which contains length of the array.
               */
              function _decodeCriteriaProof(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive array size based on one word per array element and length.
                      uint256 arrSize = (arrLength + 1) << OneWordShift;
                      // Allocate memory equal to the array size.
                      mPtrLength = malloc(arrSize);
                      // Copy the array from calldata into memory.
                      cdPtrLength.copy(mPtrLength, arrSize);
                  }
              }
              /**
               * @dev Takes a calldata pointer to a CriteriaResolver struct and copies the
               *      decoded struct to memory.
               *
               * @param cdPtr A calldata pointer for the CriteriaResolver struct.
               *
               * @return mPtr A memory pointer to the CriteriaResolver struct head.
               */
              function _decodeCriteriaResolver(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate required memory for the CriteriaResolver head (the criteria
                  // proof bytes32 array is allocated independently).
                  mPtr = malloc(CriteriaResolver_head_size);
                  // Decode and copy order index, side, index, and identifier from
                  // calldata and write resultant memory offset to head in memory.
                  cdPtr.copy(mPtr, CriteriaResolver_fixed_segment_0);
                  // Resolve criteria proof calldata offset, use it to decode and copy
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(CriteriaResolver_criteriaProof_offset).write(
                      _decodeCriteriaProof(
                          cdPtr.pptr(CriteriaResolver_criteriaProof_offset)
                      )
                  );
              }
              /**
               * @dev Takes an array of criteria resolvers from calldata and copies it
               *      into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the criteria
               *                    resolver array in calldata which contains the length
               *                    of the array.
               *
               * @return mPtrLength A memory pointer to the start of the criteria resolver
               *                    array in memory which contains the length of the
               *                    array.
               */
              function _decodeCriteriaResolvers(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve CriteriaResolver calldata offset, use it to decode
                          // and copy from calldata, and write resultant memory offset.
                          mPtrHead.offset(offset).write(
                              _decodeCriteriaResolver(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes an array of orders from calldata and copies it into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the orders array in
               *                    calldata which contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the orders array
               *                    in memory which contains the length of the array.
               */
              function _decodeOrders(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve Order calldata offset, use it to decode and copy
                          // from calldata, and write resultant memory offset.
                          mPtrHead.offset(offset).write(
                              _decodeOrder(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes an array of fulfillment components from calldata and copies it
               *      into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the fulfillment
               *                    components array in calldata which contains the length
               *                    of the array.
               *
               * @return mPtrLength A memory pointer to the start of the fulfillment
               *                    components array in memory which contains the length
               *                    of the array.
               */
              function _decodeFulfillmentComponents(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  assembly {
                      let arrLength := and(calldataload(cdPtrLength), OffsetOrLengthMask)
                      // Get the current free memory pointer.
                      mPtrLength := mload(FreeMemoryPointerSlot)
                      mstore(mPtrLength, arrLength)
                      let mPtrHead := add(mPtrLength, OneWord)
                      let mPtrTail := add(mPtrHead, shl(OneWordShift, arrLength))
                      let mPtrTailNext := mPtrTail
                      calldatacopy(
                          mPtrTail,
                          add(cdPtrLength, OneWord),
                          shl(FulfillmentComponent_mem_tail_size_shift, arrLength)
                      )
                      let mPtrHeadNext := mPtrHead
                      for {
                      } lt(mPtrHeadNext, mPtrTail) {
                      } {
                          mstore(mPtrHeadNext, mPtrTailNext)
                          mPtrHeadNext := add(mPtrHeadNext, OneWord)
                          mPtrTailNext := add(
                              mPtrTailNext,
                              FulfillmentComponent_mem_tail_size
                          )
                      }
                      // Update the free memory pointer.
                      mstore(FreeMemoryPointerSlot, mPtrTailNext)
                  }
              }
              /**
               * @dev Takes a nested array of fulfillment components from calldata and
               *      copies it into memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the nested
               *                    fulfillment components array in calldata which
               *                    contains the length of the array.
               *
               * @return mPtrLength A memory pointer to the start of the nested
               *                    fulfillment components array in memory which
               *                    contains the length of the array.
               */
              function _decodeNestedFulfillmentComponents(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve FulfillmentComponents array calldata offset, use it
                          // to decode and copy from calldata, and write memory offset.
                          mPtrHead.offset(offset).write(
                              _decodeFulfillmentComponents(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes an array of advanced orders from calldata and copies it into
               *      memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the advanced orders
               *                    array in calldata which contains the length of the
               *                    array.
               *
               * @return mPtrLength A memory pointer to the start of the advanced orders
               *                    array in memory which contains the length of the
               *                    array.
               */
              function _decodeAdvancedOrders(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve AdvancedOrder calldata offset, use it to decode and
                          // copy from calldata, and write resultant memory offset.
                          mPtrHead.offset(offset).write(
                              _decodeAdvancedOrder(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes a calldata pointer to a Fulfillment struct and copies the
               *      decoded struct to memory.
               *
               * @param cdPtr A calldata pointer for the Fulfillment struct.
               *
               * @return mPtr A memory pointer to the Fulfillment struct head.
               */
              function _decodeFulfillment(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate required memory for the Fulfillment head (the fulfillment
                  // components arrays are allocated independently).
                  mPtr = malloc(Fulfillment_head_size);
                  // Resolve offerComponents calldata offset, use it to decode and copy
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.write(_decodeFulfillmentComponents(cdPtr.pptr()));
                  // Resolve considerationComponents calldata offset, use it to decode and
                  // copy from calldata, and write resultant memory offset to memory head.
                  mPtr.offset(Fulfillment_considerationComponents_offset).write(
                      _decodeFulfillmentComponents(
                          cdPtr.pptr(Fulfillment_considerationComponents_offset)
                      )
                  );
              }
              /**
               * @dev Takes an array of fulfillments from calldata and copies it into
               *      memory.
               *
               * @param cdPtrLength A calldata pointer to the start of the fulfillments
               *                    array in calldata which contains the length of the
               *                    array.
               *
               * @return mPtrLength A memory pointer to the start of the fulfillments
               *                    array in memory which contains the length of the
               *                    array.
               */
              function _decodeFulfillments(
                  CalldataPointer cdPtrLength
              ) internal pure returns (MemoryPointer mPtrLength) {
                  // Retrieve length of array, masking to prevent potential overflow.
                  uint256 arrLength = cdPtrLength.readMaskedUint256();
                  unchecked {
                      // Derive offset to the tail based on one word per array element.
                      uint256 tailOffset = arrLength << OneWordShift;
                      // Add one additional word for the length and allocate memory.
                      mPtrLength = malloc(tailOffset + OneWord);
                      // Write the length of the array to memory.
                      mPtrLength.write(arrLength);
                      // Advance to first memory & calldata pointers (e.g. after length).
                      MemoryPointer mPtrHead = mPtrLength.next();
                      CalldataPointer cdPtrHead = cdPtrLength.next();
                      // Iterate over each pointer, word by word, until tail is reached.
                      for (uint256 offset = 0; offset < tailOffset; offset += OneWord) {
                          // Resolve Fulfillment calldata offset, use it to decode and
                          // copy from calldata, and write resultant memory offset.
                          mPtrHead.offset(offset).write(
                              _decodeFulfillment(cdPtrHead.pptr(offset))
                          );
                      }
                  }
              }
              /**
               * @dev Takes a calldata pointer to an OrderComponents struct and copies the
               *      decoded struct to memory as an OrderParameters struct (with the
               *      totalOriginalConsiderationItems value set equal to the length of the
               *      supplied consideration array).
               *
               * @param cdPtr A calldata pointer for the OrderComponents struct.
               *
               * @return mPtr A memory pointer to the OrderParameters struct head.
               */
              function _decodeOrderComponentsAsOrderParameters(
                  CalldataPointer cdPtr
              ) internal pure returns (MemoryPointer mPtr) {
                  // Allocate memory for the OrderParameters head.
                  mPtr = malloc(OrderParameters_head_size);
                  // Copy the full OrderComponents head from calldata to memory.
                  cdPtr.copy(mPtr, OrderComponents_OrderParameters_common_head_size);
                  // Resolve the offer calldata offset, use that to decode and copy offer
                  // from calldata, and write resultant memory offset to head in memory.
                  mPtr.offset(OrderParameters_offer_head_offset).write(
                      _decodeOffer(cdPtr.pptr(OrderParameters_offer_head_offset))
                  );
                  // Resolve consideration calldata offset, use that to copy consideration
                  // from calldata, and write resultant memory offset to head in memory.
                  MemoryPointer consideration = _decodeConsideration(
                      cdPtr.pptr(OrderParameters_consideration_head_offset)
                  );
                  mPtr.offset(OrderParameters_consideration_head_offset).write(
                      consideration
                  );
                  // Write masked consideration length to totalOriginalConsiderationItems.
                  mPtr
                      .offset(OrderParameters_totalOriginalConsiderationItems_offset)
                      .write(consideration.readUint256());
              }
              /**
               * @dev Decodes the returndata from a call to generateOrder, or returns
               *      empty arrays and a boolean signifying that the returndata does not
               *      adhere to a valid encoding scheme if it cannot be decoded.
               *
               * @return invalidEncoding A boolean signifying whether the returndata has
               *                         an invalid encoding.
               * @return offer           The decoded offer array.
               * @return consideration   The decoded consideration array.
               */
              function _decodeGenerateOrderReturndata()
                  internal
                  pure
                  returns (
                      uint256 invalidEncoding,
                      MemoryPointer offer,
                      MemoryPointer consideration
                  )
              {
                  assembly {
                      // Check that returndatasize is at least four words: offerOffset,
                      // considerationOffset, offerLength, & considerationLength
                      invalidEncoding := lt(returndatasize(), FourWords)
                      let offsetOffer
                      let offsetConsideration
                      let offerLength
                      let considerationLength
                      // Proceed if enough returndata is present to continue evaluation.
                      if iszero(invalidEncoding) {
                          // Copy first two words of returndata (the offsets to offer and
                          // consideration array lengths) to scratch space.
                          returndatacopy(0, 0, TwoWords)
                          offsetOffer := mload(0)
                          offsetConsideration := mload(OneWord)
                          // If valid length, check that offsets are within returndata.
                          let invalidOfferOffset := gt(offsetOffer, returndatasize())
                          let invalidConsiderationOffset := gt(
                              offsetConsideration,
                              returndatasize()
                          )
                          // Only proceed if length (and thus encoding) is valid so far.
                          invalidEncoding := or(
                              invalidOfferOffset,
                              invalidConsiderationOffset
                          )
                          if iszero(invalidEncoding) {
                              // Copy length of offer array to scratch space.
                              returndatacopy(0, offsetOffer, OneWord)
                              offerLength := mload(0)
                              // Copy length of consideration array to scratch space.
                              returndatacopy(OneWord, offsetConsideration, OneWord)
                              considerationLength := mload(OneWord)
                              {
                                  // Calculate total size of offer & consideration arrays.
                                  let totalOfferSize := shl(
                                      SpentItem_size_shift,
                                      offerLength
                                  )
                                  let totalConsiderationSize := mul(
                                      ReceivedItem_size,
                                      considerationLength
                                  )
                                  // Add 4 words to total size to cover the offset and
                                  // length fields of the two arrays.
                                  let totalSize := add(
                                      FourWords,
                                      add(totalOfferSize, totalConsiderationSize)
                                  )
                                  // Don't continue if returndatasize exceeds 65535 bytes
                                  // or is greater than the calculated size.
                                  invalidEncoding := or(
                                      gt(
                                          or(offerLength, considerationLength),
                                          generateOrder_maximum_returndatasize
                                      ),
                                      gt(totalSize, returndatasize())
                                  )
                                  // Set first word of scratch space to 0 so length of
                                  // offer/consideration are set to 0 on invalid encoding.
                                  mstore(0, 0)
                              }
                          }
                      }
                      if iszero(invalidEncoding) {
                          offer := copySpentItemsAsOfferItems(
                              add(offsetOffer, OneWord),
                              offerLength
                          )
                          consideration := copyReceivedItemsAsConsiderationItems(
                              add(offsetConsideration, OneWord),
                              considerationLength
                          )
                      }
                      function copySpentItemsAsOfferItems(rdPtrHead, length)
                          -> mPtrLength
                      {
                          // Retrieve the current free memory pointer.
                          mPtrLength := mload(FreeMemoryPointerSlot)
                          // Allocate memory for the array.
                          mstore(
                              FreeMemoryPointerSlot,
                              add(
                                  mPtrLength,
                                  add(OneWord, mul(length, OfferItem_size_with_length))
                              )
                          )
                          // Write the length of the array to the start of free memory.
                          mstore(mPtrLength, length)
                          // Use offset from length to minimize stack depth.
                          let headOffsetFromLength := OneWord
                          let headSizeWithLength := shl(OneWordShift, add(1, length))
                          let mPtrTailNext := add(mPtrLength, headSizeWithLength)
                          // Iterate over each element.
                          for {
                          } lt(headOffsetFromLength, headSizeWithLength) {
                          } {
                              // Write the memory pointer to the accompanying head offset.
                              mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext)
                              // Copy itemType, token, identifier and amount.
                              returndatacopy(mPtrTailNext, rdPtrHead, SpentItem_size)
                              // Copy amount to endAmount.
                              mstore(
                                  add(mPtrTailNext, Common_endAmount_offset),
                                  mload(add(mPtrTailNext, Common_amount_offset))
                              )
                              // Update read pointer, next tail pointer, and head offset.
                              rdPtrHead := add(rdPtrHead, SpentItem_size)
                              mPtrTailNext := add(mPtrTailNext, OfferItem_size)
                              headOffsetFromLength := add(headOffsetFromLength, OneWord)
                          }
                      }
                      function copyReceivedItemsAsConsiderationItems(rdPtrHead, length)
                          -> mPtrLength
                      {
                          // Retrieve the current free memory pointer.
                          mPtrLength := mload(FreeMemoryPointerSlot)
                          // Allocate memory for the array.
                          mstore(
                              FreeMemoryPointerSlot,
                              add(
                                  mPtrLength,
                                  add(
                                      OneWord,
                                      mul(length, ConsiderationItem_size_with_length)
                                  )
                              )
                          )
                          // Write the length of the array to the start of free memory.
                          mstore(mPtrLength, length)
                          // Use offset from length to minimize stack depth.
                          let headOffsetFromLength := OneWord
                          let headSizeWithLength := shl(OneWordShift, add(1, length))
                          let mPtrTailNext := add(mPtrLength, headSizeWithLength)
                          // Iterate over each element.
                          for {
                          } lt(headOffsetFromLength, headSizeWithLength) {
                          } {
                              // Write the memory pointer to the accompanying head offset.
                              mstore(add(mPtrLength, headOffsetFromLength), mPtrTailNext)
                              // Copy itemType, token, identifier and amount.
                              returndatacopy(
                                  mPtrTailNext,
                                  rdPtrHead,
                                  ReceivedItem_size_excluding_recipient
                              )
                              // Copy amount and recipient.
                              returndatacopy(
                                  add(mPtrTailNext, Common_endAmount_offset),
                                  add(rdPtrHead, Common_amount_offset),
                                  TwoWords
                              )
                              // Update read pointer, next tail pointer, and head offset.
                              rdPtrHead := add(rdPtrHead, ReceivedItem_size)
                              mPtrTailNext := add(mPtrTailNext, ConsiderationItem_size)
                              headOffsetFromLength := add(headOffsetFromLength, OneWord)
                          }
                      }
                  }
              }
              /**
               * @dev Converts a function returning _decodeGenerateOrderReturndata types
               *      into a function returning offer and consideration types.
               *
               * @param inFn The input function, taking no arguments and returning an
               *             error buffer, spent item array, and received item array.
               *
               * @return outFn The output function, taking no arguments and returning an
               *               error buffer, offer array, and consideration array.
               */
              function _convertGetGeneratedOrderResult(
                  function()
                      internal
                      pure
                      returns (uint256, MemoryPointer, MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function()
                          internal
                          pure
                          returns (
                              uint256,
                              OfferItem[] memory,
                              ConsiderationItem[] memory
                          ) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes
               *      types (e.g. the _transfer function) into a function taking
               *      OfferItem, address, bytes32, and bytes types.
               *
               * @param inFn The input function, taking ReceivedItem, address, bytes32,
               *             and bytes types (e.g. the _transfer function).
               *
               * @return outFn The output function, taking OfferItem, address, bytes32,
               *               and bytes types.
               */
              function _toOfferItemInput(
                  function(ReceivedItem memory, address, bytes32, bytes memory)
                      internal inFn
              )
                  internal
                  pure
                  returns (
                      function(OfferItem memory, address, bytes32, bytes memory)
                          internal outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking ReceivedItem, address, bytes32, and bytes
               *      types (e.g. the _transfer function) into a function taking
               *      ConsiderationItem, address, bytes32, and bytes types.
               *
               * @param inFn The input function, taking ReceivedItem, address, bytes32,
               *             and bytes types (e.g. the _transfer function).
               *
               * @return outFn The output function, taking ConsiderationItem, address,
               *               bytes32, and bytes types.
               */
              function _toConsiderationItemInput(
                  function(ReceivedItem memory, address, bytes32, bytes memory)
                      internal inFn
              )
                  internal
                  pure
                  returns (
                      function(ConsiderationItem memory, address, bytes32, bytes memory)
                          internal outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      an OrderParameters type.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning an OrderParameters type.
               */
              function _toOrderParametersReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (OrderParameters memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      an AdvancedOrder type.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning an AdvancedOrder type.
               */
              function _toAdvancedOrderReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (AdvancedOrder memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      a dynamic array of CriteriaResolver types.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning a dynamic array of CriteriaResolver types.
               */
              function _toCriteriaResolversReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (CriteriaResolver[] memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      a dynamic array of Order types.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning a dynamic array of Order types.
               */
              function _toOrdersReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (Order[] memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      a nested dynamic array of dynamic arrays of FulfillmentComponent
               *      types.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning a nested dynamic array of dynamic arrays of
               *               FulfillmentComponent types.
               */
              function _toNestedFulfillmentComponentsReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (FulfillmentComponent[][] memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      a dynamic array of AdvancedOrder types.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning a dynamic array of AdvancedOrder types.
               */
              function _toAdvancedOrdersReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (AdvancedOrder[] memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Converts a function taking a calldata pointer and returning a memory
               *      pointer into a function taking that calldata pointer and returning
               *      a dynamic array of Fulfillment types.
               *
               * @param inFn The input function, taking an arbitrary calldata pointer and
               *             returning an arbitrary memory pointer.
               *
               * @return outFn The output function, taking an arbitrary calldata pointer
               *               and returning a dynamic array of Fulfillment types.
               */
              function _toFulfillmentsReturnType(
                  function(CalldataPointer) internal pure returns (MemoryPointer) inFn
              )
                  internal
                  pure
                  returns (
                      function(CalldataPointer)
                          internal
                          pure
                          returns (Fulfillment[] memory) outFn
                  )
              {
                  assembly {
                      outFn := inFn
                  }
              }
              /**
               * @dev Caches the endAmount in an offer item and replaces it with
               * a given recipient so that its memory may be reused as a temporary
               * ReceivedItem.
               *
               * @param offerItem The offer item.
               * @param recipient The recipient.
               *
               * @return originalEndAmount The original end amount.
               */
              function _replaceEndAmountWithRecipient(
                  OfferItem memory offerItem,
                  address recipient
              ) internal pure returns (uint256 originalEndAmount) {
                  assembly {
                      // Derive the pointer to the end amount on the offer item.
                      let endAmountPtr := add(offerItem, ReceivedItem_recipient_offset)
                      // Retrieve the value of the end amount on the offer item.
                      originalEndAmount := mload(endAmountPtr)
                      // Write recipient to received item at the offer end amount pointer.
                      mstore(endAmountPtr, recipient)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title ConduitControllerInterface
           * @author 0age
           * @notice ConduitControllerInterface contains all external function interfaces,
           *         structs, events, and errors for the conduit controller.
           */
          interface ConduitControllerInterface {
              /**
               * @dev Track the conduit key, current owner, new potential owner, and open
               *      channels for each deployed conduit.
               */
              struct ConduitProperties {
                  bytes32 key;
                  address owner;
                  address potentialOwner;
                  address[] channels;
                  mapping(address => uint256) channelIndexesPlusOne;
              }
              /**
               * @dev Emit an event whenever a new conduit is created.
               *
               * @param conduit    The newly created conduit.
               * @param conduitKey The conduit key used to create the new conduit.
               */
              event NewConduit(address conduit, bytes32 conduitKey);
              /**
               * @dev Emit an event whenever conduit ownership is transferred.
               *
               * @param conduit       The conduit for which ownership has been
               *                      transferred.
               * @param previousOwner The previous owner of the conduit.
               * @param newOwner      The new owner of the conduit.
               */
              event OwnershipTransferred(
                  address indexed conduit,
                  address indexed previousOwner,
                  address indexed newOwner
              );
              /**
               * @dev Emit an event whenever a conduit owner registers a new potential
               *      owner for that conduit.
               *
               * @param newPotentialOwner The new potential owner of the conduit.
               */
              event PotentialOwnerUpdated(address indexed newPotentialOwner);
              /**
               * @dev Revert with an error when attempting to create a new conduit using a
               *      conduit key where the first twenty bytes of the key do not match the
               *      address of the caller.
               */
              error InvalidCreator();
              /**
               * @dev Revert with an error when attempting to create a new conduit when no
               *      initial owner address is supplied.
               */
              error InvalidInitialOwner();
              /**
               * @dev Revert with an error when attempting to set a new potential owner
               *      that is already set.
               */
              error NewPotentialOwnerAlreadySet(
                  address conduit,
                  address newPotentialOwner
              );
              /**
               * @dev Revert with an error when attempting to cancel ownership transfer
               *      when no new potential owner is currently set.
               */
              error NoPotentialOwnerCurrentlySet(address conduit);
              /**
               * @dev Revert with an error when attempting to interact with a conduit that
               *      does not yet exist.
               */
              error NoConduit();
              /**
               * @dev Revert with an error when attempting to create a conduit that
               *      already exists.
               */
              error ConduitAlreadyExists(address conduit);
              /**
               * @dev Revert with an error when attempting to update channels or transfer
               *      ownership of a conduit when the caller is not the owner of the
               *      conduit in question.
               */
              error CallerIsNotOwner(address conduit);
              /**
               * @dev Revert with an error when attempting to register a new potential
               *      owner and supplying the null address.
               */
              error NewPotentialOwnerIsZeroAddress(address conduit);
              /**
               * @dev Revert with an error when attempting to claim ownership of a conduit
               *      with a caller that is not the current potential owner for the
               *      conduit in question.
               */
              error CallerIsNotNewPotentialOwner(address conduit);
              /**
               * @dev Revert with an error when attempting to retrieve a channel using an
               *      index that is out of range.
               */
              error ChannelOutOfRange(address conduit);
              /**
               * @notice Deploy a new conduit using a supplied conduit key and assigning
               *         an initial owner for the deployed conduit. Note that the first
               *         twenty bytes of the supplied conduit key must match the caller
               *         and that a new conduit cannot be created if one has already been
               *         deployed using the same conduit key.
               *
               * @param conduitKey   The conduit key used to deploy the conduit. Note that
               *                     the first twenty bytes of the conduit key must match
               *                     the caller of this contract.
               * @param initialOwner The initial owner to set for the new conduit.
               *
               * @return conduit The address of the newly deployed conduit.
               */
              function createConduit(
                  bytes32 conduitKey,
                  address initialOwner
              ) external returns (address conduit);
              /**
               * @notice Open or close a channel on a given conduit, thereby allowing the
               *         specified account to execute transfers against that conduit.
               *         Extreme care must be taken when updating channels, as malicious
               *         or vulnerable channels can transfer any ERC20, ERC721 and ERC1155
               *         tokens where the token holder has granted the conduit approval.
               *         Only the owner of the conduit in question may call this function.
               *
               * @param conduit The conduit for which to open or close the channel.
               * @param channel The channel to open or close on the conduit.
               * @param isOpen  A boolean indicating whether to open or close the channel.
               */
              function updateChannel(
                  address conduit,
                  address channel,
                  bool isOpen
              ) external;
              /**
               * @notice Initiate conduit ownership transfer by assigning a new potential
               *         owner for the given conduit. Once set, the new potential owner
               *         may call `acceptOwnership` to claim ownership of the conduit.
               *         Only the owner of the conduit in question may call this function.
               *
               * @param conduit The conduit for which to initiate ownership transfer.
               * @param newPotentialOwner The new potential owner of the conduit.
               */
              function transferOwnership(
                  address conduit,
                  address newPotentialOwner
              ) external;
              /**
               * @notice Clear the currently set potential owner, if any, from a conduit.
               *         Only the owner of the conduit in question may call this function.
               *
               * @param conduit The conduit for which to cancel ownership transfer.
               */
              function cancelOwnershipTransfer(address conduit) external;
              /**
               * @notice Accept ownership of a supplied conduit. Only accounts that the
               *         current owner has set as the new potential owner may call this
               *         function.
               *
               * @param conduit The conduit for which to accept ownership.
               */
              function acceptOwnership(address conduit) external;
              /**
               * @notice Retrieve the current owner of a deployed conduit.
               *
               * @param conduit The conduit for which to retrieve the associated owner.
               *
               * @return owner The owner of the supplied conduit.
               */
              function ownerOf(address conduit) external view returns (address owner);
              /**
               * @notice Retrieve the conduit key for a deployed conduit via reverse
               *         lookup.
               *
               * @param conduit The conduit for which to retrieve the associated conduit
               *                key.
               *
               * @return conduitKey The conduit key used to deploy the supplied conduit.
               */
              function getKey(address conduit) external view returns (bytes32 conduitKey);
              /**
               * @notice Derive the conduit associated with a given conduit key and
               *         determine whether that conduit exists (i.e. whether it has been
               *         deployed).
               *
               * @param conduitKey The conduit key used to derive the conduit.
               *
               * @return conduit The derived address of the conduit.
               * @return exists  A boolean indicating whether the derived conduit has been
               *                 deployed or not.
               */
              function getConduit(
                  bytes32 conduitKey
              ) external view returns (address conduit, bool exists);
              /**
               * @notice Retrieve the potential owner, if any, for a given conduit. The
               *         current owner may set a new potential owner via
               *         `transferOwnership` and that owner may then accept ownership of
               *         the conduit in question via `acceptOwnership`.
               *
               * @param conduit The conduit for which to retrieve the potential owner.
               *
               * @return potentialOwner The potential owner, if any, for the conduit.
               */
              function getPotentialOwner(
                  address conduit
              ) external view returns (address potentialOwner);
              /**
               * @notice Retrieve the status (either open or closed) of a given channel on
               *         a conduit.
               *
               * @param conduit The conduit for which to retrieve the channel status.
               * @param channel The channel for which to retrieve the status.
               *
               * @return isOpen The status of the channel on the given conduit.
               */
              function getChannelStatus(
                  address conduit,
                  address channel
              ) external view returns (bool isOpen);
              /**
               * @notice Retrieve the total number of open channels for a given conduit.
               *
               * @param conduit The conduit for which to retrieve the total channel count.
               *
               * @return totalChannels The total number of open channels for the conduit.
               */
              function getTotalChannels(
                  address conduit
              ) external view returns (uint256 totalChannels);
              /**
               * @notice Retrieve an open channel at a specific index for a given conduit.
               *         Note that the index of a channel can change as a result of other
               *         channels being closed on the conduit.
               *
               * @param conduit      The conduit for which to retrieve the open channel.
               * @param channelIndex The index of the channel in question.
               *
               * @return channel The open channel, if any, at the specified channel index.
               */
              function getChannel(
                  address conduit,
                  uint256 channelIndex
              ) external view returns (address channel);
              /**
               * @notice Retrieve all open channels for a given conduit. Note that calling
               *         this function for a conduit with many channels will revert with
               *         an out-of-gas error.
               *
               * @param conduit The conduit for which to retrieve open channels.
               *
               * @return channels An array of open channels on the given conduit.
               */
              function getChannels(
                  address conduit
              ) external view returns (address[] memory channels);
              /**
               * @dev Retrieve the conduit creation code and runtime code hashes.
               */
              function getConduitCodeHashes()
                  external
                  view
                  returns (bytes32 creationCodeHash, bytes32 runtimeCodeHash);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title SignatureVerificationErrors
           * @author 0age
           * @notice SignatureVerificationErrors contains all errors related to signature
           *         verification.
           */
          interface SignatureVerificationErrors {
              /**
               * @dev Revert with an error when a signature that does not contain a v
               *      value of 27 or 28 has been supplied.
               *
               * @param v The invalid v value.
               */
              error BadSignatureV(uint8 v);
              /**
               * @dev Revert with an error when the signer recovered by the supplied
               *      signature does not match the offerer or an allowed EIP-1271 signer
               *      as specified by the offerer in the event they are a contract.
               */
              error InvalidSigner();
              /**
               * @dev Revert with an error when a signer cannot be recovered from the
               *      supplied signature.
               */
              error InvalidSignature();
              /**
               * @dev Revert with an error when an EIP-1271 call to an account fails.
               */
              error BadContractSignature();
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /*
           * -------------------------- Disambiguation & Other Notes ---------------------
           *    - The term "head" is used as it is in the documentation for ABI encoding,
           *      but only in reference to dynamic types, i.e. it always refers to the
           *      offset or pointer to the body of a dynamic type. In calldata, the head
           *      is always an offset (relative to the parent object), while in memory,
           *      the head is always the pointer to the body. More information found here:
           *      https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#argument-encoding
           *        - Note that the length of an array is separate from and precedes the
           *          head of the array.
           *
           *    - The term "body" is used in place of the term "head" used in the ABI
           *      documentation. It refers to the start of the data for a dynamic type,
           *      e.g. the first word of a struct or the first word of the first element
           *      in an array.
           *
           *    - The term "pointer" is used to describe the absolute position of a value
           *      and never an offset relative to another value.
           *        - The suffix "_ptr" refers to a memory pointer.
           *        - The suffix "_cdPtr" refers to a calldata pointer.
           *
           *    - The term "offset" is used to describe the position of a value relative
           *      to some parent value. For example, OrderParameters_conduit_offset is the
           *      offset to the "conduit" value in the OrderParameters struct relative to
           *      the start of the body.
           *        - Note: Offsets are used to derive pointers.
           *
           *    - Some structs have pointers defined for all of their fields in this file.
           *      Lines which are commented out are fields that are not used in the
           *      codebase but have been left in for readability.
           */
          uint256 constant ThirtyOneBytes = 0x1f;
          uint256 constant OneWord = 0x20;
          uint256 constant TwoWords = 0x40;
          uint256 constant ThreeWords = 0x60;
          uint256 constant OneWordShift = 0x5;
          uint256 constant TwoWordsShift = 0x6;
          uint256 constant FreeMemoryPointerSlot = 0x40;
          uint256 constant ZeroSlot = 0x60;
          uint256 constant DefaultFreeMemoryPointer = 0x80;
          uint256 constant Slot0x80 = 0x80;
          uint256 constant Slot0xA0 = 0xa0;
          uint256 constant Slot0xC0 = 0xc0;
          uint256 constant Generic_error_selector_offset = 0x1c;
          // abi.encodeWithSignature("transferFrom(address,address,uint256)")
          uint256 constant ERC20_transferFrom_signature = (
              0x23b872dd00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC20_transferFrom_sig_ptr = 0x0;
          uint256 constant ERC20_transferFrom_from_ptr = 0x04;
          uint256 constant ERC20_transferFrom_to_ptr = 0x24;
          uint256 constant ERC20_transferFrom_amount_ptr = 0x44;
          uint256 constant ERC20_transferFrom_length = 0x64; // 4 + 32 * 3 == 100
          // abi.encodeWithSignature(
          //     "safeTransferFrom(address,address,uint256,uint256,bytes)"
          // )
          uint256 constant ERC1155_safeTransferFrom_signature = (
              0xf242432a00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155_safeTransferFrom_sig_ptr = 0x0;
          uint256 constant ERC1155_safeTransferFrom_from_ptr = 0x04;
          uint256 constant ERC1155_safeTransferFrom_to_ptr = 0x24;
          uint256 constant ERC1155_safeTransferFrom_id_ptr = 0x44;
          uint256 constant ERC1155_safeTransferFrom_amount_ptr = 0x64;
          uint256 constant ERC1155_safeTransferFrom_data_offset_ptr = 0x84;
          uint256 constant ERC1155_safeTransferFrom_data_length_ptr = 0xa4;
          uint256 constant ERC1155_safeTransferFrom_length = 0xc4; // 4 + 32 * 6 == 196
          uint256 constant ERC1155_safeTransferFrom_data_length_offset = 0xa0;
          // abi.encodeWithSignature(
          //     "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"
          // )
          uint256 constant ERC1155_safeBatchTransferFrom_signature = (
              0x2eb2c2d600000000000000000000000000000000000000000000000000000000
          );
          // bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4(
          //     bytes32(ERC1155_safeBatchTransferFrom_signature)
          // );
          uint256 constant ERC721_transferFrom_signature = (
              0x23b872dd00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC721_transferFrom_sig_ptr = 0x0;
          uint256 constant ERC721_transferFrom_from_ptr = 0x04;
          uint256 constant ERC721_transferFrom_to_ptr = 0x24;
          uint256 constant ERC721_transferFrom_id_ptr = 0x44;
          uint256 constant ERC721_transferFrom_length = 0x64; // 4 + 32 * 3 == 100
          /*
           *  error NoContract(address account)
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x00: account
           * Revert buffer is memory[0x1c:0x40]
           */
          uint256 constant NoContract_error_selector = 0x5f15d672;
          uint256 constant NoContract_error_account_ptr = 0x20;
          uint256 constant NoContract_error_length = 0x24;
          /*
           *  error TokenTransferGenericFailure(
           *      address token,
           *      address from,
           *      address to,
           *      uint256 identifier,
           *      uint256 amount
           *  )
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x20: token
           *    - 0x40: from
           *    - 0x60: to
           *    - 0x80: identifier
           *    - 0xa0: amount
           * Revert buffer is memory[0x1c:0xc0]
           */
          uint256 constant TokenTransferGenericFailure_error_selector = 0xf486bc87;
          uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x20;
          uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x40;
          uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x60;
          uint256 constant TokenTransferGenericFailure_error_identifier_ptr = 0x80;
          uint256 constant TokenTransferGenericFailure_err_identifier_ptr = 0x80;
          uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0xa0;
          uint256 constant TokenTransferGenericFailure_error_length = 0xa4;
          uint256 constant ExtraGasBuffer = 0x20;
          uint256 constant CostPerWord = 0x3;
          uint256 constant MemoryExpansionCoefficientShift = 0x9;
          // Values are offset by 32 bytes in order to write the token to the beginning
          // in the event of a revert
          uint256 constant BatchTransfer1155Params_ptr = 0x24;
          uint256 constant BatchTransfer1155Params_ids_head_ptr = 0x64;
          uint256 constant BatchTransfer1155Params_amounts_head_ptr = 0x84;
          uint256 constant BatchTransfer1155Params_data_head_ptr = 0xa4;
          uint256 constant BatchTransfer1155Params_data_length_basePtr = 0xc4;
          uint256 constant BatchTransfer1155Params_calldata_baseSize = 0xc4;
          uint256 constant BatchTransfer1155Params_ids_length_ptr = 0xc4;
          uint256 constant BatchTransfer1155Params_ids_length_offset = 0xa0;
          // uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0;
          // uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0;
          uint256 constant ConduitBatch1155Transfer_usable_head_size = 0x80;
          uint256 constant ConduitBatch1155Transfer_from_offset = 0x20;
          uint256 constant ConduitBatch1155Transfer_ids_head_offset = 0x60;
          // uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80;
          uint256 constant ConduitBatch1155Transfer_ids_length_offset = 0xa0;
          uint256 constant ConduitBatch1155Transfer_amounts_length_baseOffset = 0xc0;
          // uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0;
          // Note: abbreviated version of above constant to adhere to line length limit.
          uint256 constant ConduitBatchTransfer_amounts_head_offset = 0x80;
          uint256 constant Invalid1155BatchTransferEncoding_ptr = 0x00;
          uint256 constant Invalid1155BatchTransferEncoding_length = 0x04;
          uint256 constant Invalid1155BatchTransferEncoding_selector = (
              0xeba2084c00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155BatchTransferGenericFailure_error_signature = (
              0xafc445e200000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155BatchTransferGenericFailure_token_ptr = 0x04;
          uint256 constant ERC1155BatchTransferGenericFailure_ids_offset = 0xc0;
          /*
           *  error BadReturnValueFromERC20OnTransfer(
           *      address token, address from, address to, uint256 amount
           *  )
           *    - Defined in TokenTransferrerErrors.sol
           *  Memory layout:
           *    - 0x00: Left-padded selector (data begins at 0x1c)
           *    - 0x00: token
           *    - 0x20: from
           *    - 0x40: to
           *    - 0x60: amount
           * Revert buffer is memory[0x1c:0xa0]
           */
          uint256 constant BadReturnValueFromERC20OnTransfer_error_selector = 0x98891923;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x20;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x40;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x60;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x80;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84;
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          /**
           * @title ZoneInteractionErrors
           * @author 0age
           * @notice ZoneInteractionErrors contains errors related to zone interaction.
           */
          interface ZoneInteractionErrors {
              /**
               * @dev Revert with an error when attempting to fill an order that specifies
               *      a restricted submitter as its order type when not submitted by
               *      either the offerer or the order's zone or approved as valid by the
               *      zone in question via a call to `isValidOrder`.
               *
               * @param orderHash The order hash for the invalid restricted order.
               */
              error InvalidRestrictedOrder(bytes32 orderHash);
              /**
               * @dev Revert with an error when attempting to fill a contract order that
               *      fails to generate an order successfully, that does not adhere to the
               *      requirements for minimum spent or maximum received supplied by the
               *      fulfiller, or that fails the post-execution `ratifyOrder` check..
               *
               * @param orderHash The order hash for the invalid contract order.
               */
              error InvalidContractOrder(bytes32 orderHash);
          }
          

          File 2 of 6: Bueno721Drop
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/ContextUpgradeable.sol";
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              function __Ownable_init() internal onlyInitializing {
                  __Ownable_init_unchained();
              }
              function __Ownable_init_unchained() internal onlyInitializing {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (finance/PaymentSplitter.sol)
          pragma solidity ^0.8.0;
          import "../token/ERC20/utils/SafeERC20Upgradeable.sol";
          import "../utils/AddressUpgradeable.sol";
          import "../utils/ContextUpgradeable.sol";
          import "../proxy/utils/Initializable.sol";
          /**
           * @title PaymentSplitter
           * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
           * that the Ether will be split in this way, since it is handled transparently by the contract.
           *
           * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
           * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
           * an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
           * time of contract deployment and can't be updated thereafter.
           *
           * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
           * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
           * function.
           *
           * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
           * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
           * to run tests before sending real value to this contract.
           */
          contract PaymentSplitterUpgradeable is Initializable, ContextUpgradeable {
              event PayeeAdded(address account, uint256 shares);
              event PaymentReleased(address to, uint256 amount);
              event ERC20PaymentReleased(IERC20Upgradeable indexed token, address to, uint256 amount);
              event PaymentReceived(address from, uint256 amount);
              uint256 private _totalShares;
              uint256 private _totalReleased;
              mapping(address => uint256) private _shares;
              mapping(address => uint256) private _released;
              address[] private _payees;
              mapping(IERC20Upgradeable => uint256) private _erc20TotalReleased;
              mapping(IERC20Upgradeable => mapping(address => uint256)) private _erc20Released;
              /**
               * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
               * the matching position in the `shares` array.
               *
               * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
               * duplicates in `payees`.
               */
              function __PaymentSplitter_init(address[] memory payees, uint256[] memory shares_) internal onlyInitializing {
                  __PaymentSplitter_init_unchained(payees, shares_);
              }
              function __PaymentSplitter_init_unchained(address[] memory payees, uint256[] memory shares_) internal onlyInitializing {
                  require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
                  require(payees.length > 0, "PaymentSplitter: no payees");
                  for (uint256 i = 0; i < payees.length; i++) {
                      _addPayee(payees[i], shares_[i]);
                  }
              }
              /**
               * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
               * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
               * reliability of the events, and not the actual splitting of Ether.
               *
               * To learn more about this see the Solidity documentation for
               * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
               * functions].
               */
              receive() external payable virtual {
                  emit PaymentReceived(_msgSender(), msg.value);
              }
              /**
               * @dev Getter for the total shares held by payees.
               */
              function totalShares() public view returns (uint256) {
                  return _totalShares;
              }
              /**
               * @dev Getter for the total amount of Ether already released.
               */
              function totalReleased() public view returns (uint256) {
                  return _totalReleased;
              }
              /**
               * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
               * contract.
               */
              function totalReleased(IERC20Upgradeable token) public view returns (uint256) {
                  return _erc20TotalReleased[token];
              }
              /**
               * @dev Getter for the amount of shares held by an account.
               */
              function shares(address account) public view returns (uint256) {
                  return _shares[account];
              }
              /**
               * @dev Getter for the amount of Ether already released to a payee.
               */
              function released(address account) public view returns (uint256) {
                  return _released[account];
              }
              /**
               * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
               * IERC20 contract.
               */
              function released(IERC20Upgradeable token, address account) public view returns (uint256) {
                  return _erc20Released[token][account];
              }
              /**
               * @dev Getter for the address of the payee number `index`.
               */
              function payee(uint256 index) public view returns (address) {
                  return _payees[index];
              }
              /**
               * @dev Getter for the amount of payee's releasable Ether.
               */
              function releasable(address account) public view returns (uint256) {
                  uint256 totalReceived = address(this).balance + totalReleased();
                  return _pendingPayment(account, totalReceived, released(account));
              }
              /**
               * @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
               * IERC20 contract.
               */
              function releasable(IERC20Upgradeable token, address account) public view returns (uint256) {
                  uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
                  return _pendingPayment(account, totalReceived, released(token, account));
              }
              /**
               * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
               * total shares and their previous withdrawals.
               */
              function release(address payable account) public virtual {
                  require(_shares[account] > 0, "PaymentSplitter: account has no shares");
                  uint256 payment = releasable(account);
                  require(payment != 0, "PaymentSplitter: account is not due payment");
                  // _totalReleased is the sum of all values in _released.
                  // If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
                  _totalReleased += payment;
                  unchecked {
                      _released[account] += payment;
                  }
                  AddressUpgradeable.sendValue(account, payment);
                  emit PaymentReleased(account, payment);
              }
              /**
               * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
               * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
               * contract.
               */
              function release(IERC20Upgradeable token, address account) public virtual {
                  require(_shares[account] > 0, "PaymentSplitter: account has no shares");
                  uint256 payment = releasable(token, account);
                  require(payment != 0, "PaymentSplitter: account is not due payment");
                  // _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
                  // If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
                  // cannot overflow.
                  _erc20TotalReleased[token] += payment;
                  unchecked {
                      _erc20Released[token][account] += payment;
                  }
                  SafeERC20Upgradeable.safeTransfer(token, account, payment);
                  emit ERC20PaymentReleased(token, account, payment);
              }
              /**
               * @dev internal logic for computing the pending payment of an `account` given the token historical balances and
               * already released amounts.
               */
              function _pendingPayment(
                  address account,
                  uint256 totalReceived,
                  uint256 alreadyReleased
              ) private view returns (uint256) {
                  return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
              }
              /**
               * @dev Add a new payee to the contract.
               * @param account The address of the payee to add.
               * @param shares_ The number of shares owned by the payee.
               */
              function _addPayee(address account, uint256 shares_) private {
                  require(account != address(0), "PaymentSplitter: account is the zero address");
                  require(shares_ > 0, "PaymentSplitter: shares are 0");
                  require(_shares[account] == 0, "PaymentSplitter: account already has shares");
                  _payees.push(account);
                  _shares[account] = shares_;
                  _totalShares = _totalShares + shares_;
                  emit PayeeAdded(account, shares_);
              }
              /**
               * @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[43] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
          pragma solidity ^0.8.0;
          import "../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev Interface for the NFT Royalty Standard.
           *
           * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
           * support for royalty payments across all NFT marketplaces and ecosystem participants.
           *
           * _Available since v4.5._
           */
          interface IERC2981Upgradeable is IERC165Upgradeable {
              /**
               * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
               * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
               */
              function royaltyInfo(uint256 tokenId, uint256 salePrice)
                  external
                  view
                  returns (address receiver, uint256 royaltyAmount);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
          pragma solidity ^0.8.2;
          import "../../utils/AddressUpgradeable.sol";
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               * @custom:oz-retyped-from bool
               */
              uint8 private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint8 version);
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts.
               *
               * 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 Internal function that returns the initialized version. Returns `_initialized`
               */
              function _getInitializedVersion() internal view returns (uint8) {
                  return _initialized;
              }
              /**
               * @dev Internal function that returns the initialized version. Returns `_initializing`
               */
              function _isInitializing() internal view returns (bool) {
                  return _initializing;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
          pragma solidity ^0.8.0;
          import "../../interfaces/IERC2981Upgradeable.sol";
          import "../../utils/introspection/ERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
           *
           * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
           * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
           *
           * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
           * fee is specified in basis points by default.
           *
           * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
           * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
           * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
           *
           * _Available since v4.5._
           */
          abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
              function __ERC2981_init() internal onlyInitializing {
              }
              function __ERC2981_init_unchained() internal onlyInitializing {
              }
              struct RoyaltyInfo {
                  address receiver;
                  uint96 royaltyFraction;
              }
              RoyaltyInfo private _defaultRoyaltyInfo;
              mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
                  return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
              }
              /**
               * @inheritdoc IERC2981Upgradeable
               */
              function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
                  RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
                  if (royalty.receiver == address(0)) {
                      royalty = _defaultRoyaltyInfo;
                  }
                  uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
                  return (royalty.receiver, royaltyAmount);
              }
              /**
               * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
               * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
               * override.
               */
              function _feeDenominator() internal pure virtual returns (uint96) {
                  return 10000;
              }
              /**
               * @dev Sets the royalty information that all ids in this contract will default to.
               *
               * Requirements:
               *
               * - `receiver` cannot be the zero address.
               * - `feeNumerator` cannot be greater than the fee denominator.
               */
              function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
                  require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                  require(receiver != address(0), "ERC2981: invalid receiver");
                  _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
              }
              /**
               * @dev Removes default royalty information.
               */
              function _deleteDefaultRoyalty() internal virtual {
                  delete _defaultRoyaltyInfo;
              }
              /**
               * @dev Sets the royalty information for a specific token id, overriding the global default.
               *
               * Requirements:
               *
               * - `receiver` cannot be the zero address.
               * - `feeNumerator` cannot be greater than the fee denominator.
               */
              function _setTokenRoyalty(
                  uint256 tokenId,
                  address receiver,
                  uint96 feeNumerator
              ) internal virtual {
                  require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                  require(receiver != address(0), "ERC2981: Invalid parameters");
                  _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
              }
              /**
               * @dev Resets royalty information for the token id back to the global default.
               */
              function _resetTokenRoyalty(uint256 tokenId) internal virtual {
                  delete _tokenRoyaltyInfo[tokenId];
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[48] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol)
          pragma solidity ^0.8.0;
          import "./IERC1155Upgradeable.sol";
          import "./IERC1155ReceiverUpgradeable.sol";
          import "./extensions/IERC1155MetadataURIUpgradeable.sol";
          import "../../utils/AddressUpgradeable.sol";
          import "../../utils/ContextUpgradeable.sol";
          import "../../utils/introspection/ERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the basic standard multi-token.
           * See https://eips.ethereum.org/EIPS/eip-1155
           * Originally based on code by Enjin: https://github.com/enjin/erc-1155
           *
           * _Available since v3.1._
           */
          contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
              using AddressUpgradeable for address;
              // Mapping from token ID to account balances
              mapping(uint256 => mapping(address => uint256)) private _balances;
              // Mapping from account to operator approvals
              mapping(address => mapping(address => bool)) private _operatorApprovals;
              // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
              string private _uri;
              /**
               * @dev See {_setURI}.
               */
              function __ERC1155_init(string memory uri_) internal onlyInitializing {
                  __ERC1155_init_unchained(uri_);
              }
              function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
                  _setURI(uri_);
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                  return
                      interfaceId == type(IERC1155Upgradeable).interfaceId ||
                      interfaceId == type(IERC1155MetadataURIUpgradeable).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
              /**
               * @dev See {IERC1155MetadataURI-uri}.
               *
               * This implementation returns the same URI for *all* token types. It relies
               * on the token type ID substitution mechanism
               * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
               *
               * Clients calling this function must replace the `\\{id\\}` substring with the
               * actual token type ID.
               */
              function uri(uint256) public view virtual override returns (string memory) {
                  return _uri;
              }
              /**
               * @dev See {IERC1155-balanceOf}.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
                  require(account != address(0), "ERC1155: address zero is not a valid owner");
                  return _balances[id][account];
              }
              /**
               * @dev See {IERC1155-balanceOfBatch}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
                  public
                  view
                  virtual
                  override
                  returns (uint256[] memory)
              {
                  require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
                  uint256[] memory batchBalances = new uint256[](accounts.length);
                  for (uint256 i = 0; i < accounts.length; ++i) {
                      batchBalances[i] = balanceOf(accounts[i], ids[i]);
                  }
                  return batchBalances;
              }
              /**
               * @dev See {IERC1155-setApprovalForAll}.
               */
              function setApprovalForAll(address operator, bool approved) public virtual override {
                  _setApprovalForAll(_msgSender(), operator, approved);
              }
              /**
               * @dev See {IERC1155-isApprovedForAll}.
               */
              function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
                  return _operatorApprovals[account][operator];
              }
              /**
               * @dev See {IERC1155-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) public virtual override {
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: caller is not token owner or approved"
                  );
                  _safeTransferFrom(from, to, id, amount, data);
              }
              /**
               * @dev See {IERC1155-safeBatchTransferFrom}.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) public virtual override {
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: caller is not token owner or approved"
                  );
                  _safeBatchTransferFrom(from, to, ids, amounts, data);
              }
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function _safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  _balances[id][to] += amount;
                  emit TransferSingle(operator, from, to, id, amount);
                  _afterTokenTransfer(operator, from, to, ids, amounts, data);
                  _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function _safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  for (uint256 i = 0; i < ids.length; ++i) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 fromBalance = _balances[id][from];
                      require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                      unchecked {
                          _balances[id][from] = fromBalance - amount;
                      }
                      _balances[id][to] += amount;
                  }
                  emit TransferBatch(operator, from, to, ids, amounts);
                  _afterTokenTransfer(operator, from, to, ids, amounts, data);
                  _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
              }
              /**
               * @dev Sets a new URI for all token types, by relying on the token type ID
               * substitution mechanism
               * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
               *
               * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
               * URI or any of the amounts in the JSON file at said URI will be replaced by
               * clients with the token type ID.
               *
               * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
               * interpreted by clients as
               * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
               * for token type ID 0x4cce0.
               *
               * See {uri}.
               *
               * Because these URIs cannot be meaningfully represented by the {URI} event,
               * this function emits no events.
               */
              function _setURI(string memory newuri) internal virtual {
                  _uri = newuri;
              }
              /**
               * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function _mint(
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: mint to the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _balances[id][to] += amount;
                  emit TransferSingle(operator, address(0), to, id, amount);
                  _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function _mintBatch(
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: mint to the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                  for (uint256 i = 0; i < ids.length; i++) {
                      _balances[ids[i]][to] += amounts[i];
                  }
                  emit TransferBatch(operator, address(0), to, ids, amounts);
                  _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
              }
              /**
               * @dev Destroys `amount` tokens of token type `id` from `from`
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `from` must have at least `amount` tokens of token type `id`.
               */
              function _burn(
                  address from,
                  uint256 id,
                  uint256 amount
              ) internal virtual {
                  require(from != address(0), "ERC1155: burn from the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  emit TransferSingle(operator, from, address(0), id, amount);
                  _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               */
              function _burnBatch(
                  address from,
                  uint256[] memory ids,
                  uint256[] memory amounts
              ) internal virtual {
                  require(from != address(0), "ERC1155: burn from the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                  for (uint256 i = 0; i < ids.length; i++) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 fromBalance = _balances[id][from];
                      require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                      unchecked {
                          _balances[id][from] = fromBalance - amount;
                      }
                  }
                  emit TransferBatch(operator, from, address(0), ids, amounts);
                  _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
              }
              /**
               * @dev Approve `operator` to operate on all of `owner` tokens
               *
               * Emits an {ApprovalForAll} event.
               */
              function _setApprovalForAll(
                  address owner,
                  address operator,
                  bool approved
              ) internal virtual {
                  require(owner != operator, "ERC1155: setting approval status for self");
                  _operatorApprovals[owner][operator] = approved;
                  emit ApprovalForAll(owner, operator, approved);
              }
              /**
               * @dev Hook that is called before any token transfer. This includes minting
               * and burning, as well as batched variants.
               *
               * The same hook is called on both single and batched variants. For single
               * transfers, the length of the `ids` and `amounts` arrays will be 1.
               *
               * Calling conditions (for each `id` and `amount` pair):
               *
               * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * of token type `id` will be  transferred to `to`.
               * - When `from` is zero, `amount` tokens of token type `id` will be minted
               * for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
               * will be burned.
               * - `from` and `to` are never both zero.
               * - `ids` and `amounts` have the same, non-zero length.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {}
              /**
               * @dev Hook that is called after any token transfer. This includes minting
               * and burning, as well as batched variants.
               *
               * The same hook is called on both single and batched variants. For single
               * transfers, the length of the `id` and `amount` arrays will be 1.
               *
               * Calling conditions (for each `id` and `amount` pair):
               *
               * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * of token type `id` will be  transferred to `to`.
               * - When `from` is zero, `amount` tokens of token type `id` will be minted
               * for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
               * will be burned.
               * - `from` and `to` are never both zero.
               * - `ids` and `amounts` have the same, non-zero length.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _afterTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {}
              function _doSafeTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) private {
                  if (to.isContract()) {
                      try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                          if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                      }
                  }
              }
              function _doSafeBatchTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) private {
                  if (to.isContract()) {
                      try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                          bytes4 response
                      ) {
                          if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                      }
                  }
              }
              function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                  uint256[] memory array = new uint256[](1);
                  array[0] = element;
                  return array;
              }
              /**
               * @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[47] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)
          pragma solidity ^0.8.0;
          import "../ERC1155Upgradeable.sol";
          import "../../../proxy/utils/Initializable.sol";
          /**
           * @dev Extension of ERC1155 that adds tracking of total supply per id.
           *
           * Useful for scenarios where Fungible and Non-fungible tokens have to be
           * clearly identified. Note: While a totalSupply of 1 might mean the
           * corresponding is an NFT, there is no guarantees that no other token with the
           * same id are not going to be minted.
           */
          abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable {
              function __ERC1155Supply_init() internal onlyInitializing {
              }
              function __ERC1155Supply_init_unchained() internal onlyInitializing {
              }
              mapping(uint256 => uint256) private _totalSupply;
              /**
               * @dev Total amount of tokens in with a given id.
               */
              function totalSupply(uint256 id) public view virtual returns (uint256) {
                  return _totalSupply[id];
              }
              /**
               * @dev Indicates whether any token exist with a given id, or not.
               */
              function exists(uint256 id) public view virtual returns (bool) {
                  return ERC1155SupplyUpgradeable.totalSupply(id) > 0;
              }
              /**
               * @dev See {ERC1155-_beforeTokenTransfer}.
               */
              function _beforeTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual override {
                  super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  if (from == address(0)) {
                      for (uint256 i = 0; i < ids.length; ++i) {
                          _totalSupply[ids[i]] += amounts[i];
                      }
                  }
                  if (to == address(0)) {
                      for (uint256 i = 0; i < ids.length; ++i) {
                          uint256 id = ids[i];
                          uint256 amount = amounts[i];
                          uint256 supply = _totalSupply[id];
                          require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                          unchecked {
                              _totalSupply[id] = supply - amount;
                          }
                      }
                  }
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
          pragma solidity ^0.8.0;
          import "../IERC1155Upgradeable.sol";
          /**
           * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
           * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
              /**
               * @dev Returns the URI for token type `id`.
               *
               * If the `\\{id\\}` substring is present in the URI, it must be replaced by
               * clients with the actual token type ID.
               */
              function uri(uint256 id) external view returns (string memory);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev _Available since v3.1._
           */
          interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
              /**
               * @dev Handles the receipt of a single ERC1155 token type. This function is
               * called at the end of a `safeTransferFrom` after the balance has been updated.
               *
               * NOTE: To accept the transfer, this must return
               * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
               * (i.e. 0xf23a6e61, or its own function selector).
               *
               * @param operator The address which initiated the transfer (i.e. msg.sender)
               * @param from The address which previously owned the token
               * @param id The ID of the token being transferred
               * @param value The amount of tokens being transferred
               * @param data Additional data with no specified format
               * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
               */
              function onERC1155Received(
                  address operator,
                  address from,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              ) external returns (bytes4);
              /**
               * @dev Handles the receipt of a multiple ERC1155 token types. This function
               * is called at the end of a `safeBatchTransferFrom` after the balances have
               * been updated.
               *
               * NOTE: To accept the transfer(s), this must return
               * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
               * (i.e. 0xbc197c81, or its own function selector).
               *
               * @param operator The address which initiated the batch transfer (i.e. msg.sender)
               * @param from The address which previously owned the token
               * @param ids An array containing ids of each token being transferred (order and length must match values array)
               * @param values An array containing amounts of each token being transferred (order and length must match ids array)
               * @param data Additional data with no specified format
               * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
               */
              function onERC1155BatchReceived(
                  address operator,
                  address from,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              ) external returns (bytes4);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev Required interface of an ERC1155 compliant contract, as defined in the
           * https://eips.ethereum.org/EIPS/eip-1155[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155Upgradeable is IERC165Upgradeable {
              /**
               * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
               */
              event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
              /**
               * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
               * transfers.
               */
              event TransferBatch(
                  address indexed operator,
                  address indexed from,
                  address indexed to,
                  uint256[] ids,
                  uint256[] values
              );
              /**
               * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
               * `approved`.
               */
              event ApprovalForAll(address indexed account, address indexed operator, bool approved);
              /**
               * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
               *
               * If an {URI} event was emitted for `id`, the standard
               * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
               * returned by {IERC1155MetadataURI-uri}.
               */
              event URI(string value, uint256 indexed id);
              /**
               * @dev Returns the amount of tokens of token type `id` owned by `account`.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) external view returns (uint256);
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                  external
                  view
                  returns (uint256[] memory);
              /**
               * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
               *
               * Emits an {ApprovalForAll} event.
               *
               * Requirements:
               *
               * - `operator` cannot be the caller.
               */
              function setApprovalForAll(address operator, bool approved) external;
              /**
               * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address account, address operator) external view returns (bool);
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes calldata data
              ) external;
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] calldata ids,
                  uint256[] calldata amounts,
                  bytes calldata data
              ) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
           *
           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
           * need to send a transaction, and thus is not required to hold Ether at all.
           */
          interface IERC20PermitUpgradeable {
              /**
               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
               * given ``owner``'s signed approval.
               *
               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
               * ordering also apply here.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `deadline` must be a timestamp in the future.
               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
               * over the EIP712-formatted function arguments.
               * - the signature must use ``owner``'s current nonce (see {nonces}).
               *
               * For more information on the signature format, see the
               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
               * section].
               */
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) external;
              /**
               * @dev Returns the current nonce for `owner`. This value must be
               * included whenever a signature is generated for {permit}.
               *
               * Every successful call to {permit} increases ``owner``'s nonce by one. This
               * prevents a signature from being used multiple times.
               */
              function nonces(address owner) external view returns (uint256);
              /**
               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
               */
              // solhint-disable-next-line func-name-mixedcase
              function DOMAIN_SEPARATOR() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20Upgradeable {
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `from` to `to` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 amount
              ) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
          pragma solidity ^0.8.0;
          import "../IERC20Upgradeable.sol";
          import "../extensions/draft-IERC20PermitUpgradeable.sol";
          import "../../../utils/AddressUpgradeable.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20Upgradeable {
              using AddressUpgradeable for address;
              function safeTransfer(
                  IERC20Upgradeable token,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(
                  IERC20Upgradeable token,
                  address from,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  require(
                      (value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  uint256 newAllowance = token.allowance(address(this), spender) + value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  unchecked {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                      uint256 newAllowance = oldAllowance - value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
              }
              function safePermit(
                  IERC20PermitUpgradeable token,
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal {
                  uint256 nonceBefore = token.nonces(owner);
                  token.permit(owner, spender, value, deadline, v, r, s);
                  uint256 nonceAfter = token.nonces(owner);
                  require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) {
                      // Return data is optional
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
          pragma solidity ^0.8.1;
          /**
           * @dev Collection of functions related to the address type
           */
          library AddressUpgradeable {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on `isContract` to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
                  return account.code.length > 0;
              }
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
                  (bool success, ) = recipient.call{value: amount}("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain `call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
              /**
               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
               *
               * _Available since v4.8._
               */
              function verifyCallResultFromTarget(
                  address target,
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  if (success) {
                      if (returndata.length == 0) {
                          // only check isContract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          require(isContract(target), "Address: call to non-contract");
                      }
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
              /**
               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason or using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
              function _revert(bytes memory returndata, string memory errorMessage) private pure {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract ContextUpgradeable is Initializable {
              function __Context_init() internal onlyInitializing {
              }
              function __Context_init_unchained() internal onlyInitializing {
              }
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          pragma solidity ^0.8.0;
          import "./IERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
              function __ERC165_init() internal onlyInitializing {
              }
              function __ERC165_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165Upgradeable).interfaceId;
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165Upgradeable {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
          pragma solidity ^0.8.0;
          import "./IAccessControl.sol";
          import "../utils/Context.sol";
          import "../utils/Strings.sol";
          import "../utils/introspection/ERC165.sol";
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms. This is a lightweight version that doesn't allow enumerating role
           * members except through off-chain means by accessing the contract event logs. Some
           * applications may benefit from on-chain enumerability, for those cases see
           * {AccessControlEnumerable}.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it.
           */
          abstract contract AccessControl is Context, IAccessControl, ERC165 {
              struct RoleData {
                  mapping(address => bool) members;
                  bytes32 adminRole;
              }
              mapping(bytes32 => RoleData) private _roles;
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
              /**
               * @dev Modifier that checks that an account has a specific role. Reverts
               * with a standardized message including the required role.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               *
               * _Available since v4.1._
               */
              modifier onlyRole(bytes32 role) {
                  _checkRole(role);
                  _;
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
              }
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                  return _roles[role].members[account];
              }
              /**
               * @dev Revert with a standard message if `_msgSender()` is missing `role`.
               * Overriding this function changes the behavior of the {onlyRole} modifier.
               *
               * Format of the revert message is described in {_checkRole}.
               *
               * _Available since v4.6._
               */
              function _checkRole(bytes32 role) internal view virtual {
                  _checkRole(role, _msgSender());
              }
              /**
               * @dev Revert with a standard message if `account` is missing `role`.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               */
              function _checkRole(bytes32 role, address account) internal view virtual {
                  if (!hasRole(role, account)) {
                      revert(
                          string(
                              abi.encodePacked(
                                  "AccessControl: account ",
                                  Strings.toHexString(account),
                                  " is missing role ",
                                  Strings.toHexString(uint256(role), 32)
                              )
                          )
                      );
                  }
              }
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                  return _roles[role].adminRole;
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleGranted} event.
               */
              function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _grantRole(role, account);
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleRevoked} event.
               */
              function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _revokeRole(role, account);
              }
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been revoked `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               *
               * May emit a {RoleRevoked} event.
               */
              function renounceRole(bytes32 role, address account) public virtual override {
                  require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                  _revokeRole(role, account);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event. Note that unlike {grantRole}, this function doesn't perform any
               * checks on the calling account.
               *
               * May emit a {RoleGranted} event.
               *
               * [WARNING]
               * ====
               * This function should only be called from the constructor when setting
               * up the initial roles for the system.
               *
               * Using this function in any other way is effectively circumventing the admin
               * system imposed by {AccessControl}.
               * ====
               *
               * NOTE: This function is deprecated in favor of {_grantRole}.
               */
              function _setupRole(bytes32 role, address account) internal virtual {
                  _grantRole(role, account);
              }
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  bytes32 previousAdminRole = getRoleAdmin(role);
                  _roles[role].adminRole = adminRole;
                  emit RoleAdminChanged(role, previousAdminRole, adminRole);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleGranted} event.
               */
              function _grantRole(bytes32 role, address account) internal virtual {
                  if (!hasRole(role, account)) {
                      _roles[role].members[account] = true;
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleRevoked} event.
               */
              function _revokeRole(bytes32 role, address account) internal virtual {
                  if (hasRole(role, account)) {
                      _roles[role].members[account] = false;
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev External interface of AccessControl declared to support ERC165 detection.
           */
          interface IAccessControl {
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               *
               * _Available since v3.1._
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call, an admin role
               * bearer except when using {AccessControl-_setupRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) external view returns (bool);
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {AccessControl-_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) external view returns (bytes32);
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               */
              function renounceRole(bytes32 role, address account) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
           * deploying minimal proxy contracts, also known as "clones".
           *
           * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
           * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
           *
           * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
           * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
           * deterministic method.
           *
           * _Available since v3.4._
           */
          library Clones {
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create opcode, which should never revert.
               */
              function clone(address implementation) internal returns (address instance) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create(0, 0x09, 0x37)
                  }
                  require(instance != address(0), "ERC1167: create failed");
              }
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create2 opcode and a `salt` to deterministically deploy
               * the clone. Using the same `implementation` and `salt` multiple time will revert, since
               * the clones cannot be deployed twice at the same address.
               */
              function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create2(0, 0x09, 0x37, salt)
                  }
                  require(instance != address(0), "ERC1167: create2 failed");
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(
                  address implementation,
                  bytes32 salt,
                  address deployer
              ) internal pure returns (address predicted) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let ptr := mload(0x40)
                      mstore(add(ptr, 0x38), deployer)
                      mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                      mstore(add(ptr, 0x14), implementation)
                      mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                      mstore(add(ptr, 0x58), salt)
                      mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                      predicted := keccak256(add(ptr, 0x43), 0x55)
                  }
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(address implementation, bytes32 salt)
                  internal
                  view
                  returns (address predicted)
              {
                  return predictDeterministicAddress(implementation, salt, address(this));
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev These functions deal with verification of Merkle Tree proofs.
           *
           * The tree and the proofs can be generated using our
           * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
           * You will find a quickstart guide in the readme.
           *
           * WARNING: You should avoid using leaf values that are 64 bytes long prior to
           * hashing, or use a hash function other than keccak256 for hashing leaves.
           * This is because the concatenation of a sorted pair of internal nodes in
           * the merkle tree could be reinterpreted as a leaf value.
           * OpenZeppelin's JavaScript library generates merkle trees that are safe
           * against this attack out of the box.
           */
          library MerkleProof {
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               */
              function verify(
                  bytes32[] memory proof,
                  bytes32 root,
                  bytes32 leaf
              ) internal pure returns (bool) {
                  return processProof(proof, leaf) == root;
              }
              /**
               * @dev Calldata version of {verify}
               *
               * _Available since v4.7._
               */
              function verifyCalldata(
                  bytes32[] calldata proof,
                  bytes32 root,
                  bytes32 leaf
              ) internal pure returns (bool) {
                  return processProofCalldata(proof, leaf) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leafs & pre-images are assumed to be sorted.
               *
               * _Available since v4.4._
               */
              function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Calldata version of {processProof}
               *
               * _Available since v4.7._
               */
              function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerify(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProof(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Calldata version of {multiProofVerify}
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerifyCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProofCalldata(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * _Available since v4.7._
               */
              function processMultiProof(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 totalHashes = proofFlags.length;
                  // Check proof validity.
                  require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
                  if (totalHashes > 0) {
                      return hashes[totalHashes - 1];
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              /**
               * @dev Calldata version of {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function processMultiProofCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 totalHashes = proofFlags.length;
                  // Check proof validity.
                  require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
                  if (totalHashes > 0) {
                      return hashes[totalHashes - 1];
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                  return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
              }
              function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x00, a)
                      mstore(0x20, b)
                      value := keccak256(0x00, 0x40)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          pragma solidity ^0.8.0;
          import "./IERC165.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165 is IERC165 {
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165).interfaceId;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Standard math utilities missing in the Solidity language.
           */
          library Math {
              enum Rounding {
                  Down, // Toward negative infinity
                  Up, // Toward infinity
                  Zero // Toward zero
              }
              /**
               * @dev Returns the largest of two numbers.
               */
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
              /**
               * @dev Returns the smallest of two numbers.
               */
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
              /**
               * @dev Returns the average of two numbers. The result is rounded towards
               * zero.
               */
              function average(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b) / 2 can overflow.
                  return (a & b) + (a ^ b) / 2;
              }
              /**
               * @dev Returns the ceiling of the division of two numbers.
               *
               * This differs from standard division with `/` in that it rounds up instead
               * of rounding down.
               */
              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b - 1) / b can overflow on addition, so we distribute.
                  return a == 0 ? 0 : (a - 1) / b + 1;
              }
              /**
               * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
               * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
               * with further edits by Uniswap Labs also under MIT license.
               */
              function mulDiv(
                  uint256 x,
                  uint256 y,
                  uint256 denominator
              ) internal pure returns (uint256 result) {
                  unchecked {
                      // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                      // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                      // variables such that product = prod1 * 2^256 + prod0.
                      uint256 prod0; // Least significant 256 bits of the product
                      uint256 prod1; // Most significant 256 bits of the product
                      assembly {
                          let mm := mulmod(x, y, not(0))
                          prod0 := mul(x, y)
                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                      }
                      // Handle non-overflow cases, 256 by 256 division.
                      if (prod1 == 0) {
                          return prod0 / denominator;
                      }
                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                      require(denominator > prod1);
                      ///////////////////////////////////////////////
                      // 512 by 256 division.
                      ///////////////////////////////////////////////
                      // Make division exact by subtracting the remainder from [prod1 prod0].
                      uint256 remainder;
                      assembly {
                          // Compute remainder using mulmod.
                          remainder := mulmod(x, y, denominator)
                          // Subtract 256 bit number from 512 bit number.
                          prod1 := sub(prod1, gt(remainder, prod0))
                          prod0 := sub(prod0, remainder)
                      }
                      // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                      // See https://cs.stackexchange.com/q/138556/92363.
                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                      uint256 twos = denominator & (~denominator + 1);
                      assembly {
                          // Divide denominator by twos.
                          denominator := div(denominator, twos)
                          // Divide [prod1 prod0] by twos.
                          prod0 := div(prod0, twos)
                          // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                          twos := add(div(sub(0, twos), twos), 1)
                      }
                      // Shift in bits from prod1 into prod0.
                      prod0 |= prod1 * twos;
                      // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                      // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                      // four bits. That is, denominator * inv = 1 mod 2^4.
                      uint256 inverse = (3 * denominator) ^ 2;
                      // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                      // in modular arithmetic, doubling the correct bits in each step.
                      inverse *= 2 - denominator * inverse; // inverse mod 2^8
                      inverse *= 2 - denominator * inverse; // inverse mod 2^16
                      inverse *= 2 - denominator * inverse; // inverse mod 2^32
                      inverse *= 2 - denominator * inverse; // inverse mod 2^64
                      inverse *= 2 - denominator * inverse; // inverse mod 2^128
                      inverse *= 2 - denominator * inverse; // inverse mod 2^256
                      // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                      // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                      // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                      // is no longer required.
                      result = prod0 * inverse;
                      return result;
                  }
              }
              /**
               * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
               */
              function mulDiv(
                  uint256 x,
                  uint256 y,
                  uint256 denominator,
                  Rounding rounding
              ) internal pure returns (uint256) {
                  uint256 result = mulDiv(x, y, denominator);
                  if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                      result += 1;
                  }
                  return result;
              }
              /**
               * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
               *
               * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
               */
              function sqrt(uint256 a) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
                  // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                  //
                  // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                  // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                  //
                  // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                  // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                  // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                  //
                  // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                  uint256 result = 1 << (log2(a) >> 1);
                  // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                  // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                  // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                  // into the expected uint128 result.
                  unchecked {
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      return min(result, a / result);
                  }
              }
              /**
               * @notice Calculates sqrt(a), following the selected rounding direction.
               */
              function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = sqrt(a);
                      return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 2, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 128;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 64;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 32;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 16;
                      }
                      if (value >> 8 > 0) {
                          value >>= 8;
                          result += 8;
                      }
                      if (value >> 4 > 0) {
                          value >>= 4;
                          result += 4;
                      }
                      if (value >> 2 > 0) {
                          value >>= 2;
                          result += 2;
                      }
                      if (value >> 1 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log2(value);
                      return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 10, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >= 10**64) {
                          value /= 10**64;
                          result += 64;
                      }
                      if (value >= 10**32) {
                          value /= 10**32;
                          result += 32;
                      }
                      if (value >= 10**16) {
                          value /= 10**16;
                          result += 16;
                      }
                      if (value >= 10**8) {
                          value /= 10**8;
                          result += 8;
                      }
                      if (value >= 10**4) {
                          value /= 10**4;
                          result += 4;
                      }
                      if (value >= 10**2) {
                          value /= 10**2;
                          result += 2;
                      }
                      if (value >= 10**1) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log10(value);
                      return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 256, rounded down, of a positive value.
               * Returns 0 if given 0.
               *
               * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
               */
              function log256(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 16;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 8;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 4;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 2;
                      }
                      if (value >> 8 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log256(value);
                      return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
          pragma solidity ^0.8.0;
          import "./math/Math.sol";
          /**
           * @dev String operations.
           */
          library Strings {
              bytes16 private constant _SYMBOLS = "0123456789abcdef";
              uint8 private constant _ADDRESS_LENGTH = 20;
              /**
               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      uint256 length = Math.log10(value) + 1;
                      string memory buffer = new string(length);
                      uint256 ptr;
                      /// @solidity memory-safe-assembly
                      assembly {
                          ptr := add(buffer, add(32, length))
                      }
                      while (true) {
                          ptr--;
                          /// @solidity memory-safe-assembly
                          assembly {
                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                          }
                          value /= 10;
                          if (value == 0) break;
                      }
                      return buffer;
                  }
              }
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
               */
              function toHexString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      return toHexString(value, Math.log256(value) + 1);
                  }
              }
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
               */
              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                  bytes memory buffer = new bytes(2 * length + 2);
                  buffer[0] = "0";
                  buffer[1] = "x";
                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                      buffer[i] = _SYMBOLS[value & 0xf];
                      value >>= 4;
                  }
                  require(value == 0, "Strings: hex length insufficient");
                  return string(buffer);
              }
              /**
               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
               */
              function toHexString(address addr) internal pure returns (string memory) {
                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
          import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
          import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
          import "operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol";
          import "./ICommon.sol";
          struct TokenSettings {
              /// @dev total number of tokens that can be minted
              uint32 maxSupply;
              /// @dev total number of tokens that can be minted per wallet
              uint32 maxPerWallet;
              /// @dev tracks the total amount that have been minted
              uint32 amountMinted;
              /// @dev merkle root associated with claiming the token, otherwise bytes32(0)
              bytes32 merkleRoot;
              /// @dev timestamp of when the token can be minted
              uint32 mintStart;
              /// @dev timestamp of when the token can no longer be minted
              uint32 mintEnd;
              /// @dev price for the phase
              uint256 price;
              /// @dev uuid of the token within the Bueno ecosystem
              string uuid;
              /// @dev optional revenue splitting settings
              PaymentSplitterSettings paymentSplitterSettings;
          }
          struct TokenData {
              TokenSettings settings;
              uint256 index;
          }
          error TokenSettingsLocked();
          error TokenAlreadyExists();
          error InvalidPaymentSplitterSettings();
          error TooManyTokens();
          error InvalidToken();
          error MintNotActive();
          error InvalidMintDates();
          /// @author Bueno.art
          /// @title ERC-1155 "Drops" contract (v3)
          contract Bueno1155Drop is
              ERC1155SupplyUpgradeable,
              OwnableUpgradeable,
              ERC2981Upgradeable,
              OperatorFiltererUpgradeable
          {
              string public name;
              string public symbol;
              uint256 private _currentTokenId;
              bool private allowBurning;
              /// @dev maps the token ID (eg 1, 2 ...n) to the token's minting settings
              mapping(uint256 => TokenSettings) private _tokens;
              /// @dev track how many mints a particular wallet has made for a given token
              mapping(uint256 => mapping(address => uint64))
                  private _mintBalanceByTokenId;
              /// @dev track how much revenue each payee has earned
              mapping(address => uint256) private _revenueByAddress;
              /// @dev track how much revenue has been released to each address
              mapping(address => uint256) private _released;
              /// @dev track how much revenue has been released in total
              uint256 private _totalReleased;
              /// @dev "fallback" payment splitter settings in case token-level settings aren't specified
              PaymentSplitterSettings private _fallbackPaymentSplitterSettings;
              event RoyaltyUpdated(address royaltyAddress, uint96 royaltyAmount);
              event TokenRoyaltyUpdated(
                  uint256 tokenId,
                  address royaltyAddress,
                  uint96 royaltyAmount
              );
              event TokenCreated(string indexed uuid, uint256 indexed tokenId);
              event BurnStatusChanged(bool burnActive);
              event TokensAirdropped(uint256 numRecipients, uint256 numTokens);
              event TokenBurned(address indexed owner, uint256 tokenId, uint256 amount);
              event PaymentReleased(address to, uint256 amount);
              event TokenSettingsUpdated(uint256 tokenId);
              event RevenueSettingsUpdated(uint256 tokenId);
              event FallbackRevenueSettingsUpdated();
              event TokensMinted(address indexed to, uint256 tokenId, uint256 quantity);
              event TokenSupplyCapped(uint256 tokenId, uint256 maxSupply);
              /// @custom:oz-upgrades-unsafe-allow constructor
              constructor() {
                  _disableInitializers();
              }
              function initialize(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  TokenSettings[] calldata _tokenSettings,
                  RoyaltySettings calldata _royaltySettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  bool _allowBurning,
                  address _deployer,
                  address _operatorFilter
              ) public initializer {
                  __ERC1155_init(_baseUri);
                  __Ownable_init();
                  uint256 numTokens = _tokenSettings.length;
                  // set a reasonable maximum here so we don't run out of gas
                  if (numTokens > 100) {
                      revert TooManyTokens();
                  }
                  // verify fallback (contract-level) payment splitter settings
                  _verifyPaymentSplitterSettings(_paymentSplitterSettings);
                  for (uint256 i = 0; i < numTokens; ) {
                      // verify token-level payment splitter settings, if present
                      if (_tokenSettings[i].paymentSplitterSettings.payees.length > 0) {
                          _verifyPaymentSplitterSettings(
                              _tokenSettings[i].paymentSplitterSettings
                          );
                      }
                      _verifyMintingTime(
                          _tokenSettings[i].mintStart,
                          _tokenSettings[i].mintEnd
                      );
                      _tokens[i] = _tokenSettings[i];
                      // this value should always be 0 for new tokens
                      _tokens[i].amountMinted = 0;
                      emit TokenCreated(_tokenSettings[i].uuid, i);
                      // numTokens has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  _currentTokenId = numTokens;
                  _fallbackPaymentSplitterSettings = _paymentSplitterSettings;
                  name = _name;
                  symbol = _symbol;
                  allowBurning = _allowBurning;
                  _setDefaultRoyalty(
                      _royaltySettings.royaltyAddress,
                      _royaltySettings.royaltyAmount
                  );
                  _transferOwnership(_deployer);
                  OperatorFiltererUpgradeable.__OperatorFilterer_init(
                      _operatorFilter,
                      _operatorFilter == address(0) ? false : true // only subscribe if a filter is provided
                  );
              }
              /*//////////////////////////////////////////////////////////////
                                     CREATOR FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Create a new token to be minted with the provided settings.
               */
              function createDropToken(
                  TokenSettings calldata settings
              ) external onlyOwner {
                  if (settings.paymentSplitterSettings.payees.length > 0) {
                      _verifyPaymentSplitterSettings(settings.paymentSplitterSettings);
                  }
                  _verifyMintingTime(settings.mintStart, settings.mintEnd);
                  uint256 id = _currentTokenId;
                  _tokens[id] = settings;
                  // this value should always be 0 for new tokens
                  _tokens[id].amountMinted = 0;
                  ++_currentTokenId;
                  emit TokenCreated(settings.uuid, id);
              }
              /**
               * @notice Create multiple tokens to be minted with the provided settings.
               */
              function createDropTokens(
                  TokenSettings[] calldata tokenSettings
              ) external onlyOwner {
                  uint256 numTokens = tokenSettings.length;
                  uint256 currentTokenId = _currentTokenId;
                  for (uint256 i = 0; i < numTokens; ) {
                      if (tokenSettings[i].paymentSplitterSettings.payees.length > 0) {
                          _verifyPaymentSplitterSettings(
                              tokenSettings[i].paymentSplitterSettings
                          );
                      }
                      TokenSettings memory settings = tokenSettings[i];
                      _verifyMintingTime(settings.mintStart, settings.mintEnd);
                      uint256 id = currentTokenId;
                      // this value should always be 0 for new tokens
                      settings.amountMinted = 0;
                      _tokens[id] = settings;
                      ++currentTokenId;
                      // numTokens has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                      emit TokenCreated(settings.uuid, id);
                  }
                  _currentTokenId = currentTokenId;
              }
              /**
               * @notice Update the settings for a token. Certain settings cannot be changed once a token has been minted.
               */
              function updateTokenSettingsByIndex(
                  uint256 id,
                  TokenSettings calldata settings
              ) external onlyOwner {
                  // cannot edit a token larger than the current token ID
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  TokenSettings memory token = _tokens[id];
                  uint32 existingAmountMinted = token.amountMinted;
                  PaymentSplitterSettings memory existingPaymentSplitterSettings = token
                      .paymentSplitterSettings;
                  // Once a token has been minted, it's not possible to change the supply & start/end times
                  if (
                      existingAmountMinted > 0 &&
                      (settings.maxSupply != token.maxSupply ||
                          settings.mintStart != token.mintStart ||
                          settings.mintEnd != token.mintEnd)
                  ) {
                      revert TokenSettingsLocked();
                  }
                  _verifyMintingTime(settings.mintStart, settings.mintEnd);
                  _tokens[id] = settings;
                  // it's not possible to update how many have been claimed, but it's part of the TokenSettings struct
                  // ignore any value that is passed in and use the existing value
                  _tokens[id].amountMinted = existingAmountMinted;
                  // payment splitter settings can only be updated via `updatePaymentSplitterSettingsByIndex`
                  _tokens[id].paymentSplitterSettings = existingPaymentSplitterSettings;
                  emit TokenSettingsUpdated(id);
              }
              function updatePaymentSplitterSettingsByIndex(
                  uint256 id,
                  PaymentSplitterSettings calldata settings
              ) external onlyOwner {
                  // cannot edit a token larger than the current token ID
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  // revenue split cannot be changed once a token is minted
                  if (_tokens[id].amountMinted > 0) {
                      revert TokenSettingsLocked();
                  }
                  _verifyPaymentSplitterSettings(settings);
                  _tokens[id].paymentSplitterSettings = settings;
                  emit RevenueSettingsUpdated(id);
              }
              function updateFallbackPaymentSplitterSettings(
                  PaymentSplitterSettings calldata settings
              ) external onlyOwner {
                  _verifyPaymentSplitterSettings(settings);
                  _fallbackPaymentSplitterSettings = settings;
                  emit FallbackRevenueSettingsUpdated();
              }
              function _verifyMintingTime(uint32 mintStart, uint32 mintEnd) private view {
                  if (mintEnd > 0) {
                      // mint end must be after mint start
                      if (mintEnd < mintStart) {
                          revert InvalidMintDates();
                      }
                      // mint end must be in the future
                      if (mintEnd < block.timestamp) {
                          revert InvalidMintDates();
                      }
                  }
              }
              function _verifyPaymentSplitterSettings(
                  PaymentSplitterSettings calldata settings
              ) private pure {
                  uint256 shareTotal;
                  uint256 numPayees = settings.payees.length;
                  // we discourage using the payment splitter for more than 4 payees, as it's not gas efficient for minting
                  // more advanced use-cases should consider a multi-sig payee
                  if (numPayees != settings.shares.length || numPayees > 4) {
                      revert InvalidPaymentSplitterSettings();
                  }
                  for (uint256 i = 0; i < numPayees; ) {
                      uint256 shares = settings.shares[i];
                      if (shares == 0) {
                          revert InvalidPaymentSplitterSettings();
                      }
                      shareTotal += shares;
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
                  if (shareTotal != 100) {
                      revert InvalidPaymentSplitterSettings();
                  }
              }
              /**
               * @notice Perform a batch airdrop of tokens to a list of recipients
               */
              function airdropToken(
                  uint256 id,
                  uint32[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  TokenSettings storage token = _tokens[id];
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint32 updatedAmountMinted = token.amountMinted + quantities[i];
                      if (token.maxSupply > 0 && updatedAmountMinted > token.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted
                      token.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], id, quantities[i], "");
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Release funds for a particular payee
               */
              function release(address payee) public {
                  uint256 amount = releasable(payee);
                  if (amount > 0) {
                      _totalReleased += amount;
                      // If "_totalReleased += amount" does not overflow, then "_released[payee] += amount" cannot overflow.
                      unchecked {
                          _released[payee] += amount;
                      }
                      AddressUpgradeable.sendValue(payable(payee), amount);
                      emit PaymentReleased(payee, amount);
                  }
              }
              /**
               * @notice Release funds for specified payees
               * @dev This is a convenience method to calling release() for each payee
               */
              function releaseBatch(address[] calldata payees) external {
                  uint256 numPayees = payees.length;
                  for (uint256 i = 0; i < numPayees; ) {
                      release(payees[i]);
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Update the default royalty settings (EIP-2981) for the contract.
               */
              function setRoyaltyInfo(
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setDefaultRoyalty(receiver, feeBasisPoints);
                  emit RoyaltyUpdated(receiver, feeBasisPoints);
              }
              /**
               * @notice Update the royalty settings (EIP-2981) for the token.
               */
              function setTokenRoyaltyInfo(
                  uint256 tokenId,
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setTokenRoyalty(tokenId, receiver, feeBasisPoints);
                  emit TokenRoyaltyUpdated(tokenId, receiver, feeBasisPoints);
              }
              /**
               * @notice If enabled, the token can be burned, for approved operators.
               * @dev The burn method will revert unless this is enabled
               */
              function toggleBurning() external onlyOwner {
                  allowBurning = !allowBurning;
                  emit BurnStatusChanged(allowBurning);
              }
              /**
               * @dev See {ERC1155Upgradeable-_setURI}
               */
              function setUri(string calldata uri) external onlyOwner {
                  _setURI(uri);
              }
              /**
               * @notice This function can only be called for tokens with supply. Calling this function will set the max supply
               * of a token to the current amount minted. This cannot be reversed.
               */
              function capSupplyAtIndex(uint256 id) external onlyOwner {
                  TokenSettings storage token = _tokens[id];
                  // only limited edition tokens can be capped
                  if (token.maxSupply == 0) {
                      revert InvalidToken();
                  }
                  token.maxSupply = token.amountMinted;
                  emit TokenSupplyCapped(id, token.maxSupply);
              }
              /*//////////////////////////////////////////////////////////////
                                     MINTING FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Mint a token to the sender
               */
              function mintToken(uint256 id, uint32 quantity) external payable {
                  TokenSettings memory token = _tokens[id];
                  if (token.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  _mintAfterChecks(
                      msg.sender,
                      msg.value,
                      id,
                      quantity,
                      token.maxPerWallet
                  );
              }
              /**
               * @notice Mint a token to a specific address
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintTokenTo(
                  address account,
                  uint256 id,
                  uint32 quantity
              ) external payable {
                  TokenSettings memory token = _tokens[id];
                  if (token.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  _mintAfterChecks(account, msg.value, id, quantity, token.maxPerWallet);
              }
              /**
               * @notice Mint a token that has an allowlist associated with it.
               * @dev maxQuantity is encoded as part of the proof, and is a way to associate variable quantities with each allowlisted wallet
               */
              function mintTokenAllowlist(
                  uint256 id,
                  uint32 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof
              ) external payable {
                  bytes32 merkleRoot = _tokens[id].merkleRoot;
                  if (merkleRoot == bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (
                      !MerkleProof.verify(
                          proof,
                          merkleRoot,
                          keccak256(abi.encodePacked(msg.sender, maxQuantity))
                      )
                  ) {
                      revert InvalidProof();
                  }
                  _mintAfterChecks(msg.sender, msg.value, id, quantity, maxQuantity);
              }
              function _mintAfterChecks(
                  address account,
                  uint256 balance,
                  uint256 id,
                  uint32 quantity,
                  uint32 maxQuantity
              ) private {
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  TokenSettings storage token = _tokens[id];
                  if (balance != token.price * quantity) {
                      revert InvalidPrice();
                  }
                  if (
                      token.maxSupply > 0 &&
                      token.amountMinted + quantity > token.maxSupply
                  ) {
                      revert SoldOut();
                  }
                  if (
                      maxQuantity > 0 &&
                      // maxQuantity is either the token-level maxPerWallet, or the maxQuantity passed in from the allowlist mint function
                      // if the latter, the value is provided by the user, but is first checked against the merkle tree
                      _mintBalanceByTokenId[id][account] + quantity > maxQuantity
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  if (token.mintStart > 0 && block.timestamp < token.mintStart) {
                      revert MintNotActive();
                  }
                  if (token.mintEnd > 0 && block.timestamp > token.mintEnd) {
                      revert MintNotActive();
                  }
                  // we only need to proceed if this is a revenue generating mint
                  if (balance > 0) {
                      uint256 numPayees = token.paymentSplitterSettings.payees.length;
                      if (numPayees > 0) {
                          // if we have token-level payment splitter settings, use those
                          calculateRevenueSplit(balance, token.paymentSplitterSettings);
                      } else {
                          // otherwise, fallback to the contract-level payment splitter settings
                          calculateRevenueSplit(
                              balance,
                              _fallbackPaymentSplitterSettings
                          );
                      }
                  }
                  token.amountMinted += quantity;
                  _mintBalanceByTokenId[id][account] += quantity;
                  _mint(account, id, quantity, "");
                  emit TokensMinted(account, id, quantity);
              }
              function calculateRevenueSplit(
                  uint256 value,
                  PaymentSplitterSettings storage paymentSplitterSettings
              ) private {
                  uint256 numPayees = paymentSplitterSettings.payees.length;
                  // each token can have different payment splitter settings, and price can change while mint is occurring
                  // therefore we need to do some revenue accounting at the time of mint based on the price paid
                  for (uint256 i = 0; i < numPayees; ) {
                      address payee = paymentSplitterSettings.payees[i];
                      uint256 amount = ((value * paymentSplitterSettings.shares[i]) /
                          100);
                      _revenueByAddress[payee] += amount;
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Burn a token, if the contract allows for it
               */
              function burn(uint256 id, uint256 amount) external {
                  if (!allowBurning) {
                      revert BurningNotAllowed();
                  }
                  _burn(msg.sender, id, amount);
                  emit TokenBurned(msg.sender, id, amount);
              }
              /*//////////////////////////////////////////////////////////////
                                       VIEW FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Get the token data based on it's ID (1, 2, etc)
               */
              function getTokenSettingsByTokenId(
                  uint256 id
              ) external view returns (TokenSettings memory) {
                  return _tokens[id];
              }
              /**
               * @notice Retrieve the fallback payment splitter config (used if a token doesn't have it's own payment splitter settings)
               */
              function getFallbackPaymentSplitterSettings()
                  external
                  view
                  returns (PaymentSplitterSettings memory)
              {
                  return _fallbackPaymentSplitterSettings;
              }
              /**
               * @notice Get the token data for all tokens associated with the contract
               */
              function getAllTokenData() external view returns (TokenData[] memory) {
                  uint256 numTokens = _currentTokenId;
                  TokenData[] memory tokens = new TokenData[](numTokens);
                  for (uint256 i = 0; i < numTokens; i++) {
                      tokens[i].settings = _tokens[i];
                      tokens[i].index = i;
                  }
                  return tokens;
              }
              /**
               * @dev Getter for the amount of Ether already released to a payee.
               */
              function released(address account) public view returns (uint256) {
                  return _released[account];
              }
              /**
               * @dev Getter for the total amount of Ether already released.
               */
              function totalReleased() external view returns (uint256) {
                  return _totalReleased;
              }
              /**
               * @dev Getter for the amount of payee's releasable Ether.
               */
              function releasable(address account) public view returns (uint256) {
                  return _revenueByAddress[account] - released(account);
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              )
                  public
                  view
                  virtual
                  override(ERC1155Upgradeable, ERC2981Upgradeable)
                  returns (bool)
              {
                  return super.supportsInterface(interfaceId);
              }
              /*//////////////////////////////////////////////////////////////
                                OPERATOR REGISTRY OVERRIDES
              //////////////////////////////////////////////////////////////*/
              function setApprovalForAll(
                  address operator,
                  bool approved
              ) public override onlyAllowedOperatorApproval(operator) {
                  super.setApprovalForAll(operator, approved);
              }
              /**
               * @dev See {IERC1155-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  uint256 amount,
                  bytes memory data
              ) public override onlyAllowedOperator(from) {
                  super.safeTransferFrom(from, to, tokenId, amount, data);
              }
              /**
               * @dev See {IERC1155-safeBatchTransferFrom}.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) public virtual override onlyAllowedOperator(from) {
                  super.safeBatchTransferFrom(from, to, ids, amounts, data);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "erc721a-upgradeable/contracts/extensions/ERC721AQueryableUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
          import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
          import "@openzeppelin/contracts-upgradeable/finance/PaymentSplitterUpgradeable.sol";
          import "operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol";
          import "./ICommon.sol";
          struct PhaseSettings {
              /// @dev phase supply. This can be released to public by ending the phase.
              uint64 maxSupply;
              /// @dev tracks the total amount minted in the phase
              uint64 amountMinted;
              /// @dev wallet maximum for the phase
              uint64 maxPerWallet;
              /// @dev merkle root for the phase (if applicable, otherwise bytes32(0))
              bytes32 merkleRoot;
              /// @dev whether the phase is active
              bool isActive;
              /// @dev price for the phase (or free if 0)
              uint256 price;
          }
          struct BaseSettings {
              /// @dev public sale supply. ending a phase will carry supply into this value
              uint64 maxSupply;
              /// @dev global wallet maximum across all phases (including public)
              uint64 maxPerWallet;
              /// @dev tracks the total amount minted in the public sale
              uint64 amountMinted;
              /// @dev price for the public sale (or free if 0)
              uint256 price;
          }
          struct SaleState {
              uint64 numPhases;
              mapping(uint256 => PhaseSettings) phases;
          }
          error PhaseNotActive();
          error InvalidPhase();
          error PhaseNotEnded();
          /// @author Bueno.art
          /// @title ERC-721 Multi-Phase Drop Contract (v3)
          contract Bueno721Drop is
              ERC721AQueryableUpgradeable,
              OwnableUpgradeable,
              ERC2981Upgradeable,
              PaymentSplitterUpgradeable,
              OperatorFiltererUpgradeable
          {
              string public _baseTokenURI;
              SaleState public saleState;
              BaseSettings public baseSettings;
              uint256 public maxSupply;
              address[] public withdrawAddresses;
              mapping(address => mapping(uint256 => uint64)) private amountMintedForPhase;
              bool public isPublicActive;
              bool private allowBurning;
              event TokensMinted(address indexed to, uint256 quantity);
              event TokenBurned(address indexed owner, uint256 tokenId);
              event TokensAirdropped(uint256 numRecipients, uint256 numTokens);
              event PhasesActivated(uint256[] phaseIds, bool activatedPublic);
              event PhasesPaused(uint256[] phaseIds, bool pausedPublic);
              event PhaseEnded(uint256 phaseIds);
              event BurnStatusChanged(bool burnActive);
              event PhaseSettingsUpdated(uint256 phaseId, PhaseSettings settings);
              event BaseSettingsUpdated(BaseSettings settings);
              event BaseURIUpdated(string baseURI);
              event RoyaltyUpdated(address royaltyAddress, uint96 royaltyAmount);
              /// @custom:oz-upgrades-unsafe-allow constructor
              constructor() {
                  _disableInitializers();
              }
              function initialize(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  RoyaltySettings calldata _royaltySettings,
                  PhaseSettings[] calldata _phases,
                  BaseSettings calldata _baseSettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  uint256 _maxIntendedSupply,
                  bool _allowBurning,
                  address _deployer,
                  address _operatorFilter
              ) public initializerERC721A initializer {
                  __ERC721A_init(_name, _symbol);
                  __Ownable_init();
                  __PaymentSplitter_init(
                      _paymentSplitterSettings.payees,
                      _paymentSplitterSettings.shares
                  );
                  uint64 numPhases = uint64(_phases.length);
                  uint256 supplyValidationCount = _baseSettings.maxSupply;
                  for (uint256 i = 0; i < numPhases; ) {
                      saleState.phases[i] = _phases[i];
                      supplyValidationCount += _phases[i].maxSupply;
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  require(
                      supplyValidationCount == _maxIntendedSupply,
                      "Supply of all phases must equal maxIntendedSupply"
                  );
                  _baseTokenURI = _baseUri;
                  withdrawAddresses = _paymentSplitterSettings.payees;
                  saleState.numPhases = numPhases;
                  baseSettings = _baseSettings;
                  allowBurning = _allowBurning;
                  maxSupply = _maxIntendedSupply;
                  _setDefaultRoyalty(
                      _royaltySettings.royaltyAddress,
                      _royaltySettings.royaltyAmount
                  );
                  transferOwnership(_deployer);
                  OperatorFiltererUpgradeable.__OperatorFilterer_init(
                      _operatorFilter,
                      _operatorFilter == address(0) ? false : true // only subscribe if a filter is provided
                  );
              }
              // ========= EXTERNAL MINTING METHODS =========
              /**
               * @notice Mint tokens for an allowlisted phase
               * @dev Calling this function for a phase that doesn't have an allowlist will fail
               */
              function mintPhaseAllowlist(
                  uint256 phaseIndex,
                  uint64 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof
              ) external payable {
                  uint64 updatedAmountMinted = _checkAllowlistPhaseMintConditions(
                      quantity,
                      maxQuantity,
                      proof,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(msg.sender, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint tokens for a non-allowlist phase.
               * @dev Calling this function for a phase that has an allowlist will fail
               */
              function mintPhase(uint256 phaseIndex, uint64 quantity) external payable {
                  uint64 updatedAmountMinted = _checkPhaseMintConditions(
                      msg.sender,
                      quantity,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(msg.sender, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint a token to a specific address from a non-allowlisted sale phase
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintPhaseTo(
                  address account,
                  uint256 phaseIndex,
                  uint64 quantity
              ) external payable {
                  uint64 updatedAmountMinted = _checkPhaseMintConditions(
                      account,
                      quantity,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(account, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint tokens for a given phase
               * @param account The address to mint the tokens to
               * @param quantity The number of tokens to mint
               * @param phaseIndex The index of the phase to mint from
               * @param updatedAmountMinted The updated amount minted for the phase
               */
              function _mintPhaseTo(
                  address account,
                  uint64 quantity,
                  uint256 phaseIndex,
                  uint64 updatedAmountMinted
              ) internal {
                  _checkGlobalPerWalletMax(account, quantity);
                  saleState.phases[phaseIndex].amountMinted += quantity;
                  amountMintedForPhase[account][phaseIndex] = updatedAmountMinted;
                  _mint(account, quantity);
                  emit TokensMinted(account, quantity);
              }
              /**
               * @notice Mint tokens in the public sale
               */
              function mintPublic(uint64 quantity) external payable {
                  _mintPublicTo(msg.sender, quantity);
              }
              /**
               * @notice Mint a token to a specific address from the public sale
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintPublicTo(address account, uint64 quantity) external payable {
                  _mintPublicTo(account, quantity);
              }
              /**
               * @notice Mint tokens in the public sale
               * @param account The address to mint the tokens to
               * @param quantity The number of tokens to mint
               */
              function _mintPublicTo(address account, uint64 quantity) internal {
                  uint64 updatedAmountMinted = _checkPublicMintConditions(
                      quantity,
                      msg.value
                  );
                  _checkGlobalPerWalletMax(account, quantity);
                  baseSettings.amountMinted = updatedAmountMinted;
                  _mint(account, quantity);
                  emit TokensMinted(account, quantity);
              }
              /**
               * @notice Mint tokens in all possible phases (including public sale)
               */
              function mintBatch(
                  uint64[] calldata quantities,
                  uint32[] calldata maxQuantities,
                  bytes32[][] calldata proofs,
                  uint256[] calldata phaseIndices,
                  uint64 publicQuantity
              ) external payable {
                  uint256 phaseLength = phaseIndices.length;
                  if (
                      phaseLength > saleState.numPhases ||
                      phaseLength != quantities.length ||
                      phaseLength != proofs.length ||
                      phaseLength != maxQuantities.length
                  ) {
                      revert InvalidPhase();
                  }
                  uint256 balance = msg.value;
                  uint256 quantityToMint;
                  for (uint256 i = 0; i < phaseLength; ) {
                      uint64 updatedAmount;
                      uint256 phaseIndex = phaseIndices[i];
                      uint64 quantity = quantities[i];
                      uint32 maxQuantity = maxQuantities[i];
                      bytes32[] calldata proof = proofs[i];
                      PhaseSettings storage phase = saleState.phases[phaseIndex];
                      uint256 priceForPhase = phase.price * quantity;
                      // Since price is strictly checked in the _check* functions below,
                      // we have an additional check here to ensure that the balance doesn't underflow
                      if (balance < priceForPhase) {
                          revert InvalidPrice();
                      }
                      // if the phase has no allowlist, the merkleRoot will be zeroed out.
                      if (phase.merkleRoot == bytes32(0)) {
                          updatedAmount = _checkPhaseMintConditions(
                              msg.sender,
                              quantity,
                              phaseIndex,
                              priceForPhase
                          );
                      } else {
                          updatedAmount = _checkAllowlistPhaseMintConditions(
                              quantity,
                              maxQuantity,
                              proof,
                              phaseIndex,
                              priceForPhase
                          );
                      }
                      // quantity & phaseLength have a maximum value of 2^64 - 1
                      // balance underflow is checked above
                      unchecked {
                          saleState.phases[phaseIndex].amountMinted += quantity;
                          amountMintedForPhase[msg.sender][phaseIndex] = updatedAmount;
                          balance -= priceForPhase;
                          quantityToMint += quantity;
                          ++i;
                      }
                  }
                  uint256 totalMintQuantity = quantityToMint;
                  if (publicQuantity > 0) {
                      _checkPublicMintConditions(publicQuantity, balance);
                      // publicQuantity has a max value of 2^64 - 1
                      unchecked {
                          baseSettings.amountMinted += publicQuantity;
                          totalMintQuantity += publicQuantity;
                      }
                  } else {
                      // remaining balance is checked in _checkPublicMintConditions, so we only need to check
                      // in the case where publicQuantity is 0
                      if (balance != 0) {
                          revert InvalidPrice();
                      }
                  }
                  _checkGlobalPerWalletMax(msg.sender, totalMintQuantity);
                  _mint(msg.sender, totalMintQuantity);
                  emit TokensMinted(msg.sender, totalMintQuantity);
              }
              /**
               * @notice Burn a token, if the contract allows for it
               */
              function burn(uint256 tokenId) external {
                  if (!allowBurning) {
                      revert BurningNotAllowed();
                  }
                  _burn(tokenId, true);
                  emit TokenBurned(msg.sender, tokenId);
              }
              // ========= OWNER METHODS =========
              /**
               * @notice Perform a batch airdrop for a particular phase.
               * @dev Minted tokens are pulled from the phase that is specified in the airdropper.
               */
              function airdropForPhase(
                  uint256 phaseIndex,
                  uint64[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint64 updatedAmountMinted = phase.amountMinted + quantities[i];
                      if (updatedAmountMinted > phase.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted for the phase
                      phase.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], quantities[i]);
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Perform a batch airdrop for the public phase.
               * @dev Minted tokens are pulled from the public phase.
               */
              function airdropPublic(
                  uint64[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint64 updatedAmountMinted = baseSettings.amountMinted +
                          quantities[i];
                      if (updatedAmountMinted > baseSettings.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted for the phase
                      baseSettings.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], quantities[i]);
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Specify which phases are active.
               * Public sale can be activated by setting `activatePublic` to true.
               */
              function activatePhases(
                  uint256[] calldata phaseIndices,
                  bool activatePublic
              ) external onlyOwner {
                  uint256 numPhases = phaseIndices.length;
                  // activate all the phases provided in phaseIndices
                  for (uint256 i = 0; i < numPhases; ) {
                      uint256 phaseIndex = phaseIndices[i];
                      if (phaseIndex >= saleState.numPhases) {
                          // phaseIndex is out of bounds
                          revert InvalidPhase();
                      }
                      saleState.phases[phaseIndices[i]].isActive = true;
                      // numPhases has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // calling this function with activatePublic=false only indicates the public sale
                  // is not intended to be activated, but it does not pause it.
                  if (activatePublic) {
                      isPublicActive = true;
                  }
                  emit PhasesActivated(phaseIndices, activatePublic);
              }
              /**
               * @notice Specify which phases are inactive (paused).
               * Public sale can be paused by setting `pausePublic` to true.
               * Pausing is separate from ending, since ending permanently closes the phase.
               */
              function pausePhases(
                  uint256[] calldata phaseIndices,
                  bool pausePublic
              ) external onlyOwner {
                  uint256 numPhases = phaseIndices.length;
                  for (uint256 i = 0; i < numPhases; ) {
                      uint256 phaseIndex = phaseIndices[i];
                      if (phaseIndex >= saleState.numPhases) {
                          // phaseIndex is out of bounds
                          revert InvalidPhase();
                      }
                      saleState.phases[phaseIndex].isActive = false;
                      // numPhases has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // calling this function with pausePublic=false only indicates the public sale
                  // is not intended to be paused, but it does not pause it.
                  if (pausePublic) {
                      isPublicActive = false;
                  }
                  emit PhasesPaused(phaseIndices, pausePublic);
              }
              /**
               * @notice If enabled, the token can be burned, for approved operators.
               * @dev The burn method will revert unless this is enabled
               */
              function toggleBurning() external onlyOwner {
                  allowBurning = !allowBurning;
                  emit BurnStatusChanged(allowBurning);
              }
              /**
               * @notice Permanently closes a phase by capping the supply & releasing it
               */
              function endPhase(uint256 phaseIndex) public onlyOwner {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  // if the phase never had supply, there is nothing to do
                  if (phase.maxSupply == 0) {
                      revert InvalidPhase();
                  }
                  // transfer the remaining supply into the base settings (used for public sale accounting)
                  baseSettings.maxSupply += phase.maxSupply - phase.amountMinted;
                  // remove the supply from the phase
                  phase.maxSupply = 0;
                  emit PhaseEnded(phaseIndex);
              }
              function endPhases(uint64[] calldata phaseIndices) external onlyOwner {
                  uint256 phaseIndicesLength = phaseIndices.length;
                  // ensure that phaseIndices argument will only ever be as large as the number of phases
                  if (phaseIndicesLength > saleState.numPhases) {
                      revert InvalidPhase();
                  }
                  for (uint256 i = 0; i < phaseIndicesLength; ) {
                      endPhase(phaseIndices[i]);
                      // phaseIndicesLength has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Updates the minting rules for a particular phase
               * @dev supply, amountMinted are not changeable. isActive is handled by activatePhases
               */
              function updatePhaseSettings(
                  uint256 phaseIndex,
                  PhaseSettings calldata phase
              ) external onlyOwner {
                  uint64 existingAmountMinted = saleState.phases[phaseIndex].amountMinted;
                  uint64 existingMaxSupply = saleState.phases[phaseIndex].maxSupply;
                  bool existingStatus = saleState.phases[phaseIndex].isActive;
                  saleState.phases[phaseIndex] = phase;
                  // ensure that the amountMinted, maxSupply, and status values cannot be set
                  saleState.phases[phaseIndex].amountMinted = existingAmountMinted;
                  saleState.phases[phaseIndex].maxSupply = existingMaxSupply;
                  saleState.phases[phaseIndex].isActive = existingStatus;
                  emit PhaseSettingsUpdated(phaseIndex, phase);
              }
              /**
               * @notice Updates the the base minting settings
               * The global maxPerWallet setting applies to all phases
               * Pricing and other fields will apply to the public sale
               *
               * @dev maxSupply & amountMinted are not changeable. isActive is handled by activatePhases
               */
              function updateBaseSettings(
                  BaseSettings calldata _baseSettings
              ) external onlyOwner {
                  uint64 existingMaxSupply = baseSettings.maxSupply;
                  uint64 existingAmountMinted = baseSettings.amountMinted;
                  baseSettings = _baseSettings;
                  // ensure that the maxSupply & amountMinted value cannot be set
                  baseSettings.maxSupply = existingMaxSupply;
                  baseSettings.amountMinted = existingAmountMinted;
                  emit BaseSettingsUpdated(_baseSettings);
              }
              /**
               * @dev Payment can be pulled via PaymentSplitter.release
               * this method is provided for convenience to release all payee funds
               */
              function withdraw() external onlyOwner {
                  uint256 numAddresses = withdrawAddresses.length;
                  for (uint256 i = 0; i < numAddresses; ) {
                      address payable withdrawAddress = payable(withdrawAddresses[i]);
                      if (releasable(withdrawAddress) > 0) {
                          release(withdrawAddress);
                      }
                      // numAddresses has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
              }
              function setBaseURI(string memory baseURI) external onlyOwner {
                  _baseTokenURI = baseURI;
                  emit BaseURIUpdated(baseURI);
              }
              function setRoyaltyInfo(
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setDefaultRoyalty(receiver, feeBasisPoints);
                  emit RoyaltyUpdated(receiver, feeBasisPoints);
              }
              /**
               * @notice This function cannot be called if any phase has not ended. This will cap the max supply
               * to the amount of tokens that have been minted so far. This cannot be reversed.
               */
              function capMaxSupply() external onlyOwner {
                  uint64 numPhases = saleState.numPhases;
                  for (uint64 i = 0; i < numPhases; ) {
                      if (saleState.phases[i].maxSupply > 0) {
                          // we only want to cap the supply if all phases are ended, meaning their supply
                          // has been transferred to the public/base phase
                          revert PhaseNotEnded();
                      }
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // whatever the amount of tokens that has been minted is the new overall "max supply"
                  maxSupply = _totalMinted();
                  // the max supply of the public/base phase is however many have been minted
                  baseSettings.maxSupply = baseSettings.amountMinted;
              }
              // ========= VIEW METHODS =========
              function _startTokenId() internal view virtual override returns (uint256) {
                  return 1;
              }
              function _checkAllowlistPhaseMintConditions(
                  uint64 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof,
                  uint256 phaseIndex,
                  uint256 balance
              ) internal view returns (uint64) {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  if (!phase.isActive) {
                      revert PhaseNotActive();
                  }
                  // there should be a valid merkle root for the phase
                  if (phase.merkleRoot == bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (phase.amountMinted + quantity > phase.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * phase.price) {
                      revert InvalidPrice();
                  }
                  if (
                      !MerkleProof.verify(
                          proof,
                          phase.merkleRoot,
                          keccak256(abi.encodePacked(msg.sender, maxQuantity))
                      )
                  ) {
                      revert InvalidProof();
                  }
                  uint256 amountMinted = amountMintedForPhase[msg.sender][phaseIndex];
                  uint256 updatedAmountMinted = amountMinted + quantity;
                  if (
                      // phases can have a maxPerWallet
                      (maxQuantity == 0 &&
                          phase.maxPerWallet > 0 &&
                          updatedAmountMinted > phase.maxPerWallet) ||
                      // wallets can have a maxPerWallet encoded in the merkle tree
                      (maxQuantity > 0 && updatedAmountMinted > maxQuantity)
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkPhaseMintConditions(
                  address wallet,
                  uint256 quantity,
                  uint256 phaseIndex,
                  uint256 balance
              ) internal view returns (uint64) {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  if (!phase.isActive) {
                      revert PhaseNotActive();
                  }
                  // the phase should not have a merkleRoot
                  if (phase.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (phase.amountMinted + quantity > phase.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * phase.price) {
                      revert InvalidPrice();
                  }
                  uint256 amountMinted = amountMintedForPhase[wallet][phaseIndex];
                  uint256 updatedAmountMinted = amountMinted + quantity;
                  // phases can have a maxPerWallet
                  if (
                      phase.maxPerWallet > 0 && updatedAmountMinted > phase.maxPerWallet
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkPublicMintConditions(
                  uint256 quantity,
                  uint256 balance
              ) internal view returns (uint64) {
                  if (!isPublicActive) {
                      revert PhaseNotActive();
                  }
                  uint256 updatedAmountMinted = baseSettings.amountMinted + quantity;
                  if (updatedAmountMinted > baseSettings.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * baseSettings.price) {
                      revert InvalidPrice();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkGlobalPerWalletMax(
                  address wallet,
                  uint256 quantity
              ) internal view {
                  if (
                      baseSettings.maxPerWallet > 0 &&
                      _numberMinted(wallet) + quantity > baseSettings.maxPerWallet
                  ) {
                      revert ExceedMaxPerWallet();
                  }
              }
              function getDataForPhase(
                  uint256 phaseIndex
              ) external view returns (PhaseSettings memory) {
                  return saleState.phases[phaseIndex];
              }
              function getMintBalance(address wallet) external view returns (uint256) {
                  return _numberMinted(wallet);
              }
              function getAmountMintedForPhase(
                  uint256 phaseIndex,
                  address wallet
              ) external view returns (uint64) {
                  return amountMintedForPhase[wallet][phaseIndex];
              }
              function getAmountMintedForOwner(
                  address wallet
              ) external view returns (uint256[] memory) {
                  uint256[] memory amountMintedPerPhase = new uint256[](
                      saleState.numPhases + 1
                  );
                  for (uint64 i = 0; i < saleState.numPhases; ) {
                      amountMintedPerPhase[i] = amountMintedForPhase[wallet][i];
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  amountMintedPerPhase[saleState.numPhases] = _numberMinted(wallet);
                  return amountMintedPerPhase;
              }
              /**
               * @dev See {ERC721-_baseURI}.
               */
              function _baseURI() internal view virtual override returns (string memory) {
                  return _baseTokenURI;
              }
              function supportsInterface(
                  bytes4 interfaceId
              )
                  public
                  view
                  virtual
                  override(IERC721AUpgradeable, ERC721AUpgradeable, ERC2981Upgradeable)
                  returns (bool)
              {
                  return
                      ERC721AUpgradeable.supportsInterface(interfaceId) ||
                      ERC2981Upgradeable.supportsInterface(interfaceId);
              }
              // ========= OPERATOR FILTERER OVERRIDES =========
              function setApprovalForAll(
                  address operator,
                  bool approved
              )
                  public
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperatorApproval(operator)
              {
                  super.setApprovalForAll(operator, approved);
              }
              function approve(
                  address operator,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperatorApproval(operator)
              {
                  super.approve(operator, tokenId);
              }
              function transferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.transferFrom(from, to, tokenId);
              }
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.safeTransferFrom(from, to, tokenId);
              }
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory data
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.safeTransferFrom(from, to, tokenId, data);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "@openzeppelin/contracts/proxy/Clones.sol";
          import "@openzeppelin/contracts/access/AccessControl.sol";
          import "./Bueno721Drop.sol";
          import "./Bueno1155Drop.sol";
          contract BuenoFactory is AccessControl {
              address private DEFAULT_OPERATOR_FILTER =
                  address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
              address private drop721Implementation;
              address private drop1155Implementation;
              event ContractCreated(address creator, address contractAddress);
              constructor() {
                  _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
              }
              function updateDefaultOperatorFilter(
                  address newFilter
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  DEFAULT_OPERATOR_FILTER = newFilter;
              }
              function update721Implementation(
                  address newImplementation
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  drop721Implementation = newImplementation;
              }
              function update1155Implementation(
                  address newImplementation
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  drop1155Implementation = newImplementation;
              }
              function getOperatorFilter() external view returns (address) {
                  return DEFAULT_OPERATOR_FILTER;
              }
              function get721ImplementationAddress() external view returns (address) {
                  return drop721Implementation;
              }
              function get1155ImplementationAddress() external view returns (address) {
                  return drop1155Implementation;
              }
              function deploy721Drop(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  RoyaltySettings calldata _royaltySettings,
                  PhaseSettings[] calldata _phases,
                  BaseSettings calldata _baseSettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  uint256 _maxIntendedSupply,
                  bool _registerOperatorFilter,
                  bool _allowBurning
              ) external {
                  require(drop721Implementation != address(0), "Implementation not set");
                  address payable clone = payable(Clones.clone(drop721Implementation));
                  address operatorFilter = _registerOperatorFilter
                      ? DEFAULT_OPERATOR_FILTER
                      : address(0);
                  Bueno721Drop(clone).initialize(
                      _name,
                      _symbol,
                      _baseUri,
                      _royaltySettings,
                      _phases,
                      _baseSettings,
                      _paymentSplitterSettings,
                      _maxIntendedSupply,
                      _allowBurning,
                      msg.sender,
                      operatorFilter
                  );
                  emit ContractCreated(msg.sender, clone);
              }
              function deploy1155Drop(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  TokenSettings[] calldata _tokenSettings,
                  RoyaltySettings calldata _royaltySettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  bool _registerOperatorFilter,
                  bool _allowBurning
              ) external {
                  require(drop1155Implementation != address(0), "Implementation not set");
                  address clone = Clones.clone(drop1155Implementation);
                  address operatorFilter = _registerOperatorFilter
                      ? DEFAULT_OPERATOR_FILTER
                      : address(0);
                  Bueno1155Drop(clone).initialize(
                      _name,
                      _symbol,
                      _baseUri,
                      _tokenSettings,
                      _royaltySettings,
                      _paymentSplitterSettings,
                      _allowBurning,
                      msg.sender,
                      operatorFilter
                  );
                  emit ContractCreated(msg.sender, clone);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          error InvalidPrice();
          error SoldOut();
          error ExceedMaxPerWallet();
          error InvalidProof();
          error InvalidMintFunction();
          error InvalidAirdrop();
          error BurningNotAllowed();
          struct PaymentSplitterSettings {
              address[] payees;
              uint256[] shares;
          }
          struct RoyaltySettings {
              address royaltyAddress;
              uint96 royaltyAmount;
          }
          // 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;
              }
              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.2.3
          // 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()`.
           *
           * 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();
              }
              // =============================================================
              //                   TOKEN COUNTING OPERATIONS
              // =============================================================
              /**
               * @dev Returns the starting token ID.
               * To change the starting token ID, please override this function.
               */
              function _startTokenId() internal view virtual returns (uint256) {
                  return 0;
              }
              /**
               * @dev Returns the next token ID to be minted.
               */
              function _nextTokenId() internal view virtual returns (uint256) {
                  return 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) {
                  // Counter underflow is impossible as _burnCounter cannot be incremented
                  // more than `_currentIndex - _startTokenId()` times.
                  unchecked {
                      return ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total amount of tokens minted in the contract.
               */
              function _totalMinted() internal view virtual returns (uint256) {
                  // Counter underflow is impossible as `_currentIndex` does not decrement,
                  // and it is initialized to `_startTokenId()`.
                  unchecked {
                      return ERC721AStorage.layout()._currentIndex - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total number of tokens burned.
               */
              function _totalBurned() internal view virtual returns (uint256) {
                  return ERC721AStorage.layout()._burnCounter;
              }
              // =============================================================
              //                    ADDRESS DATA OPERATIONS
              // =============================================================
              /**
               * @dev Returns the number of tokens in `owner`'s account.
               */
              function balanceOf(address owner) public view virtual override returns (uint256) {
                  if (owner == address(0)) revert BalanceQueryForZeroAddress();
                  return 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();
                  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 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);
                  }
              }
              /**
               * 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 not burned.
                      if (packed & _BITMASK_BURNED == 0) {
                          // If the data at the starting slot does not exist, start the scan.
                          if (packed == 0) {
                              if (tokenId >= ERC721AStorage.layout()._currentIndex) revert OwnerQueryForNonexistentToken();
                              // 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;
                                  return packed;
                              }
                          }
                          // Otherwise, the data exists and is not burned. 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.
                          return packed;
                      }
                  }
                  revert OwnerQueryForNonexistentToken();
              }
              /**
               * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
               */
              function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
                  ownership.addr = address(uint160(packed));
                  ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
                  ownership.burned = packed & _BITMASK_BURNED != 0;
                  ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
              }
              /**
               * @dev Packs ownership data into a single uint256.
               */
              function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                      result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
                  }
              }
              /**
               * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
               */
              function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
                  // For branchless setting of the `nextInitialized` flag.
                  assembly {
                      // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                      result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
                  }
              }
              // =============================================================
              //                      APPROVAL OPERATIONS
              // =============================================================
              /**
               * @dev Gives permission to `to` to transfer `tokenId` token to another account. 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();
                  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) {
                  return
                      _startTokenId() <= tokenId &&
                      tokenId < ERC721AStorage.layout()._currentIndex && // If within bounds,
                      ERC721AStorage.layout()._packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
              }
              /**
               * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
               */
              function _isSenderApprovedOrOwner(
                  address approvedAddress,
                  address owner,
                  address msgSender
              ) private pure returns (bool result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      msgSender := and(msgSender, _BITMASK_ADDRESS)
                      // `msgSender == owner || msgSender == approvedAddress`.
                      result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
                  }
              }
              /**
               * @dev Returns the storage slot and value for the approved address of `tokenId`.
               */
              function _getApprovedSlotAndAddress(uint256 tokenId)
                  private
                  view
                  returns (uint256 approvedAddressSlot, address approvedAddress)
              {
                  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);
                  if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
                  (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                  // The nested ifs save around 20+ gas over a compound boolean condition.
                  if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                      if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                  if (to == address(0)) revert TransferToZeroAddress();
                  _beforeTokenTransfers(from, to, tokenId, 1);
                  // Clear approvals from the previous owner.
                  assembly {
                      if approvedAddress {
                          // This is equivalent to `delete _tokenApprovals[tokenId]`.
                          sstore(approvedAddressSlot, 0)
                      }
                  }
                  // Underflow of the sender's balance is impossible because we check for
                  // ownership above and the recipient's balance can't realistically overflow.
                  // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
                  unchecked {
                      // We can directly increment and decrement the balances.
                      --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;
                              }
                          }
                      }
                  }
                  emit Transfer(from, to, tokenId);
                  _afterTokenTransfers(from, to, tokenId, 1);
              }
              /**
               * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public payable virtual override {
                  safeTransferFrom(from, to, tokenId, '');
              }
              /**
               * @dev Safely transfers `tokenId` token from `from` to `to`.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               * - `tokenId` token must exist and be owned by `from`.
               * - If the caller is not `from`, it must be approved to move this token
               * by either {approve} or {setApprovalForAll}.
               * - If `to` refers to a smart contract, it must implement
               * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) public payable virtual override {
                  transferFrom(from, to, tokenId);
                  if (to.code.length != 0)
                      if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                          revert TransferToNonERC721ReceiverImplementer();
                      }
              }
              /**
               * @dev Hook that is called before a set of serially-ordered token IDs
               * are about to be transferred. This includes minting.
               * And also called before burning one token.
               *
               * `startTokenId` - the first token ID to be transferred.
               * `quantity` - the amount to be transferred.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
               * transferred to `to`.
               * - When `from` is zero, `tokenId` will be minted for `to`.
               * - When `to` is zero, `tokenId` will be burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _beforeTokenTransfers(
                  address from,
                  address to,
                  uint256 startTokenId,
                  uint256 quantity
              ) internal virtual {}
              /**
               * @dev Hook that is called after a set of serially-ordered token IDs
               * have been transferred. This includes minting.
               * And also called after one token has been burned.
               *
               * `startTokenId` - the first token ID to be transferred.
               * `quantity` - the amount to be transferred.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
               * transferred to `to`.
               * - When `from` is zero, `tokenId` has been minted for `to`.
               * - When `to` is zero, `tokenId` has been burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _afterTokenTransfers(
                  address from,
                  address to,
                  uint256 startTokenId,
                  uint256 quantity
              ) internal virtual {}
              /**
               * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
               *
               * `from` - Previous owner of the given token ID.
               * `to` - Target address that will receive the token.
               * `tokenId` - Token ID to be transferred.
               * `_data` - Optional data to send along with the call.
               *
               * Returns whether the call correctly returned the expected magic value.
               */
              function _checkContractOnERC721Received(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) private returns (bool) {
                  try
                      ERC721A__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();
                      } else {
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              }
              // =============================================================
              //                        MINT OPERATIONS
              // =============================================================
              /**
               * @dev Mints `quantity` tokens and transfers them to `to`.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `quantity` must be greater than 0.
               *
               * Emits a {Transfer} event for each mint.
               */
              function _mint(address to, uint256 quantity) internal virtual {
                  uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
                  if (quantity == 0) revert MintZeroQuantity();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are incredibly unrealistic.
                  // `balance` and `numberMinted` have a maximum limit of 2**64.
                  // `tokenId` has a maximum limit of 2**256.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      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)
                      );
                      uint256 toMasked;
                      uint256 end = startTokenId + quantity;
                      // Use assembly to loop and emit the `Transfer` event for gas savings.
                      // The duplicated `log4` removes an extra check and reduces stack juggling.
                      // The assembly, together with the surrounding Solidity code, have been
                      // delicately arranged to nudge the compiler into producing optimized opcodes.
                      assembly {
                          // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          toMasked := and(to, _BITMASK_ADDRESS)
                          // Emit the `Transfer` event.
                          log4(
                              0, // Start of data (0, since no data).
                              0, // End of data (0, since no data).
                              _TRANSFER_EVENT_SIGNATURE, // Signature.
                              0, // `address(0)`.
                              toMasked, // `to`.
                              startTokenId // `tokenId`.
                          )
                          // The `iszero(eq(,))` check ensures that large values of `quantity`
                          // that overflows uint256 will make the loop run out of gas.
                          // The compiler will optimize the `iszero` away for performance.
                          for {
                              let tokenId := add(startTokenId, 1)
                          } iszero(eq(tokenId, end)) {
                              tokenId := add(tokenId, 1)
                          } {
                              // Emit the `Transfer` event. Similar to above.
                              log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                          }
                      }
                      if (toMasked == 0) revert MintToZeroAddress();
                      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();
                  if (quantity == 0) revert MintZeroQuantity();
                  if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      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)
                      );
                      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();
                              }
                          } while (index < end);
                          // Reentrancy protection.
                          if (ERC721AStorage.layout()._currentIndex != end) revert();
                      }
                  }
              }
              /**
               * @dev Equivalent to `_safeMint(to, quantity, '')`.
               */
              function _safeMint(address to, uint256 quantity) internal virtual {
                  _safeMint(to, quantity, '');
              }
              // =============================================================
              //                       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)
                      if (_msgSenderERC721A() != owner)
                          if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                  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();
                  }
                  _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 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();
                  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)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.3
          // 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) {
                  TokenOwnership memory ownership;
                  if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
                      return ownership;
                  }
                  ownership = _ownershipAt(tokenId);
                  if (ownership.burned) {
                      return ownership;
                  }
                  return _ownershipOf(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)
              {
                  unchecked {
                      uint256 tokenIdsLength = tokenIds.length;
                      TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                      for (uint256 i; i != tokenIdsLength; ++i) {
                          ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                      }
                      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) {
                  unchecked {
                      if (start >= stop) revert InvalidQueryRange();
                      uint256 tokenIdsIdx;
                      uint256 stopLimit = _nextTokenId();
                      // Set `start = max(start, _startTokenId())`.
                      if (start < _startTokenId()) {
                          start = _startTokenId();
                      }
                      // Set `stop = min(stop, stopLimit)`.
                      if (stop > stopLimit) {
                          stop = stopLimit;
                      }
                      uint256 tokenIdsMaxLength = balanceOf(owner);
                      // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                      // to cater for cases where `balanceOf(owner)` is too big.
                      if (start < stop) {
                          uint256 rangeLength = stop - start;
                          if (rangeLength < tokenIdsMaxLength) {
                              tokenIdsMaxLength = rangeLength;
                          }
                      } else {
                          tokenIdsMaxLength = 0;
                      }
                      uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                      if (tokenIdsMaxLength == 0) {
                          return tokenIds;
                      }
                      // 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;
                      }
                      for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                          ownership = _ownershipAt(i);
                          if (ownership.burned) {
                              continue;
                          }
                          if (ownership.addr != address(0)) {
                              currOwnershipAddr = ownership.addr;
                          }
                          if (currOwnershipAddr == owner) {
                              tokenIds[tokenIdsIdx++] = i;
                          }
                      }
                      // Downsize the array to fit.
                      assembly {
                          mstore(tokenIds, tokenIdsIdx)
                      }
                      return tokenIds;
                  }
              }
              /**
               * @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) {
                  unchecked {
                      uint256 tokenIdsIdx;
                      address currOwnershipAddr;
                      uint256 tokenIdsLength = balanceOf(owner);
                      uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                      TokenOwnership memory ownership;
                      for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                          ownership = _ownershipAt(i);
                          if (ownership.burned) {
                              continue;
                          }
                          if (ownership.addr != address(0)) {
                              currOwnershipAddr = ownership.addr;
                          }
                          if (currOwnershipAddr == owner) {
                              tokenIds[tokenIdsIdx++] = i;
                          }
                      }
                      return tokenIds;
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.3
          // 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.2.3
          // 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();
              // =============================================================
              //                            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.13;
          interface IOperatorFilterRegistry {
              function isOperatorAllowed(address registrant, address operator) external view returns (bool);
              function register(address registrant) external;
              function registerAndSubscribe(address registrant, address subscription) external;
              function registerAndCopyEntries(address registrant, address registrantToCopy) external;
              function unregister(address addr) external;
              function updateOperator(address registrant, address operator, bool filtered) external;
              function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
              function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
              function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
              function subscribe(address registrant, address registrantToSubscribe) external;
              function unsubscribe(address registrant, bool copyExistingEntries) external;
              function subscriptionOf(address addr) external returns (address registrant);
              function subscribers(address registrant) external returns (address[] memory);
              function subscriberAt(address registrant, uint256 index) external returns (address);
              function copyEntriesOf(address registrant, address registrantToCopy) external;
              function isOperatorFiltered(address registrant, address operator) external returns (bool);
              function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
              function filteredOperators(address addr) external returns (address[] memory);
              function filteredCodeHashes(address addr) external returns (bytes32[] memory);
              function filteredOperatorAt(address registrant, uint256 index) external returns (address);
              function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
              function isRegistered(address addr) external returns (bool);
              function codeHashOf(address addr) external returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {IOperatorFilterRegistry} from "../IOperatorFilterRegistry.sol";
          import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
          abstract contract OperatorFiltererUpgradeable is Initializable {
              error OperatorNotAllowed(address operator);
              IOperatorFilterRegistry constant operatorFilterRegistry =
                  IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
              function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe)
                  internal
                  onlyInitializing
              {
                  // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                  // will not revert, but the contract will need to be registered with the registry once it is deployed in
                  // order for the modifier to filter addresses.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (!operatorFilterRegistry.isRegistered(address(this))) {
                          if (subscribe) {
                              operatorFilterRegistry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                          } else {
                              if (subscriptionOrRegistrantToCopy != address(0)) {
                                  operatorFilterRegistry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                              } else {
                                  operatorFilterRegistry.register(address(this));
                              }
                          }
                      }
                  }
              }
              modifier onlyAllowedOperator(address from) virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      // Allow spending tokens from addresses with balance
                      // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                      // from an EOA.
                      if (from == msg.sender) {
                          _;
                          return;
                      }
                      if (!operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)) {
                          revert OperatorNotAllowed(msg.sender);
                      }
                  }
                  _;
              }
              modifier onlyAllowedOperatorApproval(address operator) virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (!operatorFilterRegistry.isOperatorAllowed(address(this), operator)) {
                          revert OperatorNotAllowed(operator);
                      }
                  }
                  _;
              }
          }
          

          File 3 of 6: PayableProxy
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.7;
          import { PayableProxyInterface } from "../interfaces/PayableProxyInterface.sol";
          interface IUpgradeBeacon {
              /**
               * @notice An external view function that returns the implementation.
               *
               * @return The address of the implementation.
               */
              function implementation() external view returns (address);
          }
          /**
           * @title   PayableProxy
           * @author  OpenSea Protocol Team
           * @notice  PayableProxy is a beacon proxy which will immediately return if
           *          called with callvalue. Otherwise, it will delegatecall the beacon
           *          implementation.
           */
          contract PayableProxy is PayableProxyInterface {
              // Address of the beacon.
              address private immutable _beacon;
              constructor(address beacon) payable {
                  // Ensure the origin is an approved deployer.
                  require(
                      (tx.origin == address(0x939C8d89EBC11fA45e576215E2353673AD0bA18A) ||
                          tx.origin ==
                          address(0xe80a65eB7a3018DedA407e621Ef5fb5B416678CA) ||
                          tx.origin ==
                          address(0x86D26897267711ea4b173C8C124a0A73612001da) ||
                          tx.origin ==
                          address(0x3B52ad533687Ce908bA0485ac177C5fb42972962)),
                      "Deployment must originate from an approved deployer."
                  );
                  // Set the initial beacon.
                  _beacon = beacon;
              }
              function initialize(address ownerToSet) external {
                  // Ensure the origin is an approved deployer.
                  require(
                      (tx.origin == address(0x939C8d89EBC11fA45e576215E2353673AD0bA18A) ||
                          tx.origin ==
                          address(0xe80a65eB7a3018DedA407e621Ef5fb5B416678CA) ||
                          tx.origin ==
                          address(0x86D26897267711ea4b173C8C124a0A73612001da) ||
                          tx.origin ==
                          address(0x3B52ad533687Ce908bA0485ac177C5fb42972962)),
                      "Initialize must originate from an approved deployer."
                  );
                  // Get the implementation address from the provided beacon.
                  address implementation = IUpgradeBeacon(_beacon).implementation();
                  // Create the initializationCalldata from the provided parameters.
                  bytes memory initializationCalldata = abi.encodeWithSignature(
                      "initialize(address)",
                      ownerToSet
                  );
                  // Delegatecall into the implementation, supplying initialization
                  // calldata.
                  (bool ok, ) = implementation.delegatecall(initializationCalldata);
                  // Revert and include revert data if delegatecall to implementation
                  // reverts.
                  if (!ok) {
                      assembly {
                          returndatacopy(0, 0, returndatasize())
                          revert(0, returndatasize())
                      }
                  }
              }
              /**
               * @dev Fallback function that delegates calls to the address returned by
               *      `_implementation()`. Will run if no other function in the contract
               *      matches the call data.
               */
              fallback() external payable override {
                  _fallback();
              }
              /**
               * @dev Internal fallback function that delegates calls to the address
               *      returned by `_implementation()`. Will run if no other function
               *      in the contract matches the call data.
               */
              function _fallback() internal {
                  // Delegate if call value is zero.
                  if (msg.value == 0) {
                      _delegate(_implementation());
                  }
              }
              /**
               * @dev Delegates the current call to `implementation`.
               *
               * This function does not return to its internal call site, it will
               * return directly to the external caller.
               */
              function _delegate(address implementation) internal virtual {
                  assembly {
                      // Copy msg.data. We take full control of memory in this
                      // inline assembly block because it will not return to
                      // Solidity code. We overwrite the Solidity scratch pad
                      // at memory position 0.
                      calldatacopy(0, 0, calldatasize())
                      // Call the implementation.
                      // out and outsize are 0 because we don't know the size yet.
                      let result := delegatecall(
                          gas(),
                          implementation,
                          0,
                          calldatasize(),
                          0,
                          0
                      )
                      // Copy the returned data.
                      returndatacopy(0, 0, returndatasize())
                      switch result
                      // delegatecall returns 0 on error.
                      case 0 {
                          revert(0, returndatasize())
                      }
                      default {
                          return(0, returndatasize())
                      }
                  }
              }
              /**
               * @dev This function returns the address to which the fallback function
               *      should delegate.
               */
              function _implementation() internal view returns (address) {
                  return IUpgradeBeacon(_beacon).implementation();
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.7;
          /**
           * @title   PayableProxyInterface
           * @author  OpenSea Protocol Team
           * @notice  PayableProxyInterface contains all external function interfaces
           *          for the payable proxy.
           */
          interface PayableProxyInterface {
              /**
               * @dev Fallback function that delegates calls to the address returned by
               *      `_implementation()`. Will run if no other function in the contract
               *      matches the call data.
               */
              fallback() external payable;
          }
          

          File 4 of 6: Conduit
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
          import { ConduitItemType } from "./lib/ConduitEnums.sol";
          import { TokenTransferrer } from "../lib/TokenTransferrer.sol";
          // prettier-ignore
          import {
              ConduitTransfer,
              ConduitBatch1155Transfer
          } from "./lib/ConduitStructs.sol";
          import "./lib/ConduitConstants.sol";
          /**
           * @title Conduit
           * @author 0age
           * @notice This contract serves as an originator for "proxied" transfers. Each
           *         conduit is deployed and controlled by a "conduit controller" that can
           *         add and remove "channels" or contracts that can instruct the conduit
           *         to transfer approved ERC20/721/1155 tokens. *IMPORTANT NOTE: each
           *         conduit has an owner that can arbitrarily add or remove channels, and
           *         a malicious or negligent owner can add a channel that allows for any
           *         approved ERC20/721/1155 tokens to be taken immediately — be extremely
           *         cautious with what conduits you give token approvals to!*
           */
          contract Conduit is ConduitInterface, TokenTransferrer {
              // Set deployer as an immutable controller that can update channel statuses.
              address private immutable _controller;
              // Track the status of each channel.
              mapping(address => bool) private _channels;
              /**
               * @notice Ensure that the caller is currently registered as an open channel
               *         on the conduit.
               */
              modifier onlyOpenChannel() {
                  // Utilize assembly to access channel storage mapping directly.
                  assembly {
                      // Write the caller to scratch space.
                      mstore(ChannelKey_channel_ptr, caller())
                      // Write the storage slot for _channels to scratch space.
                      mstore(ChannelKey_slot_ptr, _channels.slot)
                      // Derive the position in storage of _channels[msg.sender]
                      // and check if the stored value is zero.
                      if iszero(
                          sload(keccak256(ChannelKey_channel_ptr, ChannelKey_length))
                      ) {
                          // The caller is not an open channel; revert with
                          // ChannelClosed(caller). First, set error signature in memory.
                          mstore(ChannelClosed_error_ptr, ChannelClosed_error_signature)
                          // Next, set the caller as the argument.
                          mstore(ChannelClosed_channel_ptr, caller())
                          // Finally, revert, returning full custom error with argument.
                          revert(ChannelClosed_error_ptr, ChannelClosed_error_length)
                      }
                  }
                  // Continue with function execution.
                  _;
              }
              /**
               * @notice In the constructor, set the deployer as the controller.
               */
              constructor() {
                  // Set the deployer as the controller.
                  _controller = msg.sender;
              }
              /**
               * @notice Execute a sequence of ERC20/721/1155 transfers. Only a caller
               *         with an open channel can call this function. Note that channels
               *         are expected to implement reentrancy protection if desired, and
               *         that cross-channel reentrancy may be possible if the conduit has
               *         multiple open channels at once. Also note that channels are
               *         expected to implement checks against transferring any zero-amount
               *         items if that constraint is desired.
               *
               * @param transfers The ERC20/721/1155 transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function execute(ConduitTransfer[] calldata transfers)
                  external
                  override
                  onlyOpenChannel
                  returns (bytes4 magicValue)
              {
                  // Retrieve the total number of transfers and place on the stack.
                  uint256 totalStandardTransfers = transfers.length;
                  // Iterate over each transfer.
                  for (uint256 i = 0; i < totalStandardTransfers; ) {
                      // Retrieve the transfer in question and perform the transfer.
                      _transfer(transfers[i]);
                      // Skip overflow check as for loop is indexed starting at zero.
                      unchecked {
                          ++i;
                      }
                  }
                  // Return a magic value indicating that the transfers were performed.
                  magicValue = this.execute.selector;
              }
              /**
               * @notice Execute a sequence of batch 1155 item transfers. Only a caller
               *         with an open channel can call this function. Note that channels
               *         are expected to implement reentrancy protection if desired, and
               *         that cross-channel reentrancy may be possible if the conduit has
               *         multiple open channels at once. Also note that channels are
               *         expected to implement checks against transferring any zero-amount
               *         items if that constraint is desired.
               *
               * @param batchTransfers The 1155 batch item transfers to perform.
               *
               * @return magicValue A magic value indicating that the item transfers were
               *                    performed successfully.
               */
              function executeBatch1155(
                  ConduitBatch1155Transfer[] calldata batchTransfers
              ) external override onlyOpenChannel returns (bytes4 magicValue) {
                  // Perform 1155 batch transfers. Note that memory should be considered
                  // entirely corrupted from this point forward.
                  _performERC1155BatchTransfers(batchTransfers);
                  // Return a magic value indicating that the transfers were performed.
                  magicValue = this.executeBatch1155.selector;
              }
              /**
               * @notice Execute a sequence of transfers, both single ERC20/721/1155 item
               *         transfers as well as batch 1155 item transfers. Only a caller
               *         with an open channel can call this function. Note that channels
               *         are expected to implement reentrancy protection if desired, and
               *         that cross-channel reentrancy may be possible if the conduit has
               *         multiple open channels at once. Also note that channels are
               *         expected to implement checks against transferring any zero-amount
               *         items if that constraint is desired.
               *
               * @param standardTransfers The ERC20/721/1155 item transfers to perform.
               * @param batchTransfers    The 1155 batch item transfers to perform.
               *
               * @return magicValue A magic value indicating that the item transfers were
               *                    performed successfully.
               */
              function executeWithBatch1155(
                  ConduitTransfer[] calldata standardTransfers,
                  ConduitBatch1155Transfer[] calldata batchTransfers
              ) external override onlyOpenChannel returns (bytes4 magicValue) {
                  // Retrieve the total number of transfers and place on the stack.
                  uint256 totalStandardTransfers = standardTransfers.length;
                  // Iterate over each standard transfer.
                  for (uint256 i = 0; i < totalStandardTransfers; ) {
                      // Retrieve the transfer in question and perform the transfer.
                      _transfer(standardTransfers[i]);
                      // Skip overflow check as for loop is indexed starting at zero.
                      unchecked {
                          ++i;
                      }
                  }
                  // Perform 1155 batch transfers. Note that memory should be considered
                  // entirely corrupted from this point forward aside from the free memory
                  // pointer having the default value.
                  _performERC1155BatchTransfers(batchTransfers);
                  // Return a magic value indicating that the transfers were performed.
                  magicValue = this.executeWithBatch1155.selector;
              }
              /**
               * @notice Open or close a given channel. Only callable by the controller.
               *
               * @param channel The channel to open or close.
               * @param isOpen  The status of the channel (either open or closed).
               */
              function updateChannel(address channel, bool isOpen) external override {
                  // Ensure that the caller is the controller of this contract.
                  if (msg.sender != _controller) {
                      revert InvalidController();
                  }
                  // Ensure that the channel does not already have the indicated status.
                  if (_channels[channel] == isOpen) {
                      revert ChannelStatusAlreadySet(channel, isOpen);
                  }
                  // Update the status of the channel.
                  _channels[channel] = isOpen;
                  // Emit a corresponding event.
                  emit ChannelUpdated(channel, isOpen);
              }
              /**
               * @dev Internal function to transfer a given ERC20/721/1155 item. Note that
               *      channels are expected to implement checks against transferring any
               *      zero-amount items if that constraint is desired.
               *
               * @param item The ERC20/721/1155 item to transfer.
               */
              function _transfer(ConduitTransfer calldata item) internal {
                  // Determine the transfer method based on the respective item type.
                  if (item.itemType == ConduitItemType.ERC20) {
                      // Transfer ERC20 token. Note that item.identifier is ignored and
                      // therefore ERC20 transfer items are potentially malleable — this
                      // check should be performed by the calling channel if a constraint
                      // on item malleability is desired.
                      _performERC20Transfer(item.token, item.from, item.to, item.amount);
                  } else if (item.itemType == ConduitItemType.ERC721) {
                      // Ensure that exactly one 721 item is being transferred.
                      if (item.amount != 1) {
                          revert InvalidERC721TransferAmount();
                      }
                      // Transfer ERC721 token.
                      _performERC721Transfer(
                          item.token,
                          item.from,
                          item.to,
                          item.identifier
                      );
                  } else if (item.itemType == ConduitItemType.ERC1155) {
                      // Transfer ERC1155 token.
                      _performERC1155Transfer(
                          item.token,
                          item.from,
                          item.to,
                          item.identifier,
                          item.amount
                      );
                  } else {
                      // Throw with an error.
                      revert InvalidItemType();
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          // prettier-ignore
          import {
              ConduitTransfer,
              ConduitBatch1155Transfer
          } from "../conduit/lib/ConduitStructs.sol";
          /**
           * @title ConduitInterface
           * @author 0age
           * @notice ConduitInterface contains all external function interfaces, events,
           *         and errors for conduit contracts.
           */
          interface ConduitInterface {
              /**
               * @dev Revert with an error when attempting to execute transfers using a
               *      caller that does not have an open channel.
               */
              error ChannelClosed(address channel);
              /**
               * @dev Revert with an error when attempting to update a channel to the
               *      current status of that channel.
               */
              error ChannelStatusAlreadySet(address channel, bool isOpen);
              /**
               * @dev Revert with an error when attempting to execute a transfer for an
               *      item that does not have an ERC20/721/1155 item type.
               */
              error InvalidItemType();
              /**
               * @dev Revert with an error when attempting to update the status of a
               *      channel from a caller that is not the conduit controller.
               */
              error InvalidController();
              /**
               * @dev Emit an event whenever a channel is opened or closed.
               *
               * @param channel The channel that has been updated.
               * @param open    A boolean indicating whether the conduit is open or not.
               */
              event ChannelUpdated(address indexed channel, bool open);
              /**
               * @notice Execute a sequence of ERC20/721/1155 transfers. Only a caller
               *         with an open channel can call this function.
               *
               * @param transfers The ERC20/721/1155 transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function execute(ConduitTransfer[] calldata transfers)
                  external
                  returns (bytes4 magicValue);
              /**
               * @notice Execute a sequence of batch 1155 transfers. Only a caller with an
               *         open channel can call this function.
               *
               * @param batch1155Transfers The 1155 batch transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function executeBatch1155(
                  ConduitBatch1155Transfer[] calldata batch1155Transfers
              ) external returns (bytes4 magicValue);
              /**
               * @notice Execute a sequence of transfers, both single and batch 1155. Only
               *         a caller with an open channel can call this function.
               *
               * @param standardTransfers  The ERC20/721/1155 transfers to perform.
               * @param batch1155Transfers The 1155 batch transfers to perform.
               *
               * @return magicValue A magic value indicating that the transfers were
               *                    performed successfully.
               */
              function executeWithBatch1155(
                  ConduitTransfer[] calldata standardTransfers,
                  ConduitBatch1155Transfer[] calldata batch1155Transfers
              ) external returns (bytes4 magicValue);
              /**
               * @notice Open or close a given channel. Only callable by the controller.
               *
               * @param channel The channel to open or close.
               * @param isOpen  The status of the channel (either open or closed).
               */
              function updateChannel(address channel, bool isOpen) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          enum ConduitItemType {
              NATIVE, // unused
              ERC20,
              ERC721,
              ERC1155
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          import "./TokenTransferrerConstants.sol";
          // prettier-ignore
          import {
              TokenTransferrerErrors
          } from "../interfaces/TokenTransferrerErrors.sol";
          import { ConduitBatch1155Transfer } from "../conduit/lib/ConduitStructs.sol";
          /**
           * @title TokenTransferrer
           * @author 0age
           * @custom:coauthor d1ll0n
           * @custom:coauthor transmissions11
           * @notice TokenTransferrer is a library for performing optimized ERC20, ERC721,
           *         ERC1155, and batch ERC1155 transfers, used by both Seaport as well as
           *         by conduits deployed by the ConduitController. Use great caution when
           *         considering these functions for use in other codebases, as there are
           *         significant side effects and edge cases that need to be thoroughly
           *         understood and carefully addressed.
           */
          contract TokenTransferrer is TokenTransferrerErrors {
              /**
               * @dev Internal function to transfer ERC20 tokens from a given originator
               *      to a given recipient. Sufficient approvals must be set on the
               *      contract performing the transfer.
               *
               * @param token      The ERC20 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param amount     The amount to transfer.
               */
              function _performERC20Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 amount
              ) internal {
                  // Utilize assembly to perform an optimized ERC20 token transfer.
                  assembly {
                      // The free memory pointer memory slot will be used when populating
                      // call data for the transfer; read the value and restore it later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      // Write call data into memory, starting with function selector.
                      mstore(ERC20_transferFrom_sig_ptr, ERC20_transferFrom_signature)
                      mstore(ERC20_transferFrom_from_ptr, from)
                      mstore(ERC20_transferFrom_to_ptr, to)
                      mstore(ERC20_transferFrom_amount_ptr, amount)
                      // Make call & copy up to 32 bytes of return data to scratch space.
                      // Scratch space does not need to be cleared ahead of time, as the
                      // subsequent check will ensure that either at least a full word of
                      // return data is received (in which case it will be overwritten) or
                      // that no data is received (in which case scratch space will be
                      // ignored) on a successful call to the given token.
                      let callStatus := call(
                          gas(),
                          token,
                          0,
                          ERC20_transferFrom_sig_ptr,
                          ERC20_transferFrom_length,
                          0,
                          OneWord
                      )
                      // Determine whether transfer was successful using status & result.
                      let success := and(
                          // Set success to whether the call reverted, if not check it
                          // either returned exactly 1 (can't just be non-zero data), or
                          // had no return data.
                          or(
                              and(eq(mload(0), 1), gt(returndatasize(), 31)),
                              iszero(returndatasize())
                          ),
                          callStatus
                      )
                      // Handle cases where either the transfer failed or no data was
                      // returned. Group these, as most transfers will succeed with data.
                      // Equivalent to `or(iszero(success), iszero(returndatasize()))`
                      // but after it's inverted for JUMPI this expression is cheaper.
                      if iszero(and(success, iszero(iszero(returndatasize())))) {
                          // If the token has no code or the transfer failed: Equivalent
                          // to `or(iszero(success), iszero(extcodesize(token)))` but
                          // after it's inverted for JUMPI this expression is cheaper.
                          if iszero(and(iszero(iszero(extcodesize(token))), success)) {
                              // If the transfer failed:
                              if iszero(success) {
                                  // If it was due to a revert:
                                  if iszero(callStatus) {
                                      // If it returned a message, bubble it up as long as
                                      // sufficient gas remains to do so:
                                      if returndatasize() {
                                          // Ensure that sufficient gas is available to
                                          // copy returndata while expanding memory where
                                          // necessary. Start by computing the word size
                                          // of returndata and allocated memory. Round up
                                          // to the nearest full word.
                                          let returnDataWords := div(
                                              add(returndatasize(), AlmostOneWord),
                                              OneWord
                                          )
                                          // Note: use the free memory pointer in place of
                                          // msize() to work around a Yul warning that
                                          // prevents accessing msize directly when the IR
                                          // pipeline is activated.
                                          let msizeWords := div(memPointer, OneWord)
                                          // Next, compute the cost of the returndatacopy.
                                          let cost := mul(CostPerWord, returnDataWords)
                                          // Then, compute cost of new memory allocation.
                                          if gt(returnDataWords, msizeWords) {
                                              cost := add(
                                                  cost,
                                                  add(
                                                      mul(
                                                          sub(
                                                              returnDataWords,
                                                              msizeWords
                                                          ),
                                                          CostPerWord
                                                      ),
                                                      div(
                                                          sub(
                                                              mul(
                                                                  returnDataWords,
                                                                  returnDataWords
                                                              ),
                                                              mul(msizeWords, msizeWords)
                                                          ),
                                                          MemoryExpansionCoefficient
                                                      )
                                                  )
                                              )
                                          }
                                          // Finally, add a small constant and compare to
                                          // gas remaining; bubble up the revert data if
                                          // enough gas is still available.
                                          if lt(add(cost, ExtraGasBuffer), gas()) {
                                              // Copy returndata to memory; overwrite
                                              // existing memory.
                                              returndatacopy(0, 0, returndatasize())
                                              // Revert, specifying memory region with
                                              // copied returndata.
                                              revert(0, returndatasize())
                                          }
                                      }
                                      // Otherwise revert with a generic error message.
                                      mstore(
                                          TokenTransferGenericFailure_error_sig_ptr,
                                          TokenTransferGenericFailure_error_signature
                                      )
                                      mstore(
                                          TokenTransferGenericFailure_error_token_ptr,
                                          token
                                      )
                                      mstore(
                                          TokenTransferGenericFailure_error_from_ptr,
                                          from
                                      )
                                      mstore(TokenTransferGenericFailure_error_to_ptr, to)
                                      mstore(TokenTransferGenericFailure_error_id_ptr, 0)
                                      mstore(
                                          TokenTransferGenericFailure_error_amount_ptr,
                                          amount
                                      )
                                      revert(
                                          TokenTransferGenericFailure_error_sig_ptr,
                                          TokenTransferGenericFailure_error_length
                                      )
                                  }
                                  // Otherwise revert with a message about the token
                                  // returning false or non-compliant return values.
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_sig_ptr,
                                      BadReturnValueFromERC20OnTransfer_error_signature
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_token_ptr,
                                      token
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_from_ptr,
                                      from
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_to_ptr,
                                      to
                                  )
                                  mstore(
                                      BadReturnValueFromERC20OnTransfer_error_amount_ptr,
                                      amount
                                  )
                                  revert(
                                      BadReturnValueFromERC20OnTransfer_error_sig_ptr,
                                      BadReturnValueFromERC20OnTransfer_error_length
                                  )
                              }
                              // Otherwise, revert with error about token not having code:
                              mstore(NoContract_error_sig_ptr, NoContract_error_signature)
                              mstore(NoContract_error_token_ptr, token)
                              revert(NoContract_error_sig_ptr, NoContract_error_length)
                          }
                          // Otherwise, the token just returned no data despite the call
                          // having succeeded; no need to optimize for this as it's not
                          // technically ERC20 compliant.
                      }
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer an ERC721 token from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer. Note that this function does
               *      not check whether the receiver can accept the ERC721 token (i.e. it
               *      does not use `safeTransferFrom`).
               *
               * @param token      The ERC721 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param identifier The tokenId to transfer.
               */
              function _performERC721Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 identifier
              ) internal {
                  // Utilize assembly to perform an optimized ERC721 token transfer.
                  assembly {
                      // If the token has no code, revert.
                      if iszero(extcodesize(token)) {
                          mstore(NoContract_error_sig_ptr, NoContract_error_signature)
                          mstore(NoContract_error_token_ptr, token)
                          revert(NoContract_error_sig_ptr, NoContract_error_length)
                      }
                      // The free memory pointer memory slot will be used when populating
                      // call data for the transfer; read the value and restore it later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      // Write call data to memory starting with function selector.
                      mstore(ERC721_transferFrom_sig_ptr, ERC721_transferFrom_signature)
                      mstore(ERC721_transferFrom_from_ptr, from)
                      mstore(ERC721_transferFrom_to_ptr, to)
                      mstore(ERC721_transferFrom_id_ptr, identifier)
                      // Perform the call, ignoring return data.
                      let success := call(
                          gas(),
                          token,
                          0,
                          ERC721_transferFrom_sig_ptr,
                          ERC721_transferFrom_length,
                          0,
                          0
                      )
                      // If the transfer reverted:
                      if iszero(success) {
                          // If it returned a message, bubble it up as long as sufficient
                          // gas remains to do so:
                          if returndatasize() {
                              // Ensure that sufficient gas is available to copy
                              // returndata while expanding memory where necessary. Start
                              // by computing word size of returndata & allocated memory.
                              // Round up to the nearest full word.
                              let returnDataWords := div(
                                  add(returndatasize(), AlmostOneWord),
                                  OneWord
                              )
                              // Note: use the free memory pointer in place of msize() to
                              // work around a Yul warning that prevents accessing msize
                              // directly when the IR pipeline is activated.
                              let msizeWords := div(memPointer, OneWord)
                              // Next, compute the cost of the returndatacopy.
                              let cost := mul(CostPerWord, returnDataWords)
                              // Then, compute cost of new memory allocation.
                              if gt(returnDataWords, msizeWords) {
                                  cost := add(
                                      cost,
                                      add(
                                          mul(
                                              sub(returnDataWords, msizeWords),
                                              CostPerWord
                                          ),
                                          div(
                                              sub(
                                                  mul(returnDataWords, returnDataWords),
                                                  mul(msizeWords, msizeWords)
                                              ),
                                              MemoryExpansionCoefficient
                                          )
                                      )
                                  )
                              }
                              // Finally, add a small constant and compare to gas
                              // remaining; bubble up the revert data if enough gas is
                              // still available.
                              if lt(add(cost, ExtraGasBuffer), gas()) {
                                  // Copy returndata to memory; overwrite existing memory.
                                  returndatacopy(0, 0, returndatasize())
                                  // Revert, giving memory region with copied returndata.
                                  revert(0, returndatasize())
                              }
                          }
                          // Otherwise revert with a generic error message.
                          mstore(
                              TokenTransferGenericFailure_error_sig_ptr,
                              TokenTransferGenericFailure_error_signature
                          )
                          mstore(TokenTransferGenericFailure_error_token_ptr, token)
                          mstore(TokenTransferGenericFailure_error_from_ptr, from)
                          mstore(TokenTransferGenericFailure_error_to_ptr, to)
                          mstore(TokenTransferGenericFailure_error_id_ptr, identifier)
                          mstore(TokenTransferGenericFailure_error_amount_ptr, 1)
                          revert(
                              TokenTransferGenericFailure_error_sig_ptr,
                              TokenTransferGenericFailure_error_length
                          )
                      }
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer ERC1155 tokens from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer and contract recipients must
               *      implement the ERC1155TokenReceiver interface to indicate that they
               *      are willing to accept the transfer.
               *
               * @param token      The ERC1155 token to transfer.
               * @param from       The originator of the transfer.
               * @param to         The recipient of the transfer.
               * @param identifier The id to transfer.
               * @param amount     The amount to transfer.
               */
              function _performERC1155Transfer(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount
              ) internal {
                  // Utilize assembly to perform an optimized ERC1155 token transfer.
                  assembly {
                      // If the token has no code, revert.
                      if iszero(extcodesize(token)) {
                          mstore(NoContract_error_sig_ptr, NoContract_error_signature)
                          mstore(NoContract_error_token_ptr, token)
                          revert(NoContract_error_sig_ptr, NoContract_error_length)
                      }
                      // The following memory slots will be used when populating call data
                      // for the transfer; read the values and restore them later.
                      let memPointer := mload(FreeMemoryPointerSlot)
                      let slot0x80 := mload(Slot0x80)
                      let slot0xA0 := mload(Slot0xA0)
                      let slot0xC0 := mload(Slot0xC0)
                      // Write call data into memory, beginning with function selector.
                      mstore(
                          ERC1155_safeTransferFrom_sig_ptr,
                          ERC1155_safeTransferFrom_signature
                      )
                      mstore(ERC1155_safeTransferFrom_from_ptr, from)
                      mstore(ERC1155_safeTransferFrom_to_ptr, to)
                      mstore(ERC1155_safeTransferFrom_id_ptr, identifier)
                      mstore(ERC1155_safeTransferFrom_amount_ptr, amount)
                      mstore(
                          ERC1155_safeTransferFrom_data_offset_ptr,
                          ERC1155_safeTransferFrom_data_length_offset
                      )
                      mstore(ERC1155_safeTransferFrom_data_length_ptr, 0)
                      // Perform the call, ignoring return data.
                      let success := call(
                          gas(),
                          token,
                          0,
                          ERC1155_safeTransferFrom_sig_ptr,
                          ERC1155_safeTransferFrom_length,
                          0,
                          0
                      )
                      // If the transfer reverted:
                      if iszero(success) {
                          // If it returned a message, bubble it up as long as sufficient
                          // gas remains to do so:
                          if returndatasize() {
                              // Ensure that sufficient gas is available to copy
                              // returndata while expanding memory where necessary. Start
                              // by computing word size of returndata & allocated memory.
                              // Round up to the nearest full word.
                              let returnDataWords := div(
                                  add(returndatasize(), AlmostOneWord),
                                  OneWord
                              )
                              // Note: use the free memory pointer in place of msize() to
                              // work around a Yul warning that prevents accessing msize
                              // directly when the IR pipeline is activated.
                              let msizeWords := div(memPointer, OneWord)
                              // Next, compute the cost of the returndatacopy.
                              let cost := mul(CostPerWord, returnDataWords)
                              // Then, compute cost of new memory allocation.
                              if gt(returnDataWords, msizeWords) {
                                  cost := add(
                                      cost,
                                      add(
                                          mul(
                                              sub(returnDataWords, msizeWords),
                                              CostPerWord
                                          ),
                                          div(
                                              sub(
                                                  mul(returnDataWords, returnDataWords),
                                                  mul(msizeWords, msizeWords)
                                              ),
                                              MemoryExpansionCoefficient
                                          )
                                      )
                                  )
                              }
                              // Finally, add a small constant and compare to gas
                              // remaining; bubble up the revert data if enough gas is
                              // still available.
                              if lt(add(cost, ExtraGasBuffer), gas()) {
                                  // Copy returndata to memory; overwrite existing memory.
                                  returndatacopy(0, 0, returndatasize())
                                  // Revert, giving memory region with copied returndata.
                                  revert(0, returndatasize())
                              }
                          }
                          // Otherwise revert with a generic error message.
                          mstore(
                              TokenTransferGenericFailure_error_sig_ptr,
                              TokenTransferGenericFailure_error_signature
                          )
                          mstore(TokenTransferGenericFailure_error_token_ptr, token)
                          mstore(TokenTransferGenericFailure_error_from_ptr, from)
                          mstore(TokenTransferGenericFailure_error_to_ptr, to)
                          mstore(TokenTransferGenericFailure_error_id_ptr, identifier)
                          mstore(TokenTransferGenericFailure_error_amount_ptr, amount)
                          revert(
                              TokenTransferGenericFailure_error_sig_ptr,
                              TokenTransferGenericFailure_error_length
                          )
                      }
                      mstore(Slot0x80, slot0x80) // Restore slot 0x80.
                      mstore(Slot0xA0, slot0xA0) // Restore slot 0xA0.
                      mstore(Slot0xC0, slot0xC0) // Restore slot 0xC0.
                      // Restore the original free memory pointer.
                      mstore(FreeMemoryPointerSlot, memPointer)
                      // Restore the zero slot to zero.
                      mstore(ZeroSlot, 0)
                  }
              }
              /**
               * @dev Internal function to transfer ERC1155 tokens from a given
               *      originator to a given recipient. Sufficient approvals must be set on
               *      the contract performing the transfer and contract recipients must
               *      implement the ERC1155TokenReceiver interface to indicate that they
               *      are willing to accept the transfer. NOTE: this function is not
               *      memory-safe; it will overwrite existing memory, restore the free
               *      memory pointer to the default value, and overwrite the zero slot.
               *      This function should only be called once memory is no longer
               *      required and when uninitialized arrays are not utilized, and memory
               *      should be considered fully corrupted (aside from the existence of a
               *      default-value free memory pointer) after calling this function.
               *
               * @param batchTransfers The group of 1155 batch transfers to perform.
               */
              function _performERC1155BatchTransfers(
                  ConduitBatch1155Transfer[] calldata batchTransfers
              ) internal {
                  // Utilize assembly to perform optimized batch 1155 transfers.
                  assembly {
                      let len := batchTransfers.length
                      // Pointer to first head in the array, which is offset to the struct
                      // at each index. This gets incremented after each loop to avoid
                      // multiplying by 32 to get the offset for each element.
                      let nextElementHeadPtr := batchTransfers.offset
                      // Pointer to beginning of the head of the array. This is the
                      // reference position each offset references. It's held static to
                      // let each loop calculate the data position for an element.
                      let arrayHeadPtr := nextElementHeadPtr
                      // Write the function selector, which will be reused for each call:
                      // safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)
                      mstore(
                          ConduitBatch1155Transfer_from_offset,
                          ERC1155_safeBatchTransferFrom_signature
                      )
                      // Iterate over each batch transfer.
                      for {
                          let i := 0
                      } lt(i, len) {
                          i := add(i, 1)
                      } {
                          // Read the offset to the beginning of the element and add
                          // it to pointer to the beginning of the array head to get
                          // the absolute position of the element in calldata.
                          let elementPtr := add(
                              arrayHeadPtr,
                              calldataload(nextElementHeadPtr)
                          )
                          // Retrieve the token from calldata.
                          let token := calldataload(elementPtr)
                          // If the token has no code, revert.
                          if iszero(extcodesize(token)) {
                              mstore(NoContract_error_sig_ptr, NoContract_error_signature)
                              mstore(NoContract_error_token_ptr, token)
                              revert(NoContract_error_sig_ptr, NoContract_error_length)
                          }
                          // Get the total number of supplied ids.
                          let idsLength := calldataload(
                              add(elementPtr, ConduitBatch1155Transfer_ids_length_offset)
                          )
                          // Determine the expected offset for the amounts array.
                          let expectedAmountsOffset := add(
                              ConduitBatch1155Transfer_amounts_length_baseOffset,
                              mul(idsLength, OneWord)
                          )
                          // Validate struct encoding.
                          let invalidEncoding := iszero(
                              and(
                                  // ids.length == amounts.length
                                  eq(
                                      idsLength,
                                      calldataload(add(elementPtr, expectedAmountsOffset))
                                  ),
                                  and(
                                      // ids_offset == 0xa0
                                      eq(
                                          calldataload(
                                              add(
                                                  elementPtr,
                                                  ConduitBatch1155Transfer_ids_head_offset
                                              )
                                          ),
                                          ConduitBatch1155Transfer_ids_length_offset
                                      ),
                                      // amounts_offset == 0xc0 + ids.length*32
                                      eq(
                                          calldataload(
                                              add(
                                                  elementPtr,
                                                  ConduitBatchTransfer_amounts_head_offset
                                              )
                                          ),
                                          expectedAmountsOffset
                                      )
                                  )
                              )
                          )
                          // Revert with an error if the encoding is not valid.
                          if invalidEncoding {
                              mstore(
                                  Invalid1155BatchTransferEncoding_ptr,
                                  Invalid1155BatchTransferEncoding_selector
                              )
                              revert(
                                  Invalid1155BatchTransferEncoding_ptr,
                                  Invalid1155BatchTransferEncoding_length
                              )
                          }
                          // Update the offset position for the next loop
                          nextElementHeadPtr := add(nextElementHeadPtr, OneWord)
                          // Copy the first section of calldata (before dynamic values).
                          calldatacopy(
                              BatchTransfer1155Params_ptr,
                              add(elementPtr, ConduitBatch1155Transfer_from_offset),
                              ConduitBatch1155Transfer_usable_head_size
                          )
                          // Determine size of calldata required for ids and amounts. Note
                          // that the size includes both lengths as well as the data.
                          let idsAndAmountsSize := add(TwoWords, mul(idsLength, TwoWords))
                          // Update the offset for the data array in memory.
                          mstore(
                              BatchTransfer1155Params_data_head_ptr,
                              add(
                                  BatchTransfer1155Params_ids_length_offset,
                                  idsAndAmountsSize
                              )
                          )
                          // Set the length of the data array in memory to zero.
                          mstore(
                              add(
                                  BatchTransfer1155Params_data_length_basePtr,
                                  idsAndAmountsSize
                              ),
                              0
                          )
                          // Determine the total calldata size for the call to transfer.
                          let transferDataSize := add(
                              BatchTransfer1155Params_calldata_baseSize,
                              idsAndAmountsSize
                          )
                          // Copy second section of calldata (including dynamic values).
                          calldatacopy(
                              BatchTransfer1155Params_ids_length_ptr,
                              add(elementPtr, ConduitBatch1155Transfer_ids_length_offset),
                              idsAndAmountsSize
                          )
                          // Perform the call to transfer 1155 tokens.
                          let success := call(
                              gas(),
                              token,
                              0,
                              ConduitBatch1155Transfer_from_offset, // Data portion start.
                              transferDataSize, // Location of the length of callData.
                              0,
                              0
                          )
                          // If the transfer reverted:
                          if iszero(success) {
                              // If it returned a message, bubble it up as long as
                              // sufficient gas remains to do so:
                              if returndatasize() {
                                  // Ensure that sufficient gas is available to copy
                                  // returndata while expanding memory where necessary.
                                  // Start by computing word size of returndata and
                                  // allocated memory. Round up to the nearest full word.
                                  let returnDataWords := div(
                                      add(returndatasize(), AlmostOneWord),
                                      OneWord
                                  )
                                  // Note: use transferDataSize in place of msize() to
                                  // work around a Yul warning that prevents accessing
                                  // msize directly when the IR pipeline is activated.
                                  // The free memory pointer is not used here because
                                  // this function does almost all memory management
                                  // manually and does not update it, and transferDataSize
                                  // should be the largest memory value used (unless a
                                  // previous batch was larger).
                                  let msizeWords := div(transferDataSize, OneWord)
                                  // Next, compute the cost of the returndatacopy.
                                  let cost := mul(CostPerWord, returnDataWords)
                                  // Then, compute cost of new memory allocation.
                                  if gt(returnDataWords, msizeWords) {
                                      cost := add(
                                          cost,
                                          add(
                                              mul(
                                                  sub(returnDataWords, msizeWords),
                                                  CostPerWord
                                              ),
                                              div(
                                                  sub(
                                                      mul(
                                                          returnDataWords,
                                                          returnDataWords
                                                      ),
                                                      mul(msizeWords, msizeWords)
                                                  ),
                                                  MemoryExpansionCoefficient
                                              )
                                          )
                                      )
                                  }
                                  // Finally, add a small constant and compare to gas
                                  // remaining; bubble up the revert data if enough gas is
                                  // still available.
                                  if lt(add(cost, ExtraGasBuffer), gas()) {
                                      // Copy returndata to memory; overwrite existing.
                                      returndatacopy(0, 0, returndatasize())
                                      // Revert with memory region containing returndata.
                                      revert(0, returndatasize())
                                  }
                              }
                              // Set the error signature.
                              mstore(
                                  0,
                                  ERC1155BatchTransferGenericFailure_error_signature
                              )
                              // Write the token.
                              mstore(ERC1155BatchTransferGenericFailure_token_ptr, token)
                              // Increase the offset to ids by 32.
                              mstore(
                                  BatchTransfer1155Params_ids_head_ptr,
                                  ERC1155BatchTransferGenericFailure_ids_offset
                              )
                              // Increase the offset to amounts by 32.
                              mstore(
                                  BatchTransfer1155Params_amounts_head_ptr,
                                  add(
                                      OneWord,
                                      mload(BatchTransfer1155Params_amounts_head_ptr)
                                  )
                              )
                              // Return modified region. The total size stays the same as
                              // `token` uses the same number of bytes as `data.length`.
                              revert(0, transferDataSize)
                          }
                      }
                      // Reset the free memory pointer to the default value; memory must
                      // be assumed to be dirtied and not reused from this point forward.
                      // Also note that the zero slot is not reset to zero, meaning empty
                      // arrays cannot be safely created or utilized until it is restored.
                      mstore(FreeMemoryPointerSlot, DefaultFreeMemoryPointer)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          import { ConduitItemType } from "./ConduitEnums.sol";
          struct ConduitTransfer {
              ConduitItemType itemType;
              address token;
              address from;
              address to;
              uint256 identifier;
              uint256 amount;
          }
          struct ConduitBatch1155Transfer {
              address token;
              address from;
              address to;
              uint256[] ids;
              uint256[] amounts;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          // error ChannelClosed(address channel)
          uint256 constant ChannelClosed_error_signature = (
              0x93daadf200000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ChannelClosed_error_ptr = 0x00;
          uint256 constant ChannelClosed_channel_ptr = 0x4;
          uint256 constant ChannelClosed_error_length = 0x24;
          // For the mapping:
          // mapping(address => bool) channels
          // The position in storage for a particular account is:
          // keccak256(abi.encode(account, channels.slot))
          uint256 constant ChannelKey_channel_ptr = 0x00;
          uint256 constant ChannelKey_slot_ptr = 0x20;
          uint256 constant ChannelKey_length = 0x40;
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          /*
           * -------------------------- Disambiguation & Other Notes ---------------------
           *    - The term "head" is used as it is in the documentation for ABI encoding,
           *      but only in reference to dynamic types, i.e. it always refers to the
           *      offset or pointer to the body of a dynamic type. In calldata, the head
           *      is always an offset (relative to the parent object), while in memory,
           *      the head is always the pointer to the body. More information found here:
           *      https://docs.soliditylang.org/en/v0.8.14/abi-spec.html#argument-encoding
           *        - Note that the length of an array is separate from and precedes the
           *          head of the array.
           *
           *    - The term "body" is used in place of the term "head" used in the ABI
           *      documentation. It refers to the start of the data for a dynamic type,
           *      e.g. the first word of a struct or the first word of the first element
           *      in an array.
           *
           *    - The term "pointer" is used to describe the absolute position of a value
           *      and never an offset relative to another value.
           *        - The suffix "_ptr" refers to a memory pointer.
           *        - The suffix "_cdPtr" refers to a calldata pointer.
           *
           *    - The term "offset" is used to describe the position of a value relative
           *      to some parent value. For example, OrderParameters_conduit_offset is the
           *      offset to the "conduit" value in the OrderParameters struct relative to
           *      the start of the body.
           *        - Note: Offsets are used to derive pointers.
           *
           *    - Some structs have pointers defined for all of their fields in this file.
           *      Lines which are commented out are fields that are not used in the
           *      codebase but have been left in for readability.
           */
          uint256 constant AlmostOneWord = 0x1f;
          uint256 constant OneWord = 0x20;
          uint256 constant TwoWords = 0x40;
          uint256 constant ThreeWords = 0x60;
          uint256 constant FreeMemoryPointerSlot = 0x40;
          uint256 constant ZeroSlot = 0x60;
          uint256 constant DefaultFreeMemoryPointer = 0x80;
          uint256 constant Slot0x80 = 0x80;
          uint256 constant Slot0xA0 = 0xa0;
          uint256 constant Slot0xC0 = 0xc0;
          // abi.encodeWithSignature("transferFrom(address,address,uint256)")
          uint256 constant ERC20_transferFrom_signature = (
              0x23b872dd00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC20_transferFrom_sig_ptr = 0x0;
          uint256 constant ERC20_transferFrom_from_ptr = 0x04;
          uint256 constant ERC20_transferFrom_to_ptr = 0x24;
          uint256 constant ERC20_transferFrom_amount_ptr = 0x44;
          uint256 constant ERC20_transferFrom_length = 0x64; // 4 + 32 * 3 == 100
          // abi.encodeWithSignature(
          //     "safeTransferFrom(address,address,uint256,uint256,bytes)"
          // )
          uint256 constant ERC1155_safeTransferFrom_signature = (
              0xf242432a00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155_safeTransferFrom_sig_ptr = 0x0;
          uint256 constant ERC1155_safeTransferFrom_from_ptr = 0x04;
          uint256 constant ERC1155_safeTransferFrom_to_ptr = 0x24;
          uint256 constant ERC1155_safeTransferFrom_id_ptr = 0x44;
          uint256 constant ERC1155_safeTransferFrom_amount_ptr = 0x64;
          uint256 constant ERC1155_safeTransferFrom_data_offset_ptr = 0x84;
          uint256 constant ERC1155_safeTransferFrom_data_length_ptr = 0xa4;
          uint256 constant ERC1155_safeTransferFrom_length = 0xc4; // 4 + 32 * 6 == 196
          uint256 constant ERC1155_safeTransferFrom_data_length_offset = 0xa0;
          // abi.encodeWithSignature(
          //     "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"
          // )
          uint256 constant ERC1155_safeBatchTransferFrom_signature = (
              0x2eb2c2d600000000000000000000000000000000000000000000000000000000
          );
          bytes4 constant ERC1155_safeBatchTransferFrom_selector = bytes4(
              bytes32(ERC1155_safeBatchTransferFrom_signature)
          );
          uint256 constant ERC721_transferFrom_signature = ERC20_transferFrom_signature;
          uint256 constant ERC721_transferFrom_sig_ptr = 0x0;
          uint256 constant ERC721_transferFrom_from_ptr = 0x04;
          uint256 constant ERC721_transferFrom_to_ptr = 0x24;
          uint256 constant ERC721_transferFrom_id_ptr = 0x44;
          uint256 constant ERC721_transferFrom_length = 0x64; // 4 + 32 * 3 == 100
          // abi.encodeWithSignature("NoContract(address)")
          uint256 constant NoContract_error_signature = (
              0x5f15d67200000000000000000000000000000000000000000000000000000000
          );
          uint256 constant NoContract_error_sig_ptr = 0x0;
          uint256 constant NoContract_error_token_ptr = 0x4;
          uint256 constant NoContract_error_length = 0x24; // 4 + 32 == 36
          // abi.encodeWithSignature(
          //     "TokenTransferGenericFailure(address,address,address,uint256,uint256)"
          // )
          uint256 constant TokenTransferGenericFailure_error_signature = (
              0xf486bc8700000000000000000000000000000000000000000000000000000000
          );
          uint256 constant TokenTransferGenericFailure_error_sig_ptr = 0x0;
          uint256 constant TokenTransferGenericFailure_error_token_ptr = 0x4;
          uint256 constant TokenTransferGenericFailure_error_from_ptr = 0x24;
          uint256 constant TokenTransferGenericFailure_error_to_ptr = 0x44;
          uint256 constant TokenTransferGenericFailure_error_id_ptr = 0x64;
          uint256 constant TokenTransferGenericFailure_error_amount_ptr = 0x84;
          // 4 + 32 * 5 == 164
          uint256 constant TokenTransferGenericFailure_error_length = 0xa4;
          // abi.encodeWithSignature(
          //     "BadReturnValueFromERC20OnTransfer(address,address,address,uint256)"
          // )
          uint256 constant BadReturnValueFromERC20OnTransfer_error_signature = (
              0x9889192300000000000000000000000000000000000000000000000000000000
          );
          uint256 constant BadReturnValueFromERC20OnTransfer_error_sig_ptr = 0x0;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_token_ptr = 0x4;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_from_ptr = 0x24;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_to_ptr = 0x44;
          uint256 constant BadReturnValueFromERC20OnTransfer_error_amount_ptr = 0x64;
          // 4 + 32 * 4 == 132
          uint256 constant BadReturnValueFromERC20OnTransfer_error_length = 0x84;
          uint256 constant ExtraGasBuffer = 0x20;
          uint256 constant CostPerWord = 3;
          uint256 constant MemoryExpansionCoefficient = 0x200;
          // Values are offset by 32 bytes in order to write the token to the beginning
          // in the event of a revert
          uint256 constant BatchTransfer1155Params_ptr = 0x24;
          uint256 constant BatchTransfer1155Params_ids_head_ptr = 0x64;
          uint256 constant BatchTransfer1155Params_amounts_head_ptr = 0x84;
          uint256 constant BatchTransfer1155Params_data_head_ptr = 0xa4;
          uint256 constant BatchTransfer1155Params_data_length_basePtr = 0xc4;
          uint256 constant BatchTransfer1155Params_calldata_baseSize = 0xc4;
          uint256 constant BatchTransfer1155Params_ids_length_ptr = 0xc4;
          uint256 constant BatchTransfer1155Params_ids_length_offset = 0xa0;
          uint256 constant BatchTransfer1155Params_amounts_length_baseOffset = 0xc0;
          uint256 constant BatchTransfer1155Params_data_length_baseOffset = 0xe0;
          uint256 constant ConduitBatch1155Transfer_usable_head_size = 0x80;
          uint256 constant ConduitBatch1155Transfer_from_offset = 0x20;
          uint256 constant ConduitBatch1155Transfer_ids_head_offset = 0x60;
          uint256 constant ConduitBatch1155Transfer_amounts_head_offset = 0x80;
          uint256 constant ConduitBatch1155Transfer_ids_length_offset = 0xa0;
          uint256 constant ConduitBatch1155Transfer_amounts_length_baseOffset = 0xc0;
          uint256 constant ConduitBatch1155Transfer_calldata_baseSize = 0xc0;
          // Note: abbreviated version of above constant to adhere to line length limit.
          uint256 constant ConduitBatchTransfer_amounts_head_offset = 0x80;
          uint256 constant Invalid1155BatchTransferEncoding_ptr = 0x00;
          uint256 constant Invalid1155BatchTransferEncoding_length = 0x04;
          uint256 constant Invalid1155BatchTransferEncoding_selector = (
              0xeba2084c00000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155BatchTransferGenericFailure_error_signature = (
              0xafc445e200000000000000000000000000000000000000000000000000000000
          );
          uint256 constant ERC1155BatchTransferGenericFailure_token_ptr = 0x04;
          uint256 constant ERC1155BatchTransferGenericFailure_ids_offset = 0xc0;
          // SPDX-License-Identifier: MIT
          pragma solidity >=0.8.7;
          /**
           * @title TokenTransferrerErrors
           */
          interface TokenTransferrerErrors {
              /**
               * @dev Revert with an error when an ERC721 transfer with amount other than
               *      one is attempted.
               */
              error InvalidERC721TransferAmount();
              /**
               * @dev Revert with an error when attempting to fulfill an order where an
               *      item has an amount of zero.
               */
              error MissingItemAmount();
              /**
               * @dev Revert with an error when attempting to fulfill an order where an
               *      item has unused parameters. This includes both the token and the
               *      identifier parameters for native transfers as well as the identifier
               *      parameter for ERC20 transfers. Note that the conduit does not
               *      perform this check, leaving it up to the calling channel to enforce
               *      when desired.
               */
              error UnusedItemParameters();
              /**
               * @dev Revert with an error when an ERC20, ERC721, or ERC1155 token
               *      transfer reverts.
               *
               * @param token      The token for which the transfer was attempted.
               * @param from       The source of the attempted transfer.
               * @param to         The recipient of the attempted transfer.
               * @param identifier The identifier for the attempted transfer.
               * @param amount     The amount for the attempted transfer.
               */
              error TokenTransferGenericFailure(
                  address token,
                  address from,
                  address to,
                  uint256 identifier,
                  uint256 amount
              );
              /**
               * @dev Revert with an error when a batch ERC1155 token transfer reverts.
               *
               * @param token       The token for which the transfer was attempted.
               * @param from        The source of the attempted transfer.
               * @param to          The recipient of the attempted transfer.
               * @param identifiers The identifiers for the attempted transfer.
               * @param amounts     The amounts for the attempted transfer.
               */
              error ERC1155BatchTransferGenericFailure(
                  address token,
                  address from,
                  address to,
                  uint256[] identifiers,
                  uint256[] amounts
              );
              /**
               * @dev Revert with an error when an ERC20 token transfer returns a falsey
               *      value.
               *
               * @param token      The token for which the ERC20 transfer was attempted.
               * @param from       The source of the attempted ERC20 transfer.
               * @param to         The recipient of the attempted ERC20 transfer.
               * @param amount     The amount for the attempted ERC20 transfer.
               */
              error BadReturnValueFromERC20OnTransfer(
                  address token,
                  address from,
                  address to,
                  uint256 amount
              );
              /**
               * @dev Revert with an error when an account being called as an assumed
               *      contract does not have code and returns no data.
               *
               * @param account The account that should contain code.
               */
              error NoContract(address account);
              /**
               * @dev Revert with an error when attempting to execute an 1155 batch
               *      transfer using calldata not produced by default ABI encoding or with
               *      different lengths for ids and amounts arrays.
               */
              error Invalid1155BatchTransferEncoding();
          }
          

          File 5 of 6: Bueno721Drop
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/ContextUpgradeable.sol";
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              function __Ownable_init() internal onlyInitializing {
                  __Ownable_init_unchained();
              }
              function __Ownable_init_unchained() internal onlyInitializing {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (finance/PaymentSplitter.sol)
          pragma solidity ^0.8.0;
          import "../token/ERC20/utils/SafeERC20Upgradeable.sol";
          import "../utils/AddressUpgradeable.sol";
          import "../utils/ContextUpgradeable.sol";
          import "../proxy/utils/Initializable.sol";
          /**
           * @title PaymentSplitter
           * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
           * that the Ether will be split in this way, since it is handled transparently by the contract.
           *
           * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
           * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
           * an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
           * time of contract deployment and can't be updated thereafter.
           *
           * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
           * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
           * function.
           *
           * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
           * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
           * to run tests before sending real value to this contract.
           */
          contract PaymentSplitterUpgradeable is Initializable, ContextUpgradeable {
              event PayeeAdded(address account, uint256 shares);
              event PaymentReleased(address to, uint256 amount);
              event ERC20PaymentReleased(IERC20Upgradeable indexed token, address to, uint256 amount);
              event PaymentReceived(address from, uint256 amount);
              uint256 private _totalShares;
              uint256 private _totalReleased;
              mapping(address => uint256) private _shares;
              mapping(address => uint256) private _released;
              address[] private _payees;
              mapping(IERC20Upgradeable => uint256) private _erc20TotalReleased;
              mapping(IERC20Upgradeable => mapping(address => uint256)) private _erc20Released;
              /**
               * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
               * the matching position in the `shares` array.
               *
               * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
               * duplicates in `payees`.
               */
              function __PaymentSplitter_init(address[] memory payees, uint256[] memory shares_) internal onlyInitializing {
                  __PaymentSplitter_init_unchained(payees, shares_);
              }
              function __PaymentSplitter_init_unchained(address[] memory payees, uint256[] memory shares_) internal onlyInitializing {
                  require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
                  require(payees.length > 0, "PaymentSplitter: no payees");
                  for (uint256 i = 0; i < payees.length; i++) {
                      _addPayee(payees[i], shares_[i]);
                  }
              }
              /**
               * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
               * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
               * reliability of the events, and not the actual splitting of Ether.
               *
               * To learn more about this see the Solidity documentation for
               * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
               * functions].
               */
              receive() external payable virtual {
                  emit PaymentReceived(_msgSender(), msg.value);
              }
              /**
               * @dev Getter for the total shares held by payees.
               */
              function totalShares() public view returns (uint256) {
                  return _totalShares;
              }
              /**
               * @dev Getter for the total amount of Ether already released.
               */
              function totalReleased() public view returns (uint256) {
                  return _totalReleased;
              }
              /**
               * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
               * contract.
               */
              function totalReleased(IERC20Upgradeable token) public view returns (uint256) {
                  return _erc20TotalReleased[token];
              }
              /**
               * @dev Getter for the amount of shares held by an account.
               */
              function shares(address account) public view returns (uint256) {
                  return _shares[account];
              }
              /**
               * @dev Getter for the amount of Ether already released to a payee.
               */
              function released(address account) public view returns (uint256) {
                  return _released[account];
              }
              /**
               * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
               * IERC20 contract.
               */
              function released(IERC20Upgradeable token, address account) public view returns (uint256) {
                  return _erc20Released[token][account];
              }
              /**
               * @dev Getter for the address of the payee number `index`.
               */
              function payee(uint256 index) public view returns (address) {
                  return _payees[index];
              }
              /**
               * @dev Getter for the amount of payee's releasable Ether.
               */
              function releasable(address account) public view returns (uint256) {
                  uint256 totalReceived = address(this).balance + totalReleased();
                  return _pendingPayment(account, totalReceived, released(account));
              }
              /**
               * @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
               * IERC20 contract.
               */
              function releasable(IERC20Upgradeable token, address account) public view returns (uint256) {
                  uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
                  return _pendingPayment(account, totalReceived, released(token, account));
              }
              /**
               * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
               * total shares and their previous withdrawals.
               */
              function release(address payable account) public virtual {
                  require(_shares[account] > 0, "PaymentSplitter: account has no shares");
                  uint256 payment = releasable(account);
                  require(payment != 0, "PaymentSplitter: account is not due payment");
                  // _totalReleased is the sum of all values in _released.
                  // If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
                  _totalReleased += payment;
                  unchecked {
                      _released[account] += payment;
                  }
                  AddressUpgradeable.sendValue(account, payment);
                  emit PaymentReleased(account, payment);
              }
              /**
               * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
               * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
               * contract.
               */
              function release(IERC20Upgradeable token, address account) public virtual {
                  require(_shares[account] > 0, "PaymentSplitter: account has no shares");
                  uint256 payment = releasable(token, account);
                  require(payment != 0, "PaymentSplitter: account is not due payment");
                  // _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
                  // If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
                  // cannot overflow.
                  _erc20TotalReleased[token] += payment;
                  unchecked {
                      _erc20Released[token][account] += payment;
                  }
                  SafeERC20Upgradeable.safeTransfer(token, account, payment);
                  emit ERC20PaymentReleased(token, account, payment);
              }
              /**
               * @dev internal logic for computing the pending payment of an `account` given the token historical balances and
               * already released amounts.
               */
              function _pendingPayment(
                  address account,
                  uint256 totalReceived,
                  uint256 alreadyReleased
              ) private view returns (uint256) {
                  return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
              }
              /**
               * @dev Add a new payee to the contract.
               * @param account The address of the payee to add.
               * @param shares_ The number of shares owned by the payee.
               */
              function _addPayee(address account, uint256 shares_) private {
                  require(account != address(0), "PaymentSplitter: account is the zero address");
                  require(shares_ > 0, "PaymentSplitter: shares are 0");
                  require(_shares[account] == 0, "PaymentSplitter: account already has shares");
                  _payees.push(account);
                  _shares[account] = shares_;
                  _totalShares = _totalShares + shares_;
                  emit PayeeAdded(account, shares_);
              }
              /**
               * @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[43] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
          pragma solidity ^0.8.0;
          import "../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev Interface for the NFT Royalty Standard.
           *
           * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
           * support for royalty payments across all NFT marketplaces and ecosystem participants.
           *
           * _Available since v4.5._
           */
          interface IERC2981Upgradeable is IERC165Upgradeable {
              /**
               * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
               * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
               */
              function royaltyInfo(uint256 tokenId, uint256 salePrice)
                  external
                  view
                  returns (address receiver, uint256 royaltyAmount);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
          pragma solidity ^0.8.2;
          import "../../utils/AddressUpgradeable.sol";
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               * @custom:oz-retyped-from bool
               */
              uint8 private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint8 version);
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts.
               *
               * 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 Internal function that returns the initialized version. Returns `_initialized`
               */
              function _getInitializedVersion() internal view returns (uint8) {
                  return _initialized;
              }
              /**
               * @dev Internal function that returns the initialized version. Returns `_initializing`
               */
              function _isInitializing() internal view returns (bool) {
                  return _initializing;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
          pragma solidity ^0.8.0;
          import "../../interfaces/IERC2981Upgradeable.sol";
          import "../../utils/introspection/ERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
           *
           * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
           * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
           *
           * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
           * fee is specified in basis points by default.
           *
           * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
           * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
           * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
           *
           * _Available since v4.5._
           */
          abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
              function __ERC2981_init() internal onlyInitializing {
              }
              function __ERC2981_init_unchained() internal onlyInitializing {
              }
              struct RoyaltyInfo {
                  address receiver;
                  uint96 royaltyFraction;
              }
              RoyaltyInfo private _defaultRoyaltyInfo;
              mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
                  return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
              }
              /**
               * @inheritdoc IERC2981Upgradeable
               */
              function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
                  RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
                  if (royalty.receiver == address(0)) {
                      royalty = _defaultRoyaltyInfo;
                  }
                  uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
                  return (royalty.receiver, royaltyAmount);
              }
              /**
               * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
               * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
               * override.
               */
              function _feeDenominator() internal pure virtual returns (uint96) {
                  return 10000;
              }
              /**
               * @dev Sets the royalty information that all ids in this contract will default to.
               *
               * Requirements:
               *
               * - `receiver` cannot be the zero address.
               * - `feeNumerator` cannot be greater than the fee denominator.
               */
              function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
                  require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                  require(receiver != address(0), "ERC2981: invalid receiver");
                  _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
              }
              /**
               * @dev Removes default royalty information.
               */
              function _deleteDefaultRoyalty() internal virtual {
                  delete _defaultRoyaltyInfo;
              }
              /**
               * @dev Sets the royalty information for a specific token id, overriding the global default.
               *
               * Requirements:
               *
               * - `receiver` cannot be the zero address.
               * - `feeNumerator` cannot be greater than the fee denominator.
               */
              function _setTokenRoyalty(
                  uint256 tokenId,
                  address receiver,
                  uint96 feeNumerator
              ) internal virtual {
                  require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                  require(receiver != address(0), "ERC2981: Invalid parameters");
                  _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
              }
              /**
               * @dev Resets royalty information for the token id back to the global default.
               */
              function _resetTokenRoyalty(uint256 tokenId) internal virtual {
                  delete _tokenRoyaltyInfo[tokenId];
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[48] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol)
          pragma solidity ^0.8.0;
          import "./IERC1155Upgradeable.sol";
          import "./IERC1155ReceiverUpgradeable.sol";
          import "./extensions/IERC1155MetadataURIUpgradeable.sol";
          import "../../utils/AddressUpgradeable.sol";
          import "../../utils/ContextUpgradeable.sol";
          import "../../utils/introspection/ERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the basic standard multi-token.
           * See https://eips.ethereum.org/EIPS/eip-1155
           * Originally based on code by Enjin: https://github.com/enjin/erc-1155
           *
           * _Available since v3.1._
           */
          contract ERC1155Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC1155Upgradeable, IERC1155MetadataURIUpgradeable {
              using AddressUpgradeable for address;
              // Mapping from token ID to account balances
              mapping(uint256 => mapping(address => uint256)) private _balances;
              // Mapping from account to operator approvals
              mapping(address => mapping(address => bool)) private _operatorApprovals;
              // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
              string private _uri;
              /**
               * @dev See {_setURI}.
               */
              function __ERC1155_init(string memory uri_) internal onlyInitializing {
                  __ERC1155_init_unchained(uri_);
              }
              function __ERC1155_init_unchained(string memory uri_) internal onlyInitializing {
                  _setURI(uri_);
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                  return
                      interfaceId == type(IERC1155Upgradeable).interfaceId ||
                      interfaceId == type(IERC1155MetadataURIUpgradeable).interfaceId ||
                      super.supportsInterface(interfaceId);
              }
              /**
               * @dev See {IERC1155MetadataURI-uri}.
               *
               * This implementation returns the same URI for *all* token types. It relies
               * on the token type ID substitution mechanism
               * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
               *
               * Clients calling this function must replace the `\\{id\\}` substring with the
               * actual token type ID.
               */
              function uri(uint256) public view virtual override returns (string memory) {
                  return _uri;
              }
              /**
               * @dev See {IERC1155-balanceOf}.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
                  require(account != address(0), "ERC1155: address zero is not a valid owner");
                  return _balances[id][account];
              }
              /**
               * @dev See {IERC1155-balanceOfBatch}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
                  public
                  view
                  virtual
                  override
                  returns (uint256[] memory)
              {
                  require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
                  uint256[] memory batchBalances = new uint256[](accounts.length);
                  for (uint256 i = 0; i < accounts.length; ++i) {
                      batchBalances[i] = balanceOf(accounts[i], ids[i]);
                  }
                  return batchBalances;
              }
              /**
               * @dev See {IERC1155-setApprovalForAll}.
               */
              function setApprovalForAll(address operator, bool approved) public virtual override {
                  _setApprovalForAll(_msgSender(), operator, approved);
              }
              /**
               * @dev See {IERC1155-isApprovedForAll}.
               */
              function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
                  return _operatorApprovals[account][operator];
              }
              /**
               * @dev See {IERC1155-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) public virtual override {
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: caller is not token owner or approved"
                  );
                  _safeTransferFrom(from, to, id, amount, data);
              }
              /**
               * @dev See {IERC1155-safeBatchTransferFrom}.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) public virtual override {
                  require(
                      from == _msgSender() || isApprovedForAll(from, _msgSender()),
                      "ERC1155: caller is not token owner or approved"
                  );
                  _safeBatchTransferFrom(from, to, ids, amounts, data);
              }
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function _safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  _balances[id][to] += amount;
                  emit TransferSingle(operator, from, to, id, amount);
                  _afterTokenTransfer(operator, from, to, ids, amounts, data);
                  _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function _safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  require(to != address(0), "ERC1155: transfer to the zero address");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  for (uint256 i = 0; i < ids.length; ++i) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 fromBalance = _balances[id][from];
                      require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                      unchecked {
                          _balances[id][from] = fromBalance - amount;
                      }
                      _balances[id][to] += amount;
                  }
                  emit TransferBatch(operator, from, to, ids, amounts);
                  _afterTokenTransfer(operator, from, to, ids, amounts, data);
                  _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
              }
              /**
               * @dev Sets a new URI for all token types, by relying on the token type ID
               * substitution mechanism
               * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
               *
               * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
               * URI or any of the amounts in the JSON file at said URI will be replaced by
               * clients with the token type ID.
               *
               * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
               * interpreted by clients as
               * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
               * for token type ID 0x4cce0.
               *
               * See {uri}.
               *
               * Because these URIs cannot be meaningfully represented by the {URI} event,
               * this function emits no events.
               */
              function _setURI(string memory newuri) internal virtual {
                  _uri = newuri;
              }
              /**
               * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function _mint(
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: mint to the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _balances[id][to] += amount;
                  emit TransferSingle(operator, address(0), to, id, amount);
                  _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function _mintBatch(
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {
                  require(to != address(0), "ERC1155: mint to the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
                  for (uint256 i = 0; i < ids.length; i++) {
                      _balances[ids[i]][to] += amounts[i];
                  }
                  emit TransferBatch(operator, address(0), to, ids, amounts);
                  _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
                  _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
              }
              /**
               * @dev Destroys `amount` tokens of token type `id` from `from`
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `from` must have at least `amount` tokens of token type `id`.
               */
              function _burn(
                  address from,
                  uint256 id,
                  uint256 amount
              ) internal virtual {
                  require(from != address(0), "ERC1155: burn from the zero address");
                  address operator = _msgSender();
                  uint256[] memory ids = _asSingletonArray(id);
                  uint256[] memory amounts = _asSingletonArray(amount);
                  _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                  uint256 fromBalance = _balances[id][from];
                  require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                  unchecked {
                      _balances[id][from] = fromBalance - amount;
                  }
                  emit TransferSingle(operator, from, address(0), id, amount);
                  _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
              }
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               */
              function _burnBatch(
                  address from,
                  uint256[] memory ids,
                  uint256[] memory amounts
              ) internal virtual {
                  require(from != address(0), "ERC1155: burn from the zero address");
                  require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
                  address operator = _msgSender();
                  _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
                  for (uint256 i = 0; i < ids.length; i++) {
                      uint256 id = ids[i];
                      uint256 amount = amounts[i];
                      uint256 fromBalance = _balances[id][from];
                      require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                      unchecked {
                          _balances[id][from] = fromBalance - amount;
                      }
                  }
                  emit TransferBatch(operator, from, address(0), ids, amounts);
                  _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
              }
              /**
               * @dev Approve `operator` to operate on all of `owner` tokens
               *
               * Emits an {ApprovalForAll} event.
               */
              function _setApprovalForAll(
                  address owner,
                  address operator,
                  bool approved
              ) internal virtual {
                  require(owner != operator, "ERC1155: setting approval status for self");
                  _operatorApprovals[owner][operator] = approved;
                  emit ApprovalForAll(owner, operator, approved);
              }
              /**
               * @dev Hook that is called before any token transfer. This includes minting
               * and burning, as well as batched variants.
               *
               * The same hook is called on both single and batched variants. For single
               * transfers, the length of the `ids` and `amounts` arrays will be 1.
               *
               * Calling conditions (for each `id` and `amount` pair):
               *
               * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * of token type `id` will be  transferred to `to`.
               * - When `from` is zero, `amount` tokens of token type `id` will be minted
               * for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
               * will be burned.
               * - `from` and `to` are never both zero.
               * - `ids` and `amounts` have the same, non-zero length.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _beforeTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {}
              /**
               * @dev Hook that is called after any token transfer. This includes minting
               * and burning, as well as batched variants.
               *
               * The same hook is called on both single and batched variants. For single
               * transfers, the length of the `id` and `amount` arrays will be 1.
               *
               * Calling conditions (for each `id` and `amount` pair):
               *
               * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
               * of token type `id` will be  transferred to `to`.
               * - When `from` is zero, `amount` tokens of token type `id` will be minted
               * for `to`.
               * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
               * will be burned.
               * - `from` and `to` are never both zero.
               * - `ids` and `amounts` have the same, non-zero length.
               *
               * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
               */
              function _afterTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual {}
              function _doSafeTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes memory data
              ) private {
                  if (to.isContract()) {
                      try IERC1155ReceiverUpgradeable(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                          if (response != IERC1155ReceiverUpgradeable.onERC1155Received.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                      }
                  }
              }
              function _doSafeBatchTransferAcceptanceCheck(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) private {
                  if (to.isContract()) {
                      try IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                          bytes4 response
                      ) {
                          if (response != IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector) {
                              revert("ERC1155: ERC1155Receiver rejected tokens");
                          }
                      } catch Error(string memory reason) {
                          revert(reason);
                      } catch {
                          revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                      }
                  }
              }
              function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
                  uint256[] memory array = new uint256[](1);
                  array[0] = element;
                  return array;
              }
              /**
               * @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[47] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)
          pragma solidity ^0.8.0;
          import "../ERC1155Upgradeable.sol";
          import "../../../proxy/utils/Initializable.sol";
          /**
           * @dev Extension of ERC1155 that adds tracking of total supply per id.
           *
           * Useful for scenarios where Fungible and Non-fungible tokens have to be
           * clearly identified. Note: While a totalSupply of 1 might mean the
           * corresponding is an NFT, there is no guarantees that no other token with the
           * same id are not going to be minted.
           */
          abstract contract ERC1155SupplyUpgradeable is Initializable, ERC1155Upgradeable {
              function __ERC1155Supply_init() internal onlyInitializing {
              }
              function __ERC1155Supply_init_unchained() internal onlyInitializing {
              }
              mapping(uint256 => uint256) private _totalSupply;
              /**
               * @dev Total amount of tokens in with a given id.
               */
              function totalSupply(uint256 id) public view virtual returns (uint256) {
                  return _totalSupply[id];
              }
              /**
               * @dev Indicates whether any token exist with a given id, or not.
               */
              function exists(uint256 id) public view virtual returns (bool) {
                  return ERC1155SupplyUpgradeable.totalSupply(id) > 0;
              }
              /**
               * @dev See {ERC1155-_beforeTokenTransfer}.
               */
              function _beforeTokenTransfer(
                  address operator,
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) internal virtual override {
                  super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
                  if (from == address(0)) {
                      for (uint256 i = 0; i < ids.length; ++i) {
                          _totalSupply[ids[i]] += amounts[i];
                      }
                  }
                  if (to == address(0)) {
                      for (uint256 i = 0; i < ids.length; ++i) {
                          uint256 id = ids[i];
                          uint256 amount = amounts[i];
                          uint256 supply = _totalSupply[id];
                          require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                          unchecked {
                              _totalSupply[id] = supply - amount;
                          }
                      }
                  }
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[49] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
          pragma solidity ^0.8.0;
          import "../IERC1155Upgradeable.sol";
          /**
           * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
           * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
              /**
               * @dev Returns the URI for token type `id`.
               *
               * If the `\\{id\\}` substring is present in the URI, it must be replaced by
               * clients with the actual token type ID.
               */
              function uri(uint256 id) external view returns (string memory);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev _Available since v3.1._
           */
          interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {
              /**
               * @dev Handles the receipt of a single ERC1155 token type. This function is
               * called at the end of a `safeTransferFrom` after the balance has been updated.
               *
               * NOTE: To accept the transfer, this must return
               * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
               * (i.e. 0xf23a6e61, or its own function selector).
               *
               * @param operator The address which initiated the transfer (i.e. msg.sender)
               * @param from The address which previously owned the token
               * @param id The ID of the token being transferred
               * @param value The amount of tokens being transferred
               * @param data Additional data with no specified format
               * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
               */
              function onERC1155Received(
                  address operator,
                  address from,
                  uint256 id,
                  uint256 value,
                  bytes calldata data
              ) external returns (bytes4);
              /**
               * @dev Handles the receipt of a multiple ERC1155 token types. This function
               * is called at the end of a `safeBatchTransferFrom` after the balances have
               * been updated.
               *
               * NOTE: To accept the transfer(s), this must return
               * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
               * (i.e. 0xbc197c81, or its own function selector).
               *
               * @param operator The address which initiated the batch transfer (i.e. msg.sender)
               * @param from The address which previously owned the token
               * @param ids An array containing ids of each token being transferred (order and length must match values array)
               * @param values An array containing amounts of each token being transferred (order and length must match ids array)
               * @param data Additional data with no specified format
               * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
               */
              function onERC1155BatchReceived(
                  address operator,
                  address from,
                  uint256[] calldata ids,
                  uint256[] calldata values,
                  bytes calldata data
              ) external returns (bytes4);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
          pragma solidity ^0.8.0;
          import "../../utils/introspection/IERC165Upgradeable.sol";
          /**
           * @dev Required interface of an ERC1155 compliant contract, as defined in the
           * https://eips.ethereum.org/EIPS/eip-1155[EIP].
           *
           * _Available since v3.1._
           */
          interface IERC1155Upgradeable is IERC165Upgradeable {
              /**
               * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
               */
              event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
              /**
               * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
               * transfers.
               */
              event TransferBatch(
                  address indexed operator,
                  address indexed from,
                  address indexed to,
                  uint256[] ids,
                  uint256[] values
              );
              /**
               * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
               * `approved`.
               */
              event ApprovalForAll(address indexed account, address indexed operator, bool approved);
              /**
               * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
               *
               * If an {URI} event was emitted for `id`, the standard
               * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
               * returned by {IERC1155MetadataURI-uri}.
               */
              event URI(string value, uint256 indexed id);
              /**
               * @dev Returns the amount of tokens of token type `id` owned by `account`.
               *
               * Requirements:
               *
               * - `account` cannot be the zero address.
               */
              function balanceOf(address account, uint256 id) external view returns (uint256);
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
               *
               * Requirements:
               *
               * - `accounts` and `ids` must have the same length.
               */
              function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
                  external
                  view
                  returns (uint256[] memory);
              /**
               * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
               *
               * Emits an {ApprovalForAll} event.
               *
               * Requirements:
               *
               * - `operator` cannot be the caller.
               */
              function setApprovalForAll(address operator, bool approved) external;
              /**
               * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
               *
               * See {setApprovalForAll}.
               */
              function isApprovedForAll(address account, address operator) external view returns (bool);
              /**
               * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
               *
               * Emits a {TransferSingle} event.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
               * - `from` must have a balance of tokens of type `id` of at least `amount`.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
               * acceptance magic value.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 id,
                  uint256 amount,
                  bytes calldata data
              ) external;
              /**
               * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
               *
               * Emits a {TransferBatch} event.
               *
               * Requirements:
               *
               * - `ids` and `amounts` must have the same length.
               * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
               * acceptance magic value.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] calldata ids,
                  uint256[] calldata amounts,
                  bytes calldata data
              ) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
           *
           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
           * need to send a transaction, and thus is not required to hold Ether at all.
           */
          interface IERC20PermitUpgradeable {
              /**
               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
               * given ``owner``'s signed approval.
               *
               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
               * ordering also apply here.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `deadline` must be a timestamp in the future.
               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
               * over the EIP712-formatted function arguments.
               * - the signature must use ``owner``'s current nonce (see {nonces}).
               *
               * For more information on the signature format, see the
               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
               * section].
               */
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) external;
              /**
               * @dev Returns the current nonce for `owner`. This value must be
               * included whenever a signature is generated for {permit}.
               *
               * Every successful call to {permit} increases ``owner``'s nonce by one. This
               * prevents a signature from being used multiple times.
               */
              function nonces(address owner) external view returns (uint256);
              /**
               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
               */
              // solhint-disable-next-line func-name-mixedcase
              function DOMAIN_SEPARATOR() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20Upgradeable {
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `from` to `to` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 amount
              ) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
          pragma solidity ^0.8.0;
          import "../IERC20Upgradeable.sol";
          import "../extensions/draft-IERC20PermitUpgradeable.sol";
          import "../../../utils/AddressUpgradeable.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20Upgradeable {
              using AddressUpgradeable for address;
              function safeTransfer(
                  IERC20Upgradeable token,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(
                  IERC20Upgradeable token,
                  address from,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  require(
                      (value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  uint256 newAllowance = token.allowance(address(this), spender) + value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(
                  IERC20Upgradeable token,
                  address spender,
                  uint256 value
              ) internal {
                  unchecked {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                      uint256 newAllowance = oldAllowance - value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
              }
              function safePermit(
                  IERC20PermitUpgradeable token,
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal {
                  uint256 nonceBefore = token.nonces(owner);
                  token.permit(owner, spender, value, deadline, v, r, s);
                  uint256 nonceAfter = token.nonces(owner);
                  require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) {
                      // Return data is optional
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
          pragma solidity ^0.8.1;
          /**
           * @dev Collection of functions related to the address type
           */
          library AddressUpgradeable {
              /**
               * @dev Returns true if `account` is a contract.
               *
               * [IMPORTANT]
               * ====
               * It is unsafe to assume that an address for which this function returns
               * false is an externally-owned account (EOA) and not a contract.
               *
               * Among others, `isContract` will return false for the following
               * types of addresses:
               *
               *  - an externally-owned account
               *  - a contract in construction
               *  - an address where a contract will be created
               *  - an address where a contract lived, but was destroyed
               * ====
               *
               * [IMPORTANT]
               * ====
               * You shouldn't rely on `isContract` to protect against flash loan attacks!
               *
               * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
               * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
               * constructor.
               * ====
               */
              function isContract(address account) internal view returns (bool) {
                  // This method relies on extcodesize/address.code.length, which returns 0
                  // for contracts in construction, since the code is only stored at the end
                  // of the constructor execution.
                  return account.code.length > 0;
              }
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, "Address: insufficient balance");
                  (bool success, ) = recipient.call{value: amount}("");
                  require(success, "Address: unable to send value, recipient may have reverted");
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain `call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason, it is bubbled up by this
               * function (like regular Solidity function calls).
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               *
               * _Available since v3.1._
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, "Address: low-level call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
               * `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value
              ) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
              }
              /**
               * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
               * with `errorMessage` as a fallback revert reason when `target` reverts.
               *
               * _Available since v3.1._
               */
              function functionCallWithValue(
                  address target,
                  bytes memory data,
                  uint256 value,
                  string memory errorMessage
              ) internal returns (bytes memory) {
                  require(address(this).balance >= value, "Address: insufficient balance for call");
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  return functionStaticCall(target, data, "Address: low-level static call failed");
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
               * but performing a static call.
               *
               * _Available since v3.3._
               */
              function functionStaticCall(
                  address target,
                  bytes memory data,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResultFromTarget(target, success, returndata, errorMessage);
              }
              /**
               * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
               * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
               *
               * _Available since v4.8._
               */
              function verifyCallResultFromTarget(
                  address target,
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal view returns (bytes memory) {
                  if (success) {
                      if (returndata.length == 0) {
                          // only check isContract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          require(isContract(target), "Address: call to non-contract");
                      }
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
              /**
               * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
               * revert reason or using the provided one.
               *
               * _Available since v4.3._
               */
              function verifyCallResult(
                  bool success,
                  bytes memory returndata,
                  string memory errorMessage
              ) internal pure returns (bytes memory) {
                  if (success) {
                      return returndata;
                  } else {
                      _revert(returndata, errorMessage);
                  }
              }
              function _revert(bytes memory returndata, string memory errorMessage) private pure {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      /// @solidity memory-safe-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          import "../proxy/utils/Initializable.sol";
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract ContextUpgradeable is Initializable {
              function __Context_init() internal onlyInitializing {
              }
              function __Context_init_unchained() internal onlyInitializing {
              }
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          pragma solidity ^0.8.0;
          import "./IERC165Upgradeable.sol";
          import "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
              function __ERC165_init() internal onlyInitializing {
              }
              function __ERC165_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165Upgradeable).interfaceId;
              }
              /**
               * @dev This empty reserved space is put in place to allow future versions to add new
               * variables without shifting down storage in the inheritance chain.
               * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
               */
              uint256[50] private __gap;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165Upgradeable {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
          pragma solidity ^0.8.0;
          import "./IAccessControl.sol";
          import "../utils/Context.sol";
          import "../utils/Strings.sol";
          import "../utils/introspection/ERC165.sol";
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms. This is a lightweight version that doesn't allow enumerating role
           * members except through off-chain means by accessing the contract event logs. Some
           * applications may benefit from on-chain enumerability, for those cases see
           * {AccessControlEnumerable}.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it.
           */
          abstract contract AccessControl is Context, IAccessControl, ERC165 {
              struct RoleData {
                  mapping(address => bool) members;
                  bytes32 adminRole;
              }
              mapping(bytes32 => RoleData) private _roles;
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
              /**
               * @dev Modifier that checks that an account has a specific role. Reverts
               * with a standardized message including the required role.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               *
               * _Available since v4.1._
               */
              modifier onlyRole(bytes32 role) {
                  _checkRole(role);
                  _;
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
              }
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                  return _roles[role].members[account];
              }
              /**
               * @dev Revert with a standard message if `_msgSender()` is missing `role`.
               * Overriding this function changes the behavior of the {onlyRole} modifier.
               *
               * Format of the revert message is described in {_checkRole}.
               *
               * _Available since v4.6._
               */
              function _checkRole(bytes32 role) internal view virtual {
                  _checkRole(role, _msgSender());
              }
              /**
               * @dev Revert with a standard message if `account` is missing `role`.
               *
               * The format of the revert reason is given by the following regular expression:
               *
               *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
               */
              function _checkRole(bytes32 role, address account) internal view virtual {
                  if (!hasRole(role, account)) {
                      revert(
                          string(
                              abi.encodePacked(
                                  "AccessControl: account ",
                                  Strings.toHexString(account),
                                  " is missing role ",
                                  Strings.toHexString(uint256(role), 32)
                              )
                          )
                      );
                  }
              }
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                  return _roles[role].adminRole;
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleGranted} event.
               */
              function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _grantRole(role, account);
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleRevoked} event.
               */
              function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                  _revokeRole(role, account);
              }
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been revoked `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               *
               * May emit a {RoleRevoked} event.
               */
              function renounceRole(bytes32 role, address account) public virtual override {
                  require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                  _revokeRole(role, account);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event. Note that unlike {grantRole}, this function doesn't perform any
               * checks on the calling account.
               *
               * May emit a {RoleGranted} event.
               *
               * [WARNING]
               * ====
               * This function should only be called from the constructor when setting
               * up the initial roles for the system.
               *
               * Using this function in any other way is effectively circumventing the admin
               * system imposed by {AccessControl}.
               * ====
               *
               * NOTE: This function is deprecated in favor of {_grantRole}.
               */
              function _setupRole(bytes32 role, address account) internal virtual {
                  _grantRole(role, account);
              }
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  bytes32 previousAdminRole = getRoleAdmin(role);
                  _roles[role].adminRole = adminRole;
                  emit RoleAdminChanged(role, previousAdminRole, adminRole);
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleGranted} event.
               */
              function _grantRole(bytes32 role, address account) internal virtual {
                  if (!hasRole(role, account)) {
                      _roles[role].members[account] = true;
                      emit RoleGranted(role, account, _msgSender());
                  }
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleRevoked} event.
               */
              function _revokeRole(bytes32 role, address account) internal virtual {
                  if (hasRole(role, account)) {
                      _roles[role].members[account] = false;
                      emit RoleRevoked(role, account, _msgSender());
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev External interface of AccessControl declared to support ERC165 detection.
           */
          interface IAccessControl {
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               *
               * _Available since v3.1._
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call, an admin role
               * bearer except when using {AccessControl-_setupRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) external view returns (bool);
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {AccessControl-_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) external view returns (bytes32);
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `account`.
               */
              function renounceRole(bytes32 role, address account) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
           * deploying minimal proxy contracts, also known as "clones".
           *
           * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
           * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
           *
           * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
           * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
           * deterministic method.
           *
           * _Available since v3.4._
           */
          library Clones {
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create opcode, which should never revert.
               */
              function clone(address implementation) internal returns (address instance) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create(0, 0x09, 0x37)
                  }
                  require(instance != address(0), "ERC1167: create failed");
              }
              /**
               * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
               *
               * This function uses the create2 opcode and a `salt` to deterministically deploy
               * the clone. Using the same `implementation` and `salt` multiple time will revert, since
               * the clones cannot be deployed twice at the same address.
               */
              function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
                      // of the `implementation` address with the bytecode before the address.
                      mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
                      // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
                      mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
                      instance := create2(0, 0x09, 0x37, salt)
                  }
                  require(instance != address(0), "ERC1167: create2 failed");
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(
                  address implementation,
                  bytes32 salt,
                  address deployer
              ) internal pure returns (address predicted) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      let ptr := mload(0x40)
                      mstore(add(ptr, 0x38), deployer)
                      mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
                      mstore(add(ptr, 0x14), implementation)
                      mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
                      mstore(add(ptr, 0x58), salt)
                      mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
                      predicted := keccak256(add(ptr, 0x43), 0x55)
                  }
              }
              /**
               * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
               */
              function predictDeterministicAddress(address implementation, bytes32 salt)
                  internal
                  view
                  returns (address predicted)
              {
                  return predictDeterministicAddress(implementation, salt, address(this));
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev These functions deal with verification of Merkle Tree proofs.
           *
           * The tree and the proofs can be generated using our
           * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
           * You will find a quickstart guide in the readme.
           *
           * WARNING: You should avoid using leaf values that are 64 bytes long prior to
           * hashing, or use a hash function other than keccak256 for hashing leaves.
           * This is because the concatenation of a sorted pair of internal nodes in
           * the merkle tree could be reinterpreted as a leaf value.
           * OpenZeppelin's JavaScript library generates merkle trees that are safe
           * against this attack out of the box.
           */
          library MerkleProof {
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               */
              function verify(
                  bytes32[] memory proof,
                  bytes32 root,
                  bytes32 leaf
              ) internal pure returns (bool) {
                  return processProof(proof, leaf) == root;
              }
              /**
               * @dev Calldata version of {verify}
               *
               * _Available since v4.7._
               */
              function verifyCalldata(
                  bytes32[] calldata proof,
                  bytes32 root,
                  bytes32 leaf
              ) internal pure returns (bool) {
                  return processProofCalldata(proof, leaf) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leafs & pre-images are assumed to be sorted.
               *
               * _Available since v4.4._
               */
              function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Calldata version of {processProof}
               *
               * _Available since v4.7._
               */
              function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = _hashPair(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerify(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProof(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Calldata version of {multiProofVerify}
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function multiProofVerifyCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProofCalldata(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * _Available since v4.7._
               */
              function processMultiProof(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 totalHashes = proofFlags.length;
                  // Check proof validity.
                  require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
                  if (totalHashes > 0) {
                      return hashes[totalHashes - 1];
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              /**
               * @dev Calldata version of {processMultiProof}.
               *
               * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * _Available since v4.7._
               */
              function processMultiProofCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 totalHashes = proofFlags.length;
                  // Check proof validity.
                  require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](totalHashes);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < totalHashes; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
                      hashes[i] = _hashPair(a, b);
                  }
                  if (totalHashes > 0) {
                      return hashes[totalHashes - 1];
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                  return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
              }
              function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      mstore(0x00, a)
                      mstore(0x20, b)
                      value := keccak256(0x00, 0x40)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
          pragma solidity ^0.8.0;
          import "./IERC165.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           *
           * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
           */
          abstract contract ERC165 is IERC165 {
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IERC165).interfaceId;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[EIP].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Standard math utilities missing in the Solidity language.
           */
          library Math {
              enum Rounding {
                  Down, // Toward negative infinity
                  Up, // Toward infinity
                  Zero // Toward zero
              }
              /**
               * @dev Returns the largest of two numbers.
               */
              function max(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a > b ? a : b;
              }
              /**
               * @dev Returns the smallest of two numbers.
               */
              function min(uint256 a, uint256 b) internal pure returns (uint256) {
                  return a < b ? a : b;
              }
              /**
               * @dev Returns the average of two numbers. The result is rounded towards
               * zero.
               */
              function average(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b) / 2 can overflow.
                  return (a & b) + (a ^ b) / 2;
              }
              /**
               * @dev Returns the ceiling of the division of two numbers.
               *
               * This differs from standard division with `/` in that it rounds up instead
               * of rounding down.
               */
              function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                  // (a + b - 1) / b can overflow on addition, so we distribute.
                  return a == 0 ? 0 : (a - 1) / b + 1;
              }
              /**
               * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
               * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
               * with further edits by Uniswap Labs also under MIT license.
               */
              function mulDiv(
                  uint256 x,
                  uint256 y,
                  uint256 denominator
              ) internal pure returns (uint256 result) {
                  unchecked {
                      // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                      // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                      // variables such that product = prod1 * 2^256 + prod0.
                      uint256 prod0; // Least significant 256 bits of the product
                      uint256 prod1; // Most significant 256 bits of the product
                      assembly {
                          let mm := mulmod(x, y, not(0))
                          prod0 := mul(x, y)
                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                      }
                      // Handle non-overflow cases, 256 by 256 division.
                      if (prod1 == 0) {
                          return prod0 / denominator;
                      }
                      // Make sure the result is less than 2^256. Also prevents denominator == 0.
                      require(denominator > prod1);
                      ///////////////////////////////////////////////
                      // 512 by 256 division.
                      ///////////////////////////////////////////////
                      // Make division exact by subtracting the remainder from [prod1 prod0].
                      uint256 remainder;
                      assembly {
                          // Compute remainder using mulmod.
                          remainder := mulmod(x, y, denominator)
                          // Subtract 256 bit number from 512 bit number.
                          prod1 := sub(prod1, gt(remainder, prod0))
                          prod0 := sub(prod0, remainder)
                      }
                      // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                      // See https://cs.stackexchange.com/q/138556/92363.
                      // Does not overflow because the denominator cannot be zero at this stage in the function.
                      uint256 twos = denominator & (~denominator + 1);
                      assembly {
                          // Divide denominator by twos.
                          denominator := div(denominator, twos)
                          // Divide [prod1 prod0] by twos.
                          prod0 := div(prod0, twos)
                          // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                          twos := add(div(sub(0, twos), twos), 1)
                      }
                      // Shift in bits from prod1 into prod0.
                      prod0 |= prod1 * twos;
                      // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                      // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                      // four bits. That is, denominator * inv = 1 mod 2^4.
                      uint256 inverse = (3 * denominator) ^ 2;
                      // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                      // in modular arithmetic, doubling the correct bits in each step.
                      inverse *= 2 - denominator * inverse; // inverse mod 2^8
                      inverse *= 2 - denominator * inverse; // inverse mod 2^16
                      inverse *= 2 - denominator * inverse; // inverse mod 2^32
                      inverse *= 2 - denominator * inverse; // inverse mod 2^64
                      inverse *= 2 - denominator * inverse; // inverse mod 2^128
                      inverse *= 2 - denominator * inverse; // inverse mod 2^256
                      // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                      // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                      // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                      // is no longer required.
                      result = prod0 * inverse;
                      return result;
                  }
              }
              /**
               * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
               */
              function mulDiv(
                  uint256 x,
                  uint256 y,
                  uint256 denominator,
                  Rounding rounding
              ) internal pure returns (uint256) {
                  uint256 result = mulDiv(x, y, denominator);
                  if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                      result += 1;
                  }
                  return result;
              }
              /**
               * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
               *
               * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
               */
              function sqrt(uint256 a) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
                  // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                  //
                  // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                  // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                  //
                  // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                  // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                  // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                  //
                  // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                  uint256 result = 1 << (log2(a) >> 1);
                  // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                  // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                  // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                  // into the expected uint128 result.
                  unchecked {
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      result = (result + a / result) >> 1;
                      return min(result, a / result);
                  }
              }
              /**
               * @notice Calculates sqrt(a), following the selected rounding direction.
               */
              function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = sqrt(a);
                      return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 2, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 128;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 64;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 32;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 16;
                      }
                      if (value >> 8 > 0) {
                          value >>= 8;
                          result += 8;
                      }
                      if (value >> 4 > 0) {
                          value >>= 4;
                          result += 4;
                      }
                      if (value >> 2 > 0) {
                          value >>= 2;
                          result += 2;
                      }
                      if (value >> 1 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log2(value);
                      return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 10, rounded down, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >= 10**64) {
                          value /= 10**64;
                          result += 64;
                      }
                      if (value >= 10**32) {
                          value /= 10**32;
                          result += 32;
                      }
                      if (value >= 10**16) {
                          value /= 10**16;
                          result += 16;
                      }
                      if (value >= 10**8) {
                          value /= 10**8;
                          result += 8;
                      }
                      if (value >= 10**4) {
                          value /= 10**4;
                          result += 4;
                      }
                      if (value >= 10**2) {
                          value /= 10**2;
                          result += 2;
                      }
                      if (value >= 10**1) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log10(value);
                      return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                  }
              }
              /**
               * @dev Return the log in base 256, rounded down, of a positive value.
               * Returns 0 if given 0.
               *
               * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
               */
              function log256(uint256 value) internal pure returns (uint256) {
                  uint256 result = 0;
                  unchecked {
                      if (value >> 128 > 0) {
                          value >>= 128;
                          result += 16;
                      }
                      if (value >> 64 > 0) {
                          value >>= 64;
                          result += 8;
                      }
                      if (value >> 32 > 0) {
                          value >>= 32;
                          result += 4;
                      }
                      if (value >> 16 > 0) {
                          value >>= 16;
                          result += 2;
                      }
                      if (value >> 8 > 0) {
                          result += 1;
                      }
                  }
                  return result;
              }
              /**
               * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
               * Returns 0 if given 0.
               */
              function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                  unchecked {
                      uint256 result = log256(value);
                      return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
          pragma solidity ^0.8.0;
          import "./math/Math.sol";
          /**
           * @dev String operations.
           */
          library Strings {
              bytes16 private constant _SYMBOLS = "0123456789abcdef";
              uint8 private constant _ADDRESS_LENGTH = 20;
              /**
               * @dev Converts a `uint256` to its ASCII `string` decimal representation.
               */
              function toString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      uint256 length = Math.log10(value) + 1;
                      string memory buffer = new string(length);
                      uint256 ptr;
                      /// @solidity memory-safe-assembly
                      assembly {
                          ptr := add(buffer, add(32, length))
                      }
                      while (true) {
                          ptr--;
                          /// @solidity memory-safe-assembly
                          assembly {
                              mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                          }
                          value /= 10;
                          if (value == 0) break;
                      }
                      return buffer;
                  }
              }
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
               */
              function toHexString(uint256 value) internal pure returns (string memory) {
                  unchecked {
                      return toHexString(value, Math.log256(value) + 1);
                  }
              }
              /**
               * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
               */
              function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                  bytes memory buffer = new bytes(2 * length + 2);
                  buffer[0] = "0";
                  buffer[1] = "x";
                  for (uint256 i = 2 * length + 1; i > 1; --i) {
                      buffer[i] = _SYMBOLS[value & 0xf];
                      value >>= 4;
                  }
                  require(value == 0, "Strings: hex length insufficient");
                  return string(buffer);
              }
              /**
               * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
               */
              function toHexString(address addr) internal pure returns (string memory) {
                  return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
          import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
          import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
          import "operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol";
          import "./ICommon.sol";
          struct TokenSettings {
              /// @dev total number of tokens that can be minted
              uint32 maxSupply;
              /// @dev total number of tokens that can be minted per wallet
              uint32 maxPerWallet;
              /// @dev tracks the total amount that have been minted
              uint32 amountMinted;
              /// @dev merkle root associated with claiming the token, otherwise bytes32(0)
              bytes32 merkleRoot;
              /// @dev timestamp of when the token can be minted
              uint32 mintStart;
              /// @dev timestamp of when the token can no longer be minted
              uint32 mintEnd;
              /// @dev price for the phase
              uint256 price;
              /// @dev uuid of the token within the Bueno ecosystem
              string uuid;
              /// @dev optional revenue splitting settings
              PaymentSplitterSettings paymentSplitterSettings;
          }
          struct TokenData {
              TokenSettings settings;
              uint256 index;
          }
          error TokenSettingsLocked();
          error TokenAlreadyExists();
          error InvalidPaymentSplitterSettings();
          error TooManyTokens();
          error InvalidToken();
          error MintNotActive();
          error InvalidMintDates();
          /// @author Bueno.art
          /// @title ERC-1155 "Drops" contract (v3)
          contract Bueno1155Drop is
              ERC1155SupplyUpgradeable,
              OwnableUpgradeable,
              ERC2981Upgradeable,
              OperatorFiltererUpgradeable
          {
              string public name;
              string public symbol;
              uint256 private _currentTokenId;
              bool private allowBurning;
              /// @dev maps the token ID (eg 1, 2 ...n) to the token's minting settings
              mapping(uint256 => TokenSettings) private _tokens;
              /// @dev track how many mints a particular wallet has made for a given token
              mapping(uint256 => mapping(address => uint64))
                  private _mintBalanceByTokenId;
              /// @dev track how much revenue each payee has earned
              mapping(address => uint256) private _revenueByAddress;
              /// @dev track how much revenue has been released to each address
              mapping(address => uint256) private _released;
              /// @dev track how much revenue has been released in total
              uint256 private _totalReleased;
              /// @dev "fallback" payment splitter settings in case token-level settings aren't specified
              PaymentSplitterSettings private _fallbackPaymentSplitterSettings;
              event RoyaltyUpdated(address royaltyAddress, uint96 royaltyAmount);
              event TokenRoyaltyUpdated(
                  uint256 tokenId,
                  address royaltyAddress,
                  uint96 royaltyAmount
              );
              event TokenCreated(string indexed uuid, uint256 indexed tokenId);
              event BurnStatusChanged(bool burnActive);
              event TokensAirdropped(uint256 numRecipients, uint256 numTokens);
              event TokenBurned(address indexed owner, uint256 tokenId, uint256 amount);
              event PaymentReleased(address to, uint256 amount);
              event TokenSettingsUpdated(uint256 tokenId);
              event RevenueSettingsUpdated(uint256 tokenId);
              event FallbackRevenueSettingsUpdated();
              event TokensMinted(address indexed to, uint256 tokenId, uint256 quantity);
              event TokenSupplyCapped(uint256 tokenId, uint256 maxSupply);
              /// @custom:oz-upgrades-unsafe-allow constructor
              constructor() {
                  _disableInitializers();
              }
              function initialize(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  TokenSettings[] calldata _tokenSettings,
                  RoyaltySettings calldata _royaltySettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  bool _allowBurning,
                  address _deployer,
                  address _operatorFilter
              ) public initializer {
                  __ERC1155_init(_baseUri);
                  __Ownable_init();
                  uint256 numTokens = _tokenSettings.length;
                  // set a reasonable maximum here so we don't run out of gas
                  if (numTokens > 100) {
                      revert TooManyTokens();
                  }
                  // verify fallback (contract-level) payment splitter settings
                  _verifyPaymentSplitterSettings(_paymentSplitterSettings);
                  for (uint256 i = 0; i < numTokens; ) {
                      // verify token-level payment splitter settings, if present
                      if (_tokenSettings[i].paymentSplitterSettings.payees.length > 0) {
                          _verifyPaymentSplitterSettings(
                              _tokenSettings[i].paymentSplitterSettings
                          );
                      }
                      _verifyMintingTime(
                          _tokenSettings[i].mintStart,
                          _tokenSettings[i].mintEnd
                      );
                      _tokens[i] = _tokenSettings[i];
                      // this value should always be 0 for new tokens
                      _tokens[i].amountMinted = 0;
                      emit TokenCreated(_tokenSettings[i].uuid, i);
                      // numTokens has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  _currentTokenId = numTokens;
                  _fallbackPaymentSplitterSettings = _paymentSplitterSettings;
                  name = _name;
                  symbol = _symbol;
                  allowBurning = _allowBurning;
                  _setDefaultRoyalty(
                      _royaltySettings.royaltyAddress,
                      _royaltySettings.royaltyAmount
                  );
                  _transferOwnership(_deployer);
                  OperatorFiltererUpgradeable.__OperatorFilterer_init(
                      _operatorFilter,
                      _operatorFilter == address(0) ? false : true // only subscribe if a filter is provided
                  );
              }
              /*//////////////////////////////////////////////////////////////
                                     CREATOR FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Create a new token to be minted with the provided settings.
               */
              function createDropToken(
                  TokenSettings calldata settings
              ) external onlyOwner {
                  if (settings.paymentSplitterSettings.payees.length > 0) {
                      _verifyPaymentSplitterSettings(settings.paymentSplitterSettings);
                  }
                  _verifyMintingTime(settings.mintStart, settings.mintEnd);
                  uint256 id = _currentTokenId;
                  _tokens[id] = settings;
                  // this value should always be 0 for new tokens
                  _tokens[id].amountMinted = 0;
                  ++_currentTokenId;
                  emit TokenCreated(settings.uuid, id);
              }
              /**
               * @notice Create multiple tokens to be minted with the provided settings.
               */
              function createDropTokens(
                  TokenSettings[] calldata tokenSettings
              ) external onlyOwner {
                  uint256 numTokens = tokenSettings.length;
                  uint256 currentTokenId = _currentTokenId;
                  for (uint256 i = 0; i < numTokens; ) {
                      if (tokenSettings[i].paymentSplitterSettings.payees.length > 0) {
                          _verifyPaymentSplitterSettings(
                              tokenSettings[i].paymentSplitterSettings
                          );
                      }
                      TokenSettings memory settings = tokenSettings[i];
                      _verifyMintingTime(settings.mintStart, settings.mintEnd);
                      uint256 id = currentTokenId;
                      // this value should always be 0 for new tokens
                      settings.amountMinted = 0;
                      _tokens[id] = settings;
                      ++currentTokenId;
                      // numTokens has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                      emit TokenCreated(settings.uuid, id);
                  }
                  _currentTokenId = currentTokenId;
              }
              /**
               * @notice Update the settings for a token. Certain settings cannot be changed once a token has been minted.
               */
              function updateTokenSettingsByIndex(
                  uint256 id,
                  TokenSettings calldata settings
              ) external onlyOwner {
                  // cannot edit a token larger than the current token ID
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  TokenSettings memory token = _tokens[id];
                  uint32 existingAmountMinted = token.amountMinted;
                  PaymentSplitterSettings memory existingPaymentSplitterSettings = token
                      .paymentSplitterSettings;
                  // Once a token has been minted, it's not possible to change the supply & start/end times
                  if (
                      existingAmountMinted > 0 &&
                      (settings.maxSupply != token.maxSupply ||
                          settings.mintStart != token.mintStart ||
                          settings.mintEnd != token.mintEnd)
                  ) {
                      revert TokenSettingsLocked();
                  }
                  _verifyMintingTime(settings.mintStart, settings.mintEnd);
                  _tokens[id] = settings;
                  // it's not possible to update how many have been claimed, but it's part of the TokenSettings struct
                  // ignore any value that is passed in and use the existing value
                  _tokens[id].amountMinted = existingAmountMinted;
                  // payment splitter settings can only be updated via `updatePaymentSplitterSettingsByIndex`
                  _tokens[id].paymentSplitterSettings = existingPaymentSplitterSettings;
                  emit TokenSettingsUpdated(id);
              }
              function updatePaymentSplitterSettingsByIndex(
                  uint256 id,
                  PaymentSplitterSettings calldata settings
              ) external onlyOwner {
                  // cannot edit a token larger than the current token ID
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  // revenue split cannot be changed once a token is minted
                  if (_tokens[id].amountMinted > 0) {
                      revert TokenSettingsLocked();
                  }
                  _verifyPaymentSplitterSettings(settings);
                  _tokens[id].paymentSplitterSettings = settings;
                  emit RevenueSettingsUpdated(id);
              }
              function updateFallbackPaymentSplitterSettings(
                  PaymentSplitterSettings calldata settings
              ) external onlyOwner {
                  _verifyPaymentSplitterSettings(settings);
                  _fallbackPaymentSplitterSettings = settings;
                  emit FallbackRevenueSettingsUpdated();
              }
              function _verifyMintingTime(uint32 mintStart, uint32 mintEnd) private view {
                  if (mintEnd > 0) {
                      // mint end must be after mint start
                      if (mintEnd < mintStart) {
                          revert InvalidMintDates();
                      }
                      // mint end must be in the future
                      if (mintEnd < block.timestamp) {
                          revert InvalidMintDates();
                      }
                  }
              }
              function _verifyPaymentSplitterSettings(
                  PaymentSplitterSettings calldata settings
              ) private pure {
                  uint256 shareTotal;
                  uint256 numPayees = settings.payees.length;
                  // we discourage using the payment splitter for more than 4 payees, as it's not gas efficient for minting
                  // more advanced use-cases should consider a multi-sig payee
                  if (numPayees != settings.shares.length || numPayees > 4) {
                      revert InvalidPaymentSplitterSettings();
                  }
                  for (uint256 i = 0; i < numPayees; ) {
                      uint256 shares = settings.shares[i];
                      if (shares == 0) {
                          revert InvalidPaymentSplitterSettings();
                      }
                      shareTotal += shares;
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
                  if (shareTotal != 100) {
                      revert InvalidPaymentSplitterSettings();
                  }
              }
              /**
               * @notice Perform a batch airdrop of tokens to a list of recipients
               */
              function airdropToken(
                  uint256 id,
                  uint32[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  TokenSettings storage token = _tokens[id];
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint32 updatedAmountMinted = token.amountMinted + quantities[i];
                      if (token.maxSupply > 0 && updatedAmountMinted > token.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted
                      token.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], id, quantities[i], "");
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Release funds for a particular payee
               */
              function release(address payee) public {
                  uint256 amount = releasable(payee);
                  if (amount > 0) {
                      _totalReleased += amount;
                      // If "_totalReleased += amount" does not overflow, then "_released[payee] += amount" cannot overflow.
                      unchecked {
                          _released[payee] += amount;
                      }
                      AddressUpgradeable.sendValue(payable(payee), amount);
                      emit PaymentReleased(payee, amount);
                  }
              }
              /**
               * @notice Release funds for specified payees
               * @dev This is a convenience method to calling release() for each payee
               */
              function releaseBatch(address[] calldata payees) external {
                  uint256 numPayees = payees.length;
                  for (uint256 i = 0; i < numPayees; ) {
                      release(payees[i]);
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Update the default royalty settings (EIP-2981) for the contract.
               */
              function setRoyaltyInfo(
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setDefaultRoyalty(receiver, feeBasisPoints);
                  emit RoyaltyUpdated(receiver, feeBasisPoints);
              }
              /**
               * @notice Update the royalty settings (EIP-2981) for the token.
               */
              function setTokenRoyaltyInfo(
                  uint256 tokenId,
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setTokenRoyalty(tokenId, receiver, feeBasisPoints);
                  emit TokenRoyaltyUpdated(tokenId, receiver, feeBasisPoints);
              }
              /**
               * @notice If enabled, the token can be burned, for approved operators.
               * @dev The burn method will revert unless this is enabled
               */
              function toggleBurning() external onlyOwner {
                  allowBurning = !allowBurning;
                  emit BurnStatusChanged(allowBurning);
              }
              /**
               * @dev See {ERC1155Upgradeable-_setURI}
               */
              function setUri(string calldata uri) external onlyOwner {
                  _setURI(uri);
              }
              /**
               * @notice This function can only be called for tokens with supply. Calling this function will set the max supply
               * of a token to the current amount minted. This cannot be reversed.
               */
              function capSupplyAtIndex(uint256 id) external onlyOwner {
                  TokenSettings storage token = _tokens[id];
                  // only limited edition tokens can be capped
                  if (token.maxSupply == 0) {
                      revert InvalidToken();
                  }
                  token.maxSupply = token.amountMinted;
                  emit TokenSupplyCapped(id, token.maxSupply);
              }
              /*//////////////////////////////////////////////////////////////
                                     MINTING FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Mint a token to the sender
               */
              function mintToken(uint256 id, uint32 quantity) external payable {
                  TokenSettings memory token = _tokens[id];
                  if (token.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  _mintAfterChecks(
                      msg.sender,
                      msg.value,
                      id,
                      quantity,
                      token.maxPerWallet
                  );
              }
              /**
               * @notice Mint a token to a specific address
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintTokenTo(
                  address account,
                  uint256 id,
                  uint32 quantity
              ) external payable {
                  TokenSettings memory token = _tokens[id];
                  if (token.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  _mintAfterChecks(account, msg.value, id, quantity, token.maxPerWallet);
              }
              /**
               * @notice Mint a token that has an allowlist associated with it.
               * @dev maxQuantity is encoded as part of the proof, and is a way to associate variable quantities with each allowlisted wallet
               */
              function mintTokenAllowlist(
                  uint256 id,
                  uint32 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof
              ) external payable {
                  bytes32 merkleRoot = _tokens[id].merkleRoot;
                  if (merkleRoot == bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (
                      !MerkleProof.verify(
                          proof,
                          merkleRoot,
                          keccak256(abi.encodePacked(msg.sender, maxQuantity))
                      )
                  ) {
                      revert InvalidProof();
                  }
                  _mintAfterChecks(msg.sender, msg.value, id, quantity, maxQuantity);
              }
              function _mintAfterChecks(
                  address account,
                  uint256 balance,
                  uint256 id,
                  uint32 quantity,
                  uint32 maxQuantity
              ) private {
                  if (id >= _currentTokenId) {
                      revert InvalidToken();
                  }
                  TokenSettings storage token = _tokens[id];
                  if (balance != token.price * quantity) {
                      revert InvalidPrice();
                  }
                  if (
                      token.maxSupply > 0 &&
                      token.amountMinted + quantity > token.maxSupply
                  ) {
                      revert SoldOut();
                  }
                  if (
                      maxQuantity > 0 &&
                      // maxQuantity is either the token-level maxPerWallet, or the maxQuantity passed in from the allowlist mint function
                      // if the latter, the value is provided by the user, but is first checked against the merkle tree
                      _mintBalanceByTokenId[id][account] + quantity > maxQuantity
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  if (token.mintStart > 0 && block.timestamp < token.mintStart) {
                      revert MintNotActive();
                  }
                  if (token.mintEnd > 0 && block.timestamp > token.mintEnd) {
                      revert MintNotActive();
                  }
                  // we only need to proceed if this is a revenue generating mint
                  if (balance > 0) {
                      uint256 numPayees = token.paymentSplitterSettings.payees.length;
                      if (numPayees > 0) {
                          // if we have token-level payment splitter settings, use those
                          calculateRevenueSplit(balance, token.paymentSplitterSettings);
                      } else {
                          // otherwise, fallback to the contract-level payment splitter settings
                          calculateRevenueSplit(
                              balance,
                              _fallbackPaymentSplitterSettings
                          );
                      }
                  }
                  token.amountMinted += quantity;
                  _mintBalanceByTokenId[id][account] += quantity;
                  _mint(account, id, quantity, "");
                  emit TokensMinted(account, id, quantity);
              }
              function calculateRevenueSplit(
                  uint256 value,
                  PaymentSplitterSettings storage paymentSplitterSettings
              ) private {
                  uint256 numPayees = paymentSplitterSettings.payees.length;
                  // each token can have different payment splitter settings, and price can change while mint is occurring
                  // therefore we need to do some revenue accounting at the time of mint based on the price paid
                  for (uint256 i = 0; i < numPayees; ) {
                      address payee = paymentSplitterSettings.payees[i];
                      uint256 amount = ((value * paymentSplitterSettings.shares[i]) /
                          100);
                      _revenueByAddress[payee] += amount;
                      // this can't overflow as numPayees is capped at 4
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Burn a token, if the contract allows for it
               */
              function burn(uint256 id, uint256 amount) external {
                  if (!allowBurning) {
                      revert BurningNotAllowed();
                  }
                  _burn(msg.sender, id, amount);
                  emit TokenBurned(msg.sender, id, amount);
              }
              /*//////////////////////////////////////////////////////////////
                                       VIEW FUNCTIONS
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Get the token data based on it's ID (1, 2, etc)
               */
              function getTokenSettingsByTokenId(
                  uint256 id
              ) external view returns (TokenSettings memory) {
                  return _tokens[id];
              }
              /**
               * @notice Retrieve the fallback payment splitter config (used if a token doesn't have it's own payment splitter settings)
               */
              function getFallbackPaymentSplitterSettings()
                  external
                  view
                  returns (PaymentSplitterSettings memory)
              {
                  return _fallbackPaymentSplitterSettings;
              }
              /**
               * @notice Get the token data for all tokens associated with the contract
               */
              function getAllTokenData() external view returns (TokenData[] memory) {
                  uint256 numTokens = _currentTokenId;
                  TokenData[] memory tokens = new TokenData[](numTokens);
                  for (uint256 i = 0; i < numTokens; i++) {
                      tokens[i].settings = _tokens[i];
                      tokens[i].index = i;
                  }
                  return tokens;
              }
              /**
               * @dev Getter for the amount of Ether already released to a payee.
               */
              function released(address account) public view returns (uint256) {
                  return _released[account];
              }
              /**
               * @dev Getter for the total amount of Ether already released.
               */
              function totalReleased() external view returns (uint256) {
                  return _totalReleased;
              }
              /**
               * @dev Getter for the amount of payee's releasable Ether.
               */
              function releasable(address account) public view returns (uint256) {
                  return _revenueByAddress[account] - released(account);
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(
                  bytes4 interfaceId
              )
                  public
                  view
                  virtual
                  override(ERC1155Upgradeable, ERC2981Upgradeable)
                  returns (bool)
              {
                  return super.supportsInterface(interfaceId);
              }
              /*//////////////////////////////////////////////////////////////
                                OPERATOR REGISTRY OVERRIDES
              //////////////////////////////////////////////////////////////*/
              function setApprovalForAll(
                  address operator,
                  bool approved
              ) public override onlyAllowedOperatorApproval(operator) {
                  super.setApprovalForAll(operator, approved);
              }
              /**
               * @dev See {IERC1155-safeTransferFrom}.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  uint256 amount,
                  bytes memory data
              ) public override onlyAllowedOperator(from) {
                  super.safeTransferFrom(from, to, tokenId, amount, data);
              }
              /**
               * @dev See {IERC1155-safeBatchTransferFrom}.
               */
              function safeBatchTransferFrom(
                  address from,
                  address to,
                  uint256[] memory ids,
                  uint256[] memory amounts,
                  bytes memory data
              ) public virtual override onlyAllowedOperator(from) {
                  super.safeBatchTransferFrom(from, to, ids, amounts, data);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "erc721a-upgradeable/contracts/extensions/ERC721AQueryableUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
          import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
          import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
          import "@openzeppelin/contracts-upgradeable/finance/PaymentSplitterUpgradeable.sol";
          import "operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol";
          import "./ICommon.sol";
          struct PhaseSettings {
              /// @dev phase supply. This can be released to public by ending the phase.
              uint64 maxSupply;
              /// @dev tracks the total amount minted in the phase
              uint64 amountMinted;
              /// @dev wallet maximum for the phase
              uint64 maxPerWallet;
              /// @dev merkle root for the phase (if applicable, otherwise bytes32(0))
              bytes32 merkleRoot;
              /// @dev whether the phase is active
              bool isActive;
              /// @dev price for the phase (or free if 0)
              uint256 price;
          }
          struct BaseSettings {
              /// @dev public sale supply. ending a phase will carry supply into this value
              uint64 maxSupply;
              /// @dev global wallet maximum across all phases (including public)
              uint64 maxPerWallet;
              /// @dev tracks the total amount minted in the public sale
              uint64 amountMinted;
              /// @dev price for the public sale (or free if 0)
              uint256 price;
          }
          struct SaleState {
              uint64 numPhases;
              mapping(uint256 => PhaseSettings) phases;
          }
          error PhaseNotActive();
          error InvalidPhase();
          error PhaseNotEnded();
          /// @author Bueno.art
          /// @title ERC-721 Multi-Phase Drop Contract (v3)
          contract Bueno721Drop is
              ERC721AQueryableUpgradeable,
              OwnableUpgradeable,
              ERC2981Upgradeable,
              PaymentSplitterUpgradeable,
              OperatorFiltererUpgradeable
          {
              string public _baseTokenURI;
              SaleState public saleState;
              BaseSettings public baseSettings;
              uint256 public maxSupply;
              address[] public withdrawAddresses;
              mapping(address => mapping(uint256 => uint64)) private amountMintedForPhase;
              bool public isPublicActive;
              bool private allowBurning;
              event TokensMinted(address indexed to, uint256 quantity);
              event TokenBurned(address indexed owner, uint256 tokenId);
              event TokensAirdropped(uint256 numRecipients, uint256 numTokens);
              event PhasesActivated(uint256[] phaseIds, bool activatedPublic);
              event PhasesPaused(uint256[] phaseIds, bool pausedPublic);
              event PhaseEnded(uint256 phaseIds);
              event BurnStatusChanged(bool burnActive);
              event PhaseSettingsUpdated(uint256 phaseId, PhaseSettings settings);
              event BaseSettingsUpdated(BaseSettings settings);
              event BaseURIUpdated(string baseURI);
              event RoyaltyUpdated(address royaltyAddress, uint96 royaltyAmount);
              /// @custom:oz-upgrades-unsafe-allow constructor
              constructor() {
                  _disableInitializers();
              }
              function initialize(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  RoyaltySettings calldata _royaltySettings,
                  PhaseSettings[] calldata _phases,
                  BaseSettings calldata _baseSettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  uint256 _maxIntendedSupply,
                  bool _allowBurning,
                  address _deployer,
                  address _operatorFilter
              ) public initializerERC721A initializer {
                  __ERC721A_init(_name, _symbol);
                  __Ownable_init();
                  __PaymentSplitter_init(
                      _paymentSplitterSettings.payees,
                      _paymentSplitterSettings.shares
                  );
                  uint64 numPhases = uint64(_phases.length);
                  uint256 supplyValidationCount = _baseSettings.maxSupply;
                  for (uint256 i = 0; i < numPhases; ) {
                      saleState.phases[i] = _phases[i];
                      supplyValidationCount += _phases[i].maxSupply;
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  require(
                      supplyValidationCount == _maxIntendedSupply,
                      "Supply of all phases must equal maxIntendedSupply"
                  );
                  _baseTokenURI = _baseUri;
                  withdrawAddresses = _paymentSplitterSettings.payees;
                  saleState.numPhases = numPhases;
                  baseSettings = _baseSettings;
                  allowBurning = _allowBurning;
                  maxSupply = _maxIntendedSupply;
                  _setDefaultRoyalty(
                      _royaltySettings.royaltyAddress,
                      _royaltySettings.royaltyAmount
                  );
                  transferOwnership(_deployer);
                  OperatorFiltererUpgradeable.__OperatorFilterer_init(
                      _operatorFilter,
                      _operatorFilter == address(0) ? false : true // only subscribe if a filter is provided
                  );
              }
              // ========= EXTERNAL MINTING METHODS =========
              /**
               * @notice Mint tokens for an allowlisted phase
               * @dev Calling this function for a phase that doesn't have an allowlist will fail
               */
              function mintPhaseAllowlist(
                  uint256 phaseIndex,
                  uint64 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof
              ) external payable {
                  uint64 updatedAmountMinted = _checkAllowlistPhaseMintConditions(
                      quantity,
                      maxQuantity,
                      proof,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(msg.sender, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint tokens for a non-allowlist phase.
               * @dev Calling this function for a phase that has an allowlist will fail
               */
              function mintPhase(uint256 phaseIndex, uint64 quantity) external payable {
                  uint64 updatedAmountMinted = _checkPhaseMintConditions(
                      msg.sender,
                      quantity,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(msg.sender, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint a token to a specific address from a non-allowlisted sale phase
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintPhaseTo(
                  address account,
                  uint256 phaseIndex,
                  uint64 quantity
              ) external payable {
                  uint64 updatedAmountMinted = _checkPhaseMintConditions(
                      account,
                      quantity,
                      phaseIndex,
                      msg.value
                  );
                  _mintPhaseTo(account, quantity, phaseIndex, updatedAmountMinted);
              }
              /**
               * @notice Mint tokens for a given phase
               * @param account The address to mint the tokens to
               * @param quantity The number of tokens to mint
               * @param phaseIndex The index of the phase to mint from
               * @param updatedAmountMinted The updated amount minted for the phase
               */
              function _mintPhaseTo(
                  address account,
                  uint64 quantity,
                  uint256 phaseIndex,
                  uint64 updatedAmountMinted
              ) internal {
                  _checkGlobalPerWalletMax(account, quantity);
                  saleState.phases[phaseIndex].amountMinted += quantity;
                  amountMintedForPhase[account][phaseIndex] = updatedAmountMinted;
                  _mint(account, quantity);
                  emit TokensMinted(account, quantity);
              }
              /**
               * @notice Mint tokens in the public sale
               */
              function mintPublic(uint64 quantity) external payable {
                  _mintPublicTo(msg.sender, quantity);
              }
              /**
               * @notice Mint a token to a specific address from the public sale
               * @dev Useful in case the recipient of the tokens is not the sender (gifting, fiat checkout, etc)
               */
              function mintPublicTo(address account, uint64 quantity) external payable {
                  _mintPublicTo(account, quantity);
              }
              /**
               * @notice Mint tokens in the public sale
               * @param account The address to mint the tokens to
               * @param quantity The number of tokens to mint
               */
              function _mintPublicTo(address account, uint64 quantity) internal {
                  uint64 updatedAmountMinted = _checkPublicMintConditions(
                      quantity,
                      msg.value
                  );
                  _checkGlobalPerWalletMax(account, quantity);
                  baseSettings.amountMinted = updatedAmountMinted;
                  _mint(account, quantity);
                  emit TokensMinted(account, quantity);
              }
              /**
               * @notice Mint tokens in all possible phases (including public sale)
               */
              function mintBatch(
                  uint64[] calldata quantities,
                  uint32[] calldata maxQuantities,
                  bytes32[][] calldata proofs,
                  uint256[] calldata phaseIndices,
                  uint64 publicQuantity
              ) external payable {
                  uint256 phaseLength = phaseIndices.length;
                  if (
                      phaseLength > saleState.numPhases ||
                      phaseLength != quantities.length ||
                      phaseLength != proofs.length ||
                      phaseLength != maxQuantities.length
                  ) {
                      revert InvalidPhase();
                  }
                  uint256 balance = msg.value;
                  uint256 quantityToMint;
                  for (uint256 i = 0; i < phaseLength; ) {
                      uint64 updatedAmount;
                      uint256 phaseIndex = phaseIndices[i];
                      uint64 quantity = quantities[i];
                      uint32 maxQuantity = maxQuantities[i];
                      bytes32[] calldata proof = proofs[i];
                      PhaseSettings storage phase = saleState.phases[phaseIndex];
                      uint256 priceForPhase = phase.price * quantity;
                      // Since price is strictly checked in the _check* functions below,
                      // we have an additional check here to ensure that the balance doesn't underflow
                      if (balance < priceForPhase) {
                          revert InvalidPrice();
                      }
                      // if the phase has no allowlist, the merkleRoot will be zeroed out.
                      if (phase.merkleRoot == bytes32(0)) {
                          updatedAmount = _checkPhaseMintConditions(
                              msg.sender,
                              quantity,
                              phaseIndex,
                              priceForPhase
                          );
                      } else {
                          updatedAmount = _checkAllowlistPhaseMintConditions(
                              quantity,
                              maxQuantity,
                              proof,
                              phaseIndex,
                              priceForPhase
                          );
                      }
                      // quantity & phaseLength have a maximum value of 2^64 - 1
                      // balance underflow is checked above
                      unchecked {
                          saleState.phases[phaseIndex].amountMinted += quantity;
                          amountMintedForPhase[msg.sender][phaseIndex] = updatedAmount;
                          balance -= priceForPhase;
                          quantityToMint += quantity;
                          ++i;
                      }
                  }
                  uint256 totalMintQuantity = quantityToMint;
                  if (publicQuantity > 0) {
                      _checkPublicMintConditions(publicQuantity, balance);
                      // publicQuantity has a max value of 2^64 - 1
                      unchecked {
                          baseSettings.amountMinted += publicQuantity;
                          totalMintQuantity += publicQuantity;
                      }
                  } else {
                      // remaining balance is checked in _checkPublicMintConditions, so we only need to check
                      // in the case where publicQuantity is 0
                      if (balance != 0) {
                          revert InvalidPrice();
                      }
                  }
                  _checkGlobalPerWalletMax(msg.sender, totalMintQuantity);
                  _mint(msg.sender, totalMintQuantity);
                  emit TokensMinted(msg.sender, totalMintQuantity);
              }
              /**
               * @notice Burn a token, if the contract allows for it
               */
              function burn(uint256 tokenId) external {
                  if (!allowBurning) {
                      revert BurningNotAllowed();
                  }
                  _burn(tokenId, true);
                  emit TokenBurned(msg.sender, tokenId);
              }
              // ========= OWNER METHODS =========
              /**
               * @notice Perform a batch airdrop for a particular phase.
               * @dev Minted tokens are pulled from the phase that is specified in the airdropper.
               */
              function airdropForPhase(
                  uint256 phaseIndex,
                  uint64[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint64 updatedAmountMinted = phase.amountMinted + quantities[i];
                      if (updatedAmountMinted > phase.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted for the phase
                      phase.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], quantities[i]);
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Perform a batch airdrop for the public phase.
               * @dev Minted tokens are pulled from the public phase.
               */
              function airdropPublic(
                  uint64[] calldata quantities,
                  address[] calldata recipients
              ) external onlyOwner {
                  uint256 numRecipients = recipients.length;
                  uint256 totalAirdropped;
                  if (numRecipients != quantities.length) revert InvalidAirdrop();
                  for (uint256 i = 0; i < numRecipients; ) {
                      uint64 updatedAmountMinted = baseSettings.amountMinted +
                          quantities[i];
                      if (updatedAmountMinted > baseSettings.maxSupply) {
                          revert SoldOut();
                      }
                      // airdrops are not subject to the per-wallet mint limits,
                      // but we track how much is minted for the phase
                      baseSettings.amountMinted = updatedAmountMinted;
                      totalAirdropped += quantities[i];
                      _mint(recipients[i], quantities[i]);
                      // numRecipients has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  emit TokensAirdropped(numRecipients, totalAirdropped);
              }
              /**
               * @notice Specify which phases are active.
               * Public sale can be activated by setting `activatePublic` to true.
               */
              function activatePhases(
                  uint256[] calldata phaseIndices,
                  bool activatePublic
              ) external onlyOwner {
                  uint256 numPhases = phaseIndices.length;
                  // activate all the phases provided in phaseIndices
                  for (uint256 i = 0; i < numPhases; ) {
                      uint256 phaseIndex = phaseIndices[i];
                      if (phaseIndex >= saleState.numPhases) {
                          // phaseIndex is out of bounds
                          revert InvalidPhase();
                      }
                      saleState.phases[phaseIndices[i]].isActive = true;
                      // numPhases has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // calling this function with activatePublic=false only indicates the public sale
                  // is not intended to be activated, but it does not pause it.
                  if (activatePublic) {
                      isPublicActive = true;
                  }
                  emit PhasesActivated(phaseIndices, activatePublic);
              }
              /**
               * @notice Specify which phases are inactive (paused).
               * Public sale can be paused by setting `pausePublic` to true.
               * Pausing is separate from ending, since ending permanently closes the phase.
               */
              function pausePhases(
                  uint256[] calldata phaseIndices,
                  bool pausePublic
              ) external onlyOwner {
                  uint256 numPhases = phaseIndices.length;
                  for (uint256 i = 0; i < numPhases; ) {
                      uint256 phaseIndex = phaseIndices[i];
                      if (phaseIndex >= saleState.numPhases) {
                          // phaseIndex is out of bounds
                          revert InvalidPhase();
                      }
                      saleState.phases[phaseIndex].isActive = false;
                      // numPhases has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // calling this function with pausePublic=false only indicates the public sale
                  // is not intended to be paused, but it does not pause it.
                  if (pausePublic) {
                      isPublicActive = false;
                  }
                  emit PhasesPaused(phaseIndices, pausePublic);
              }
              /**
               * @notice If enabled, the token can be burned, for approved operators.
               * @dev The burn method will revert unless this is enabled
               */
              function toggleBurning() external onlyOwner {
                  allowBurning = !allowBurning;
                  emit BurnStatusChanged(allowBurning);
              }
              /**
               * @notice Permanently closes a phase by capping the supply & releasing it
               */
              function endPhase(uint256 phaseIndex) public onlyOwner {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  // if the phase never had supply, there is nothing to do
                  if (phase.maxSupply == 0) {
                      revert InvalidPhase();
                  }
                  // transfer the remaining supply into the base settings (used for public sale accounting)
                  baseSettings.maxSupply += phase.maxSupply - phase.amountMinted;
                  // remove the supply from the phase
                  phase.maxSupply = 0;
                  emit PhaseEnded(phaseIndex);
              }
              function endPhases(uint64[] calldata phaseIndices) external onlyOwner {
                  uint256 phaseIndicesLength = phaseIndices.length;
                  // ensure that phaseIndices argument will only ever be as large as the number of phases
                  if (phaseIndicesLength > saleState.numPhases) {
                      revert InvalidPhase();
                  }
                  for (uint256 i = 0; i < phaseIndicesLength; ) {
                      endPhase(phaseIndices[i]);
                      // phaseIndicesLength has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Updates the minting rules for a particular phase
               * @dev supply, amountMinted are not changeable. isActive is handled by activatePhases
               */
              function updatePhaseSettings(
                  uint256 phaseIndex,
                  PhaseSettings calldata phase
              ) external onlyOwner {
                  uint64 existingAmountMinted = saleState.phases[phaseIndex].amountMinted;
                  uint64 existingMaxSupply = saleState.phases[phaseIndex].maxSupply;
                  bool existingStatus = saleState.phases[phaseIndex].isActive;
                  saleState.phases[phaseIndex] = phase;
                  // ensure that the amountMinted, maxSupply, and status values cannot be set
                  saleState.phases[phaseIndex].amountMinted = existingAmountMinted;
                  saleState.phases[phaseIndex].maxSupply = existingMaxSupply;
                  saleState.phases[phaseIndex].isActive = existingStatus;
                  emit PhaseSettingsUpdated(phaseIndex, phase);
              }
              /**
               * @notice Updates the the base minting settings
               * The global maxPerWallet setting applies to all phases
               * Pricing and other fields will apply to the public sale
               *
               * @dev maxSupply & amountMinted are not changeable. isActive is handled by activatePhases
               */
              function updateBaseSettings(
                  BaseSettings calldata _baseSettings
              ) external onlyOwner {
                  uint64 existingMaxSupply = baseSettings.maxSupply;
                  uint64 existingAmountMinted = baseSettings.amountMinted;
                  baseSettings = _baseSettings;
                  // ensure that the maxSupply & amountMinted value cannot be set
                  baseSettings.maxSupply = existingMaxSupply;
                  baseSettings.amountMinted = existingAmountMinted;
                  emit BaseSettingsUpdated(_baseSettings);
              }
              /**
               * @dev Payment can be pulled via PaymentSplitter.release
               * this method is provided for convenience to release all payee funds
               */
              function withdraw() external onlyOwner {
                  uint256 numAddresses = withdrawAddresses.length;
                  for (uint256 i = 0; i < numAddresses; ) {
                      address payable withdrawAddress = payable(withdrawAddresses[i]);
                      if (releasable(withdrawAddress) > 0) {
                          release(withdrawAddress);
                      }
                      // numAddresses has a maximum value of 2^256 - 1
                      unchecked {
                          ++i;
                      }
                  }
              }
              function setBaseURI(string memory baseURI) external onlyOwner {
                  _baseTokenURI = baseURI;
                  emit BaseURIUpdated(baseURI);
              }
              function setRoyaltyInfo(
                  address receiver,
                  uint96 feeBasisPoints
              ) external onlyOwner {
                  _setDefaultRoyalty(receiver, feeBasisPoints);
                  emit RoyaltyUpdated(receiver, feeBasisPoints);
              }
              /**
               * @notice This function cannot be called if any phase has not ended. This will cap the max supply
               * to the amount of tokens that have been minted so far. This cannot be reversed.
               */
              function capMaxSupply() external onlyOwner {
                  uint64 numPhases = saleState.numPhases;
                  for (uint64 i = 0; i < numPhases; ) {
                      if (saleState.phases[i].maxSupply > 0) {
                          // we only want to cap the supply if all phases are ended, meaning their supply
                          // has been transferred to the public/base phase
                          revert PhaseNotEnded();
                      }
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  // whatever the amount of tokens that has been minted is the new overall "max supply"
                  maxSupply = _totalMinted();
                  // the max supply of the public/base phase is however many have been minted
                  baseSettings.maxSupply = baseSettings.amountMinted;
              }
              // ========= VIEW METHODS =========
              function _startTokenId() internal view virtual override returns (uint256) {
                  return 1;
              }
              function _checkAllowlistPhaseMintConditions(
                  uint64 quantity,
                  uint32 maxQuantity,
                  bytes32[] calldata proof,
                  uint256 phaseIndex,
                  uint256 balance
              ) internal view returns (uint64) {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  if (!phase.isActive) {
                      revert PhaseNotActive();
                  }
                  // there should be a valid merkle root for the phase
                  if (phase.merkleRoot == bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (phase.amountMinted + quantity > phase.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * phase.price) {
                      revert InvalidPrice();
                  }
                  if (
                      !MerkleProof.verify(
                          proof,
                          phase.merkleRoot,
                          keccak256(abi.encodePacked(msg.sender, maxQuantity))
                      )
                  ) {
                      revert InvalidProof();
                  }
                  uint256 amountMinted = amountMintedForPhase[msg.sender][phaseIndex];
                  uint256 updatedAmountMinted = amountMinted + quantity;
                  if (
                      // phases can have a maxPerWallet
                      (maxQuantity == 0 &&
                          phase.maxPerWallet > 0 &&
                          updatedAmountMinted > phase.maxPerWallet) ||
                      // wallets can have a maxPerWallet encoded in the merkle tree
                      (maxQuantity > 0 && updatedAmountMinted > maxQuantity)
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkPhaseMintConditions(
                  address wallet,
                  uint256 quantity,
                  uint256 phaseIndex,
                  uint256 balance
              ) internal view returns (uint64) {
                  PhaseSettings storage phase = saleState.phases[phaseIndex];
                  if (!phase.isActive) {
                      revert PhaseNotActive();
                  }
                  // the phase should not have a merkleRoot
                  if (phase.merkleRoot != bytes32(0)) {
                      revert InvalidMintFunction();
                  }
                  if (phase.amountMinted + quantity > phase.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * phase.price) {
                      revert InvalidPrice();
                  }
                  uint256 amountMinted = amountMintedForPhase[wallet][phaseIndex];
                  uint256 updatedAmountMinted = amountMinted + quantity;
                  // phases can have a maxPerWallet
                  if (
                      phase.maxPerWallet > 0 && updatedAmountMinted > phase.maxPerWallet
                  ) {
                      revert ExceedMaxPerWallet();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkPublicMintConditions(
                  uint256 quantity,
                  uint256 balance
              ) internal view returns (uint64) {
                  if (!isPublicActive) {
                      revert PhaseNotActive();
                  }
                  uint256 updatedAmountMinted = baseSettings.amountMinted + quantity;
                  if (updatedAmountMinted > baseSettings.maxSupply) {
                      revert SoldOut();
                  }
                  if (balance != quantity * baseSettings.price) {
                      revert InvalidPrice();
                  }
                  return uint64(updatedAmountMinted);
              }
              function _checkGlobalPerWalletMax(
                  address wallet,
                  uint256 quantity
              ) internal view {
                  if (
                      baseSettings.maxPerWallet > 0 &&
                      _numberMinted(wallet) + quantity > baseSettings.maxPerWallet
                  ) {
                      revert ExceedMaxPerWallet();
                  }
              }
              function getDataForPhase(
                  uint256 phaseIndex
              ) external view returns (PhaseSettings memory) {
                  return saleState.phases[phaseIndex];
              }
              function getMintBalance(address wallet) external view returns (uint256) {
                  return _numberMinted(wallet);
              }
              function getAmountMintedForPhase(
                  uint256 phaseIndex,
                  address wallet
              ) external view returns (uint64) {
                  return amountMintedForPhase[wallet][phaseIndex];
              }
              function getAmountMintedForOwner(
                  address wallet
              ) external view returns (uint256[] memory) {
                  uint256[] memory amountMintedPerPhase = new uint256[](
                      saleState.numPhases + 1
                  );
                  for (uint64 i = 0; i < saleState.numPhases; ) {
                      amountMintedPerPhase[i] = amountMintedForPhase[wallet][i];
                      // numPhases has a maximum value of 2^64 - 1
                      unchecked {
                          ++i;
                      }
                  }
                  amountMintedPerPhase[saleState.numPhases] = _numberMinted(wallet);
                  return amountMintedPerPhase;
              }
              /**
               * @dev See {ERC721-_baseURI}.
               */
              function _baseURI() internal view virtual override returns (string memory) {
                  return _baseTokenURI;
              }
              function supportsInterface(
                  bytes4 interfaceId
              )
                  public
                  view
                  virtual
                  override(IERC721AUpgradeable, ERC721AUpgradeable, ERC2981Upgradeable)
                  returns (bool)
              {
                  return
                      ERC721AUpgradeable.supportsInterface(interfaceId) ||
                      ERC2981Upgradeable.supportsInterface(interfaceId);
              }
              // ========= OPERATOR FILTERER OVERRIDES =========
              function setApprovalForAll(
                  address operator,
                  bool approved
              )
                  public
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperatorApproval(operator)
              {
                  super.setApprovalForAll(operator, approved);
              }
              function approve(
                  address operator,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperatorApproval(operator)
              {
                  super.approve(operator, tokenId);
              }
              function transferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.transferFrom(from, to, tokenId);
              }
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.safeTransferFrom(from, to, tokenId);
              }
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory data
              )
                  public
                  payable
                  override(ERC721AUpgradeable, IERC721AUpgradeable)
                  onlyAllowedOperator(from)
              {
                  super.safeTransferFrom(from, to, tokenId, data);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          import "@openzeppelin/contracts/proxy/Clones.sol";
          import "@openzeppelin/contracts/access/AccessControl.sol";
          import "./Bueno721Drop.sol";
          import "./Bueno1155Drop.sol";
          contract BuenoFactory is AccessControl {
              address private DEFAULT_OPERATOR_FILTER =
                  address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
              address private drop721Implementation;
              address private drop1155Implementation;
              event ContractCreated(address creator, address contractAddress);
              constructor() {
                  _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
              }
              function updateDefaultOperatorFilter(
                  address newFilter
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  DEFAULT_OPERATOR_FILTER = newFilter;
              }
              function update721Implementation(
                  address newImplementation
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  drop721Implementation = newImplementation;
              }
              function update1155Implementation(
                  address newImplementation
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  drop1155Implementation = newImplementation;
              }
              function getOperatorFilter() external view returns (address) {
                  return DEFAULT_OPERATOR_FILTER;
              }
              function get721ImplementationAddress() external view returns (address) {
                  return drop721Implementation;
              }
              function get1155ImplementationAddress() external view returns (address) {
                  return drop1155Implementation;
              }
              function deploy721Drop(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  RoyaltySettings calldata _royaltySettings,
                  PhaseSettings[] calldata _phases,
                  BaseSettings calldata _baseSettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  uint256 _maxIntendedSupply,
                  bool _registerOperatorFilter,
                  bool _allowBurning
              ) external {
                  require(drop721Implementation != address(0), "Implementation not set");
                  address payable clone = payable(Clones.clone(drop721Implementation));
                  address operatorFilter = _registerOperatorFilter
                      ? DEFAULT_OPERATOR_FILTER
                      : address(0);
                  Bueno721Drop(clone).initialize(
                      _name,
                      _symbol,
                      _baseUri,
                      _royaltySettings,
                      _phases,
                      _baseSettings,
                      _paymentSplitterSettings,
                      _maxIntendedSupply,
                      _allowBurning,
                      msg.sender,
                      operatorFilter
                  );
                  emit ContractCreated(msg.sender, clone);
              }
              function deploy1155Drop(
                  string memory _name,
                  string memory _symbol,
                  string memory _baseUri,
                  TokenSettings[] calldata _tokenSettings,
                  RoyaltySettings calldata _royaltySettings,
                  PaymentSplitterSettings calldata _paymentSplitterSettings,
                  bool _registerOperatorFilter,
                  bool _allowBurning
              ) external {
                  require(drop1155Implementation != address(0), "Implementation not set");
                  address clone = Clones.clone(drop1155Implementation);
                  address operatorFilter = _registerOperatorFilter
                      ? DEFAULT_OPERATOR_FILTER
                      : address(0);
                  Bueno1155Drop(clone).initialize(
                      _name,
                      _symbol,
                      _baseUri,
                      _tokenSettings,
                      _royaltySettings,
                      _paymentSplitterSettings,
                      _allowBurning,
                      msg.sender,
                      operatorFilter
                  );
                  emit ContractCreated(msg.sender, clone);
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.17;
          error InvalidPrice();
          error SoldOut();
          error ExceedMaxPerWallet();
          error InvalidProof();
          error InvalidMintFunction();
          error InvalidAirdrop();
          error BurningNotAllowed();
          struct PaymentSplitterSettings {
              address[] payees;
              uint256[] shares;
          }
          struct RoyaltySettings {
              address royaltyAddress;
              uint96 royaltyAmount;
          }
          // 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;
              }
              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.2.3
          // 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()`.
           *
           * 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();
              }
              // =============================================================
              //                   TOKEN COUNTING OPERATIONS
              // =============================================================
              /**
               * @dev Returns the starting token ID.
               * To change the starting token ID, please override this function.
               */
              function _startTokenId() internal view virtual returns (uint256) {
                  return 0;
              }
              /**
               * @dev Returns the next token ID to be minted.
               */
              function _nextTokenId() internal view virtual returns (uint256) {
                  return 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) {
                  // Counter underflow is impossible as _burnCounter cannot be incremented
                  // more than `_currentIndex - _startTokenId()` times.
                  unchecked {
                      return ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total amount of tokens minted in the contract.
               */
              function _totalMinted() internal view virtual returns (uint256) {
                  // Counter underflow is impossible as `_currentIndex` does not decrement,
                  // and it is initialized to `_startTokenId()`.
                  unchecked {
                      return ERC721AStorage.layout()._currentIndex - _startTokenId();
                  }
              }
              /**
               * @dev Returns the total number of tokens burned.
               */
              function _totalBurned() internal view virtual returns (uint256) {
                  return ERC721AStorage.layout()._burnCounter;
              }
              // =============================================================
              //                    ADDRESS DATA OPERATIONS
              // =============================================================
              /**
               * @dev Returns the number of tokens in `owner`'s account.
               */
              function balanceOf(address owner) public view virtual override returns (uint256) {
                  if (owner == address(0)) revert BalanceQueryForZeroAddress();
                  return 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();
                  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 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);
                  }
              }
              /**
               * 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 not burned.
                      if (packed & _BITMASK_BURNED == 0) {
                          // If the data at the starting slot does not exist, start the scan.
                          if (packed == 0) {
                              if (tokenId >= ERC721AStorage.layout()._currentIndex) revert OwnerQueryForNonexistentToken();
                              // 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;
                                  return packed;
                              }
                          }
                          // Otherwise, the data exists and is not burned. 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.
                          return packed;
                      }
                  }
                  revert OwnerQueryForNonexistentToken();
              }
              /**
               * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
               */
              function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
                  ownership.addr = address(uint160(packed));
                  ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
                  ownership.burned = packed & _BITMASK_BURNED != 0;
                  ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
              }
              /**
               * @dev Packs ownership data into a single uint256.
               */
              function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                      result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
                  }
              }
              /**
               * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
               */
              function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
                  // For branchless setting of the `nextInitialized` flag.
                  assembly {
                      // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                      result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
                  }
              }
              // =============================================================
              //                      APPROVAL OPERATIONS
              // =============================================================
              /**
               * @dev Gives permission to `to` to transfer `tokenId` token to another account. 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();
                  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) {
                  return
                      _startTokenId() <= tokenId &&
                      tokenId < ERC721AStorage.layout()._currentIndex && // If within bounds,
                      ERC721AStorage.layout()._packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
              }
              /**
               * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
               */
              function _isSenderApprovedOrOwner(
                  address approvedAddress,
                  address owner,
                  address msgSender
              ) private pure returns (bool result) {
                  assembly {
                      // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      owner := and(owner, _BITMASK_ADDRESS)
                      // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                      msgSender := and(msgSender, _BITMASK_ADDRESS)
                      // `msgSender == owner || msgSender == approvedAddress`.
                      result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
                  }
              }
              /**
               * @dev Returns the storage slot and value for the approved address of `tokenId`.
               */
              function _getApprovedSlotAndAddress(uint256 tokenId)
                  private
                  view
                  returns (uint256 approvedAddressSlot, address approvedAddress)
              {
                  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);
                  if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
                  (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                  // The nested ifs save around 20+ gas over a compound boolean condition.
                  if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                      if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                  if (to == address(0)) revert TransferToZeroAddress();
                  _beforeTokenTransfers(from, to, tokenId, 1);
                  // Clear approvals from the previous owner.
                  assembly {
                      if approvedAddress {
                          // This is equivalent to `delete _tokenApprovals[tokenId]`.
                          sstore(approvedAddressSlot, 0)
                      }
                  }
                  // Underflow of the sender's balance is impossible because we check for
                  // ownership above and the recipient's balance can't realistically overflow.
                  // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
                  unchecked {
                      // We can directly increment and decrement the balances.
                      --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;
                              }
                          }
                      }
                  }
                  emit Transfer(from, to, tokenId);
                  _afterTokenTransfers(from, to, tokenId, 1);
              }
              /**
               * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId
              ) public payable virtual override {
                  safeTransferFrom(from, to, tokenId, '');
              }
              /**
               * @dev Safely transfers `tokenId` token from `from` to `to`.
               *
               * Requirements:
               *
               * - `from` cannot be the zero address.
               * - `to` cannot be the zero address.
               * - `tokenId` token must exist and be owned by `from`.
               * - If the caller is not `from`, it must be approved to move this token
               * by either {approve} or {setApprovalForAll}.
               * - If `to` refers to a smart contract, it must implement
               * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
               *
               * Emits a {Transfer} event.
               */
              function safeTransferFrom(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) public payable virtual override {
                  transferFrom(from, to, tokenId);
                  if (to.code.length != 0)
                      if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                          revert TransferToNonERC721ReceiverImplementer();
                      }
              }
              /**
               * @dev Hook that is called before a set of serially-ordered token IDs
               * are about to be transferred. This includes minting.
               * And also called before burning one token.
               *
               * `startTokenId` - the first token ID to be transferred.
               * `quantity` - the amount to be transferred.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
               * transferred to `to`.
               * - When `from` is zero, `tokenId` will be minted for `to`.
               * - When `to` is zero, `tokenId` will be burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _beforeTokenTransfers(
                  address from,
                  address to,
                  uint256 startTokenId,
                  uint256 quantity
              ) internal virtual {}
              /**
               * @dev Hook that is called after a set of serially-ordered token IDs
               * have been transferred. This includes minting.
               * And also called after one token has been burned.
               *
               * `startTokenId` - the first token ID to be transferred.
               * `quantity` - the amount to be transferred.
               *
               * Calling conditions:
               *
               * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
               * transferred to `to`.
               * - When `from` is zero, `tokenId` has been minted for `to`.
               * - When `to` is zero, `tokenId` has been burned by `from`.
               * - `from` and `to` are never both zero.
               */
              function _afterTokenTransfers(
                  address from,
                  address to,
                  uint256 startTokenId,
                  uint256 quantity
              ) internal virtual {}
              /**
               * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
               *
               * `from` - Previous owner of the given token ID.
               * `to` - Target address that will receive the token.
               * `tokenId` - Token ID to be transferred.
               * `_data` - Optional data to send along with the call.
               *
               * Returns whether the call correctly returned the expected magic value.
               */
              function _checkContractOnERC721Received(
                  address from,
                  address to,
                  uint256 tokenId,
                  bytes memory _data
              ) private returns (bool) {
                  try
                      ERC721A__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();
                      } else {
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              }
              // =============================================================
              //                        MINT OPERATIONS
              // =============================================================
              /**
               * @dev Mints `quantity` tokens and transfers them to `to`.
               *
               * Requirements:
               *
               * - `to` cannot be the zero address.
               * - `quantity` must be greater than 0.
               *
               * Emits a {Transfer} event for each mint.
               */
              function _mint(address to, uint256 quantity) internal virtual {
                  uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
                  if (quantity == 0) revert MintZeroQuantity();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are incredibly unrealistic.
                  // `balance` and `numberMinted` have a maximum limit of 2**64.
                  // `tokenId` has a maximum limit of 2**256.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      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)
                      );
                      uint256 toMasked;
                      uint256 end = startTokenId + quantity;
                      // Use assembly to loop and emit the `Transfer` event for gas savings.
                      // The duplicated `log4` removes an extra check and reduces stack juggling.
                      // The assembly, together with the surrounding Solidity code, have been
                      // delicately arranged to nudge the compiler into producing optimized opcodes.
                      assembly {
                          // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          toMasked := and(to, _BITMASK_ADDRESS)
                          // Emit the `Transfer` event.
                          log4(
                              0, // Start of data (0, since no data).
                              0, // End of data (0, since no data).
                              _TRANSFER_EVENT_SIGNATURE, // Signature.
                              0, // `address(0)`.
                              toMasked, // `to`.
                              startTokenId // `tokenId`.
                          )
                          // The `iszero(eq(,))` check ensures that large values of `quantity`
                          // that overflows uint256 will make the loop run out of gas.
                          // The compiler will optimize the `iszero` away for performance.
                          for {
                              let tokenId := add(startTokenId, 1)
                          } iszero(eq(tokenId, end)) {
                              tokenId := add(tokenId, 1)
                          } {
                              // Emit the `Transfer` event. Similar to above.
                              log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                          }
                      }
                      if (toMasked == 0) revert MintToZeroAddress();
                      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();
                  if (quantity == 0) revert MintZeroQuantity();
                  if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
                  _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                  // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
                  unchecked {
                      // Updates:
                      // - `balance += quantity`.
                      // - `numberMinted += quantity`.
                      //
                      // We can directly add to the `balance` and `numberMinted`.
                      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)
                      );
                      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();
                              }
                          } while (index < end);
                          // Reentrancy protection.
                          if (ERC721AStorage.layout()._currentIndex != end) revert();
                      }
                  }
              }
              /**
               * @dev Equivalent to `_safeMint(to, quantity, '')`.
               */
              function _safeMint(address to, uint256 quantity) internal virtual {
                  _safeMint(to, quantity, '');
              }
              // =============================================================
              //                       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)
                      if (_msgSenderERC721A() != owner)
                          if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                  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();
                  }
                  _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 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();
                  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)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.3
          // 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) {
                  TokenOwnership memory ownership;
                  if (tokenId < _startTokenId() || tokenId >= _nextTokenId()) {
                      return ownership;
                  }
                  ownership = _ownershipAt(tokenId);
                  if (ownership.burned) {
                      return ownership;
                  }
                  return _ownershipOf(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)
              {
                  unchecked {
                      uint256 tokenIdsLength = tokenIds.length;
                      TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength);
                      for (uint256 i; i != tokenIdsLength; ++i) {
                          ownerships[i] = explicitOwnershipOf(tokenIds[i]);
                      }
                      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) {
                  unchecked {
                      if (start >= stop) revert InvalidQueryRange();
                      uint256 tokenIdsIdx;
                      uint256 stopLimit = _nextTokenId();
                      // Set `start = max(start, _startTokenId())`.
                      if (start < _startTokenId()) {
                          start = _startTokenId();
                      }
                      // Set `stop = min(stop, stopLimit)`.
                      if (stop > stopLimit) {
                          stop = stopLimit;
                      }
                      uint256 tokenIdsMaxLength = balanceOf(owner);
                      // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,
                      // to cater for cases where `balanceOf(owner)` is too big.
                      if (start < stop) {
                          uint256 rangeLength = stop - start;
                          if (rangeLength < tokenIdsMaxLength) {
                              tokenIdsMaxLength = rangeLength;
                          }
                      } else {
                          tokenIdsMaxLength = 0;
                      }
                      uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength);
                      if (tokenIdsMaxLength == 0) {
                          return tokenIds;
                      }
                      // 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;
                      }
                      for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
                          ownership = _ownershipAt(i);
                          if (ownership.burned) {
                              continue;
                          }
                          if (ownership.addr != address(0)) {
                              currOwnershipAddr = ownership.addr;
                          }
                          if (currOwnershipAddr == owner) {
                              tokenIds[tokenIdsIdx++] = i;
                          }
                      }
                      // Downsize the array to fit.
                      assembly {
                          mstore(tokenIds, tokenIdsIdx)
                      }
                      return tokenIds;
                  }
              }
              /**
               * @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) {
                  unchecked {
                      uint256 tokenIdsIdx;
                      address currOwnershipAddr;
                      uint256 tokenIdsLength = balanceOf(owner);
                      uint256[] memory tokenIds = new uint256[](tokenIdsLength);
                      TokenOwnership memory ownership;
                      for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
                          ownership = _ownershipAt(i);
                          if (ownership.burned) {
                              continue;
                          }
                          if (ownership.addr != address(0)) {
                              currOwnershipAddr = ownership.addr;
                          }
                          if (currOwnershipAddr == owner) {
                              tokenIds[tokenIdsIdx++] = i;
                          }
                      }
                      return tokenIds;
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // ERC721A Contracts v4.2.3
          // 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.2.3
          // 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();
              // =============================================================
              //                            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.13;
          interface IOperatorFilterRegistry {
              function isOperatorAllowed(address registrant, address operator) external view returns (bool);
              function register(address registrant) external;
              function registerAndSubscribe(address registrant, address subscription) external;
              function registerAndCopyEntries(address registrant, address registrantToCopy) external;
              function unregister(address addr) external;
              function updateOperator(address registrant, address operator, bool filtered) external;
              function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
              function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
              function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
              function subscribe(address registrant, address registrantToSubscribe) external;
              function unsubscribe(address registrant, bool copyExistingEntries) external;
              function subscriptionOf(address addr) external returns (address registrant);
              function subscribers(address registrant) external returns (address[] memory);
              function subscriberAt(address registrant, uint256 index) external returns (address);
              function copyEntriesOf(address registrant, address registrantToCopy) external;
              function isOperatorFiltered(address registrant, address operator) external returns (bool);
              function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
              function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
              function filteredOperators(address addr) external returns (address[] memory);
              function filteredCodeHashes(address addr) external returns (bytes32[] memory);
              function filteredOperatorAt(address registrant, uint256 index) external returns (address);
              function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
              function isRegistered(address addr) external returns (bool);
              function codeHashOf(address addr) external returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.13;
          import {IOperatorFilterRegistry} from "../IOperatorFilterRegistry.sol";
          import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
          abstract contract OperatorFiltererUpgradeable is Initializable {
              error OperatorNotAllowed(address operator);
              IOperatorFilterRegistry constant operatorFilterRegistry =
                  IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
              function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe)
                  internal
                  onlyInitializing
              {
                  // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                  // will not revert, but the contract will need to be registered with the registry once it is deployed in
                  // order for the modifier to filter addresses.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (!operatorFilterRegistry.isRegistered(address(this))) {
                          if (subscribe) {
                              operatorFilterRegistry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                          } else {
                              if (subscriptionOrRegistrantToCopy != address(0)) {
                                  operatorFilterRegistry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                              } else {
                                  operatorFilterRegistry.register(address(this));
                              }
                          }
                      }
                  }
              }
              modifier onlyAllowedOperator(address from) virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      // Allow spending tokens from addresses with balance
                      // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                      // from an EOA.
                      if (from == msg.sender) {
                          _;
                          return;
                      }
                      if (!operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)) {
                          revert OperatorNotAllowed(msg.sender);
                      }
                  }
                  _;
              }
              modifier onlyAllowedOperatorApproval(address operator) virtual {
                  // Check registry code length to facilitate testing in environments without a deployed registry.
                  if (address(operatorFilterRegistry).code.length > 0) {
                      if (!operatorFilterRegistry.isOperatorAllowed(address(this), operator)) {
                          revert OperatorNotAllowed(operator);
                      }
                  }
                  _;
              }
          }
          

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