ETH Price: $2,060.71 (+6.97%)

Transaction Decoder

Block:
16741895 at Mar-02-2023 03:54:35 PM +UTC
Transaction Fee:
0.016913888069607711 ETH $34.85
Gas Used:
172,761 Gas / 97.903392951 Gwei

Emitted Events:

149 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006415a6450da7c7864d09e03b3e0605d77205f151, 0x00000000000000000000000082e63a0fa475e1f11bad26daea4f0872ad89c065, 0x0000000000000000000000000000000000000000000000000000000000002671 )
150 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000082e63a0fa475e1f11bad26daea4f0872ad89c065, 0x0000000000000000000000006415a6450da7c7864d09e03b3e0605d77205f151, 0x0000000000000000000000000000000000000000000000000000000000001c45 )
151 GnosisSafeProxy.0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d( 0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d, 0x00000000000000000000000000000000006c3852cbef3e08e8df289169ede581, 000000000000000000000000000000000000000000000000002386f26fc10000 )
152 Seaport.OrderFulfilled( orderHash=06AAC18416570CD683432F5118E777619C4B32CA187C7B2B9BAF7B993778B870, offerer=0x6415A645...77205F151, zone=[Sender] 0x82e63a0fa475e1f11bad26daea4f0872ad89c065, recipient=[Sender] 0x82e63a0fa475e1f11bad26daea4f0872ad89c065, offer=, consideration= )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...169EdE581
(Seaport 1.1)
(Lido: Execution Layer Rewards Vault)
41.450370893339599403 Eth41.450543654339599403 Eth0.000172761
0x76927267...7ACc544Cc
0x82E63A0F...2ad89C065
0.042593982671179759 Eth
Nonce: 883
0.015680094601572048 Eth
Nonce: 884
0.026913888069607711
0x83Db4412...60c15B894 2.224287110663420597 Eth2.234287110663420597 Eth0.01

Execution Trace

ETH 0.01 Seaport.fulfillOrder( order=[{name:parameters, type:tuple, order:1, indexed:false, value:[{name:offerer, type:address, order:1, indexed:false, value:0x6415A6450Da7c7864D09e03B3E0605D77205F151, valueString:0x6415A6450Da7c7864D09e03B3E0605D77205F151}, {name:zone, type:address, order:2, indexed:false, value:0x82E63A0FA475E1F11bad26dAea4F0872ad89C065, valueString:0x82E63A0FA475E1F11bad26dAea4F0872ad89C065}, {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:2, valueString:2}, {name:startTime, type:uint256, order:6, indexed:false, value:1677756108, valueString:1677756108}, {name:endTime, type:uint256, order:7, indexed:false, value:1678360903, valueString:1678360903}, {name:zoneHash, type:bytes32, order:8, indexed:false, value:3000000000000000000000000000000000000000000000000000000000000000, valueString:3000000000000000000000000000000000000000000000000000000000000000}, {name:salt, type:uint256, order:9, indexed:false, value:11154602997541850916, valueString:11154602997541850916}, {name:conduitKey, type:bytes32, order:10, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:totalOriginalConsiderationItems, type:uint256, order:11, indexed:false, value:2, valueString:2}], valueString:[{name:offerer, type:address, order:1, indexed:false, value:0x6415A6450Da7c7864D09e03B3E0605D77205F151, valueString:0x6415A6450Da7c7864D09e03B3E0605D77205F151}, {name:zone, type:address, order:2, indexed:false, value:0x82E63A0FA475E1F11bad26dAea4F0872ad89C065, valueString:0x82E63A0FA475E1F11bad26dAea4F0872ad89C065}, {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:2, valueString:2}, {name:startTime, type:uint256, order:6, indexed:false, value:1677756108, valueString:1677756108}, {name:endTime, type:uint256, order:7, indexed:false, value:1678360903, valueString:1678360903}, {name:zoneHash, type:bytes32, order:8, indexed:false, value:3000000000000000000000000000000000000000000000000000000000000000, valueString:3000000000000000000000000000000000000000000000000000000000000000}, {name:salt, type:uint256, order:9, indexed:false, value:11154602997541850916, valueString:11154602997541850916}, {name:conduitKey, type:bytes32, order:10, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:totalOriginalConsiderationItems, type:uint256, order:11, indexed:false, value:2, valueString:2}]}, {name:signature, type:bytes, order:2, indexed:false, value:0xC8F56BC793C8CC401AA5E8A34244DC0171101314785DC27C43C5D86FC0016051BD4DBC82F3C28AF8B295CAC966B8186A2F3CAFE4CBEC9ED425E7C2F938FFFA71, valueString:0xC8F56BC793C8CC401AA5E8A34244DC0171101314785DC27C43C5D86FC0016051BD4DBC82F3C28AF8B295CAC966B8186A2F3CAFE4CBEC9ED425E7C2F938FFFA71}], fulfillerConduitKey=0000000000000000000000000000000000000000000000000000000000000000 ) => ( fulfilled=True )
  • Null: 0x000...001.104ffbd0( )
  • TransparentUpgradeableProxy.23b872dd( )
    • CaptainzV2.transferFrom( from=0x6415A6450Da7c7864D09e03B3E0605D77205F151, to=0x82E63A0FA475E1F11bad26dAea4F0872ad89C065, tokenId=9841 )
      • OperatorFilterRegistry.isOperatorAllowed( registrant=0x769272677faB02575E84945F03Eca517ACc544Cc, operator=0x00000000006c3852cbEf3e08E8dF289169EdE581 ) => ( True )
      • OperatorFilterRegistry.isOperatorAllowed( registrant=0x769272677faB02575E84945F03Eca517ACc544Cc, operator=0x6415A6450Da7c7864D09e03B3E0605D77205F151 ) => ( True )
      • TransparentUpgradeableProxy.23b872dd( )
        • CaptainzV2.transferFrom( from=0x82E63A0FA475E1F11bad26dAea4F0872ad89C065, to=0x6415A6450Da7c7864D09e03B3E0605D77205F151, tokenId=7237 )
          • OperatorFilterRegistry.isOperatorAllowed( registrant=0x769272677faB02575E84945F03Eca517ACc544Cc, operator=0x00000000006c3852cbEf3e08E8dF289169EdE581 ) => ( True )
          • OperatorFilterRegistry.isOperatorAllowed( registrant=0x769272677faB02575E84945F03Eca517ACc544Cc, operator=0x82E63A0FA475E1F11bad26dAea4F0872ad89C065 ) => ( True )
          • ETH 0.01 GnosisSafeProxy.CALL( )
            • ETH 0.01 GnosisSafe.DELEGATECALL( )
              fulfillOrder[Consideration (ln:9105)]
              File 1 of 6: Seaport
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { Consideration } from "./lib/Consideration.sol";
              /**
               * @title Seaport
               * @custom:version 1.1
               * @author 0age (0age.eth)
               * @custom:coauthor d1ll0n (d1ll0n.eth)
               * @custom:coauthor transmissions11 (t11s.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 James Wenzel (emo.eth)
               * @custom:contributor Stephan Min (stephanm.eth)
               * @custom:contributor Ryan Ghods (ralxz.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 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 ETH/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 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.7;
              // prettier-ignore
              import {
                  BasicOrderParameters,
                  OrderComponents,
                  Fulfillment,
                  FulfillmentComponent,
                  Execution,
                  Order,
                  AdvancedOrder,
                  OrderStatus,
                  CriteriaResolver
              } from "../lib/ConsiderationStructs.sol";
              /**
               * @title SeaportInterface
               * @author 0age
               * @custom:version 1.1
               * @notice Seaport is a generalized ETH/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 SeaportInterface contains all external function interfaces for Seaport.
               */
              interface SeaportInterface {
                  /**
                   * @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
                   *                            Seaport.
                   *
                   * @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
                   *                            Seaport.
                   * @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.
                   */
                  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.
                   */
                  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 as 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).
                   *
                   * @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.
                   */
                  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.
                   *
                   * @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.
                   *
                   * @return executions An array of elements indicating the sequence of
                   *                    transfers performed as part of matching the given
                   *                    orders.
                   */
                  function matchAdvancedOrders(
                      AdvancedOrder[] calldata orders,
                      CriteriaResolver[] calldata criteriaResolvers,
                      Fulfillment[] calldata fulfillments
                  ) 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 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
                      );
                  /**
                   * @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.7;
              import "./TransferHelperStructs.sol";
              import { TokenTransferrer } from "../lib/TokenTransferrer.sol";
              import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
              // prettier-ignore
              import {
                  ConduitControllerInterface
              } from "../interfaces/ConduitControllerInterface.sol";
              import { Conduit } from "../conduit/Conduit.sol";
              import { ConduitTransfer } from "../conduit/lib/ConduitStructs.sol";
              // prettier-ignore
              import {
                  TransferHelperInterface
              } from "../interfaces/TransferHelperInterface.sol";
              /**
               * @title TransferHelper
               * @author stuckinaboot, stephankmin
               * @notice TransferHelper is a utility contract for transferring
               *         ERC20/ERC721/ERC1155 items in bulk to a specific recipient.
               */
              contract TransferHelper is TransferHelperInterface, TokenTransferrer {
                  // Allow for interaction with the conduit controller.
                  ConduitControllerInterface internal immutable _CONDUIT_CONTROLLER;
                  // Cache the conduit creation hash used by the conduit controller.
                  bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;
                  /**
                   * @dev Set the supplied conduit controller and retrieve its
                   *      conduit creation code hash.
                   *
                   *
                   * @param conduitController A contract that deploys conduits, or proxies
                   *                          that may optionally be used to transfer approved
                   *                          ERC20/721/1155 tokens.
                   */
                  constructor(address conduitController) {
                      // Get the conduit creation code hash from the supplied conduit
                      // controller and set it as an immutable.
                      ConduitControllerInterface controller = ConduitControllerInterface(
                          conduitController
                      );
                      (_CONDUIT_CREATION_CODE_HASH, ) = controller.getConduitCodeHashes();
                      // Set the supplied conduit controller as an immutable.
                      _CONDUIT_CONTROLLER = controller;
                  }
                  /**
                   * @notice Transfer multiple items to a single recipient.
                   *
                   * @param items      The items to transfer.
                   * @param recipient  The address the items should be transferred to.
                   * @param conduitKey The key of the conduit through which the bulk transfer
                   *                   should occur.
                   *
                   * @return magicValue A value indicating that the transfers were successful.
                   */
                  function bulkTransfer(
                      TransferHelperItem[] calldata items,
                      address recipient,
                      bytes32 conduitKey
                  ) external override returns (bytes4 magicValue) {
                      // Retrieve total number of transfers and place on stack.
                      uint256 totalTransfers = items.length;
                      // If no conduitKey is given, use TokenTransferrer to perform transfers.
                      if (conduitKey == bytes32(0)) {
                          // Skip overflow checks: all for loops are indexed starting at zero.
                          unchecked {
                              // Iterate over each transfer.
                              for (uint256 i = 0; i < totalTransfers; ++i) {
                                  // Retrieve the transfer in question.
                                  TransferHelperItem calldata item = items[i];
                                  // Perform a transfer based on the transfer's item type.
                                  // Revert if item being transferred is a native token.
                                  if (item.itemType == ConduitItemType.NATIVE) {
                                      revert InvalidItemType();
                                  } else if (item.itemType == ConduitItemType.ERC20) {
                                      _performERC20Transfer(
                                          item.token,
                                          msg.sender,
                                          recipient,
                                          item.amount
                                      );
                                  } else if (item.itemType == ConduitItemType.ERC721) {
                                      _performERC721Transfer(
                                          item.token,
                                          msg.sender,
                                          recipient,
                                          item.identifier
                                      );
                                  } else {
                                      _performERC1155Transfer(
                                          item.token,
                                          msg.sender,
                                          recipient,
                                          item.identifier,
                                          item.amount
                                      );
                                  }
                              }
                          }
                      }
                      // Otherwise, a conduitKey was provided.
                      else {
                          // Derive the conduit address from the deployer, conduit key
                          // and creation code hash.
                          address conduit = address(
                              uint160(
                                  uint256(
                                      keccak256(
                                          abi.encodePacked(
                                              bytes1(0xff),
                                              address(_CONDUIT_CONTROLLER),
                                              conduitKey,
                                              _CONDUIT_CREATION_CODE_HASH
                                          )
                                      )
                                  )
                              )
                          );
                          // Declare a new array to populate with each token transfer.
                          ConduitTransfer[] memory conduitTransfers = new ConduitTransfer[](
                              totalTransfers
                          );
                          // Skip overflow checks: all for loops are indexed starting at zero.
                          unchecked {
                              // Iterate over each transfer.
                              for (uint256 i = 0; i < totalTransfers; ++i) {
                                  // Retrieve the transfer in question.
                                  TransferHelperItem calldata item = items[i];
                                  // Create a ConduitTransfer corresponding to each
                                  // TransferHelperItem.
                                  conduitTransfers[i] = ConduitTransfer(
                                      item.itemType,
                                      item.token,
                                      msg.sender,
                                      recipient,
                                      item.identifier,
                                      item.amount
                                  );
                              }
                          }
                          // Call the conduit and execute bulk transfers.
                          ConduitInterface(conduit).execute(conduitTransfers);
                      }
                      // Return a magic value indicating that the transfers were performed.
                      magicValue = this.bulkTransfer.selector;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              import { ConduitItemType } from "../conduit/lib/ConduitEnums.sol";
              struct TransferHelperItem {
                  ConduitItemType itemType;
                  address token;
                  uint256 identifier;
                  uint256 amount;
              }
              // 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;
              // 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;
              /**
               * @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.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;
              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;
              import { TransferHelperItem } from "../helpers/TransferHelperStructs.sol";
              interface TransferHelperInterface {
                  /**
                   * @dev Revert with an error when attempting to execute transfers with a
                   *      NATIVE itemType.
                   */
                  error InvalidItemType();
                  /**
                   * @notice Transfer multiple items to a single recipient.
                   *
                   * @param items The items to transfer.
                   * @param recipient  The address the items should be transferred to.
                   * @param conduitKey  The key of the conduit performing the bulk transfer.
                   */
                  function bulkTransfer(
                      TransferHelperItem[] calldata items,
                      address recipient,
                      bytes32 conduitKey
                  ) external returns (bytes4);
              }
              // 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;
              /*
               * -------------------------- 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();
              }
              // 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.13;
              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 "./ConsiderationConstants.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) {
                              revert UnusedItemParameters();
                          }
                          // transfer the native tokens to the recipient.
                          _transferEth(item.recipient, item.amount);
                      } else if (item.itemType == ItemType.ERC20) {
                          // Ensure that no identifier is supplied.
                          if (item.identifier != 0) {
                              revert UnusedItemParameters();
                          }
                          // 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 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 itself.
                   *
                   * @param itemType   The type of item to transfer, either ERC721 or ERC1155.
                   * @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.
                   * @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,
                      address token,
                      address from,
                      address to,
                      uint256 identifier,
                      uint256 amount,
                      bytes32 conduitKey
                  ) internal {
                      // 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 to memory.
                              mstore(add(callDataOffset, Conduit_execute_transferTo_ptr), to)
                              // 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) {
                                  revert InvalidERC721TransferAmount();
                              }
                              // Perform transfer via the token contract directly.
                              _performERC721Transfer(token, from, to, identifier);
                          } else {
                              // Perform transfer via the token contract directly.
                              _performERC1155Transfer(token, from, to, identifier, amount);
                          }
                      }
                  }
                  /**
                   * @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 _transferEth(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 ETH 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.
                          revert EtherTransferGenericFailure(to, amount);
                      }
                  }
                  /**
                   * @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 (must be 1 for ERC721).
                   * @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 _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) {
                              revert InvalidERC721TransferAmount();
                          }
                          // 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.
                          revert InvalidCallToConduit(conduit);
                      }
                      // Ensure result was extracted and matches EIP-1271 magic value.
                      if (result != ConduitInterface.execute.selector) {
                          revert InvalidConduit(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.7;
              // prettier-ignore
              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
              }
              // prettier-ignore
              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
              }
              // prettier-ignore
              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
              }
              // prettier-ignore
              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
              }
              // prettier-ignore
              enum Side {
                  // 0: Items that can be spent
                  OFFER,
                  // 1: Items that must be received
                  CONSIDERATION
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              // prettier-ignore
              import {
                  OrderType,
                  BasicOrderType,
                  ItemType,
                  Side
              } from "./ConsiderationEnums.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 included in a staticcall to
               *      `isValidOrderIncludingExtraData` on the zone for the order if the order
               *      type is restricted and the offerer or zone are not the caller.
               */
              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;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { OrderStatus } from "./ConsiderationStructs.sol";
              import { Assertions } from "./Assertions.sol";
              import { SignatureVerification } from "./SignatureVerification.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) {
                      // Revert if order's timespan hasn't started yet or has already ended.
                      if (startTime > block.timestamp || endTime <= block.timestamp) {
                          // Only revert if revertOnInvalid has been supplied as true.
                          if (revertOnInvalid) {
                              revert InvalidTime();
                          }
                          // Return false as the order is invalid.
                          return false;
                      }
                      // Return true as the order time is valid.
                      valid = true;
                  }
                  /**
                   * @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 32 or 33 bytes or if the recovered signer does not match the
                   *      supplied offerer. Note that in cases where a 32 or 33 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 {
                      // Skip signature verification if the offerer is the caller.
                      if (offerer == msg.sender) {
                          return;
                      }
                      // Derive EIP-712 digest using the domain separator and the order hash.
                      bytes32 digest = _deriveEIP712Digest(_domainSeparator(), orderHash);
                      // Ensure that the signature for the digest is valid for the offerer.
                      _assertValidSignature(offerer, digest, signature);
                  }
                  /**
                   * @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) {
                              revert OrderIsCancelled(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.
                              revert OrderPartiallyFilled(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) {
                                  revert OrderAlreadyFilled(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.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.
               */
              // 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 = 77;
              uint256 constant NameWithLength = 0x0d436F6E73696465726174696F6E;
              uint256 constant Version = 0x312e31;
              uint256 constant Version_length = 3;
              uint256 constant Version_shift = 0xe8;
              uint256 constant _NOT_ENTERED = 1;
              uint256 constant _ENTERED = 2;
              // Common Offsets
              // Offsets for identically positioned fields shared by:
              // OfferItem, ConsiderationItem, SpentItem, ReceivedItem
              uint256 constant Common_token_offset = 0x20;
              uint256 constant Common_identifier_offset = 0x40;
              uint256 constant Common_amount_offset = 0x60;
              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_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 InvalidFulfillmentComponentData_error_signature = (
                  0x7fda727900000000000000000000000000000000000000000000000000000000
              );
              uint256 constant InvalidFulfillmentComponentData_error_len = 0x04;
              uint256 constant Panic_error_signature = (
                  0x4e487b7100000000000000000000000000000000000000000000000000000000
              );
              uint256 constant Panic_error_offset = 0x04;
              uint256 constant Panic_error_length = 0x24;
              uint256 constant Panic_arithmetic = 0x11;
              uint256 constant MissingItemAmount_error_signature = (
                  0x91b3e51400000000000000000000000000000000000000000000000000000000
              );
              uint256 constant MissingItemAmount_error_len = 0x04;
              uint256 constant OrderParameters_offer_head_offset = 0x40;
              uint256 constant OrderParameters_consideration_head_offset = 0x60;
              uint256 constant OrderParameters_conduit_offset = 0x120;
              uint256 constant OrderParameters_counter_offset = 0x140;
              uint256 constant Fulfillment_itemIndex_offset = 0x20;
              uint256 constant AdvancedOrder_numerator_offset = 0x20;
              uint256 constant AlmostOneWord = 0x1f;
              uint256 constant OneWord = 0x20;
              uint256 constant TwoWords = 0x40;
              uint256 constant ThreeWords = 0x60;
              uint256 constant FourWords = 0x80;
              uint256 constant FiveWords = 0xa0;
              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 EIP712_Order_size = 0x180;
              uint256 constant EIP712_OfferItem_size = 0xc0;
              uint256 constant EIP712_ConsiderationItem_size = 0xe0;
              uint256 constant AdditionalRecipients_size = 0x40;
              uint256 constant EIP712_DomainSeparator_offset = 0x02;
              uint256 constant EIP712_OrderHash_offset = 0x22;
              uint256 constant EIP712_DigestPayload_size = 0x42;
              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;
              // 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 = DefaultFreeMemoryPointer;
              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;
              // 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_signatureHead_negativeOffset = 0x20;
              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;
              // 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
              uint256 constant EIP_712_PREFIX = (
                  0x1901000000000000000000000000000000000000000000000000000000000000
              );
              uint256 constant ExtraGasBuffer = 0x20;
              uint256 constant CostPerWord = 3;
              uint256 constant MemoryExpansionCoefficient = 0x200; // 512
              uint256 constant Create2AddressDerivation_ptr = 0x0b;
              uint256 constant Create2AddressDerivation_length = 0x55;
              uint256 constant MaskOverByteTwelve = (
                  0x0000000000000000000000ff0000000000000000000000000000000000000000
              );
              uint256 constant MaskOverLastTwentyBytes = (
                  0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff
              );
              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;
              // Declare constant for errors related to amount derivation.
              // error InexactFraction() @ AmountDerivationErrors.sol
              uint256 constant InexactFraction_error_signature = (
                  0xc63cf08900000000000000000000000000000000000000000000000000000000
              );
              uint256 constant InexactFraction_error_len = 0x04;
              // Declare constant for errors related to signature verification.
              uint256 constant Ecrecover_precompile = 1;
              uint256 constant Ecrecover_args_size = 0x80;
              uint256 constant Signature_lower_v = 27;
              // error BadSignatureV(uint8) @ SignatureVerificationErrors.sol
              uint256 constant BadSignatureV_error_signature = (
                  0x1f003d0a00000000000000000000000000000000000000000000000000000000
              );
              uint256 constant BadSignatureV_error_offset = 0x04;
              uint256 constant BadSignatureV_error_length = 0x24;
              // error InvalidSigner() @ SignatureVerificationErrors.sol
              uint256 constant InvalidSigner_error_signature = (
                  0x815e1d6400000000000000000000000000000000000000000000000000000000
              );
              uint256 constant InvalidSigner_error_length = 0x04;
              // error InvalidSignature() @ SignatureVerificationErrors.sol
              uint256 constant InvalidSignature_error_signature = (
                  0x8baa579f00000000000000000000000000000000000000000000000000000000
              );
              uint256 constant InvalidSignature_error_length = 0x04;
              // error BadContractSignature() @ SignatureVerificationErrors.sol
              uint256 constant BadContractSignature_error_signature = (
                  0x4f7fb80d00000000000000000000000000000000000000000000000000000000
              );
              uint256 constant BadContractSignature_error_length = 0x04;
              uint256 constant NumBitsAfterSelector = 0xe0;
              // 69 is the lowest modulus for which the remainder
              // of every selector other than the two match functions
              // is greater than those of the match functions.
              uint256 constant NonMatchSelector_MagicModulus = 69;
              // Of the two match function selectors, the highest
              // remainder modulo 69 is 29.
              uint256 constant NonMatchSelector_MagicRemainder = 0x1d;
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { OrderParameters } from "./ConsiderationStructs.sol";
              import { GettersAndDerivers } from "./GettersAndDerivers.sol";
              // prettier-ignore
              import {
                  TokenTransferrerErrors
              } from "../interfaces/TokenTransferrerErrors.sol";
              import { CounterManager } from "./CounterManager.sol";
              import "./ConsiderationConstants.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) {
                          revert MissingOriginalConsiderationItems();
                      }
                  }
                  /**
                   * @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 {
                      // Revert if the supplied amount is equal to zero.
                      if (amount == 0) {
                          revert MissingItemAmount();
                      }
                  }
                  /**
                   * @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)
                           */
                          validOffsets := and(
                              // Order parameters at calldata 0x04 must have offset of 0x20.
                              eq(
                                  calldataload(BasicOrder_parameters_cdPtr),
                                  BasicOrder_parameters_ptr
                              ),
                              // Additional recipients at cd 0x224 must have offset of 0x240.
                              eq(
                                  calldataload(BasicOrder_additionalRecipients_head_cdPtr),
                                  BasicOrder_additionalRecipients_head_ptr
                              )
                          )
                          validOffsets := and(
                              validOffsets,
                              eq(
                                  // Load signature offset from calldata 0x244.
                                  calldataload(BasicOrder_signature_cdPtr),
                                  // Derive expected offset as start of recipients + len * 64.
                                  add(
                                      BasicOrder_signature_ptr,
                                      mul(
                                          // Additional recipients length at calldata 0x264.
                                          calldataload(
                                              BasicOrder_additionalRecipients_length_cdPtr
                                          ),
                                          // Each additional recipient has a length of 0x40.
                                          AdditionalRecipients_size
                                      )
                                  )
                              )
                          )
                          validOffsets := and(
                              validOffsets,
                              lt(
                                  // BasicOrderType parameter at calldata offset 0x124.
                                  calldataload(BasicOrder_basicOrderType_cdPtr),
                                  // Value should be less than 24.
                                  BasicOrder_basicOrderType_range
                              )
                          )
                      }
                      // Revert with an error if basic order parameter offsets are invalid.
                      if (!validOffsets) {
                          revert InvalidBasicOrderParameterEncoding();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { EIP1271Interface } from "../interfaces/EIP1271Interface.sol";
              // prettier-ignore
              import {
                  SignatureVerificationErrors
              } from "../interfaces/SignatureVerificationErrors.sol";
              import { LowLevelHelpers } from "./LowLevelHelpers.sol";
              import "./ConsiderationConstants.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 the signature against.
                   * @param signature A signature from the signer indicating that the order
                   *                  has been approved.
                   */
                  function _assertValidSignature(
                      address signer,
                      bytes32 digest,
                      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)
                          // Declare value for v signature parameter.
                          let v
                          // 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.
                                  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) {
                              // 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)
                              // Get pointer to use for `digest` input to `isValidSignature`.
                              let digestPtr := sub(
                                  signature,
                                  EIP1271_isValidSignature_digest_negativeOffset
                              )
                              // Cache the value currently stored at the digest pointer.
                              let cachedWordOverwrittenByDigest := mload(digestPtr)
                              // Write the selector first, since it overlaps the digest.
                              mstore(selectorPtr, EIP1271_isValidSignature_selector)
                              // Next, write the digest.
                              mstore(digestPtr, digest)
                              // Call signer with `isValidSignature` to validate signature.
                              success := staticcall(
                                  gas(),
                                  signer,
                                  selectorPtr,
                                  add(
                                      signatureLength,
                                      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.
                                          mstore(0, BadContractSignature_error_signature)
                                          revert(0, BadContractSignature_error_length)
                                      }
                                      // Check if signature length was invalid.
                                      if gt(sub(ECDSA_MaxLength, signatureLength), 1) {
                                          // Revert with generic invalid signature error.
                                          mstore(0, InvalidSignature_error_signature)
                                          revert(0, InvalidSignature_error_length)
                                      }
                                      // Check if v was invalid.
                                      if iszero(
                                          byte(v, ECDSA_twentySeventhAndTwentyEighthBytesSet)
                                      ) {
                                          // Revert with invalid v value.
                                          mstore(0, BadSignatureV_error_signature)
                                          mstore(BadSignatureV_error_offset, v)
                                          revert(0, BadSignatureV_error_length)
                                      }
                                      // Revert with generic invalid signer error message.
                                      mstore(0, InvalidSigner_error_signature)
                                      revert(0, InvalidSigner_error_length)
                                  }
                              }
                              // Restore the cached values overwritten by selector, digest and
                              // signature head.
                              mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
                              mstore(selectorPtr, cachedWordOverwrittenBySelector)
                              mstore(digestPtr, 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 {
                              mstore(0, BadContractSignature_error_signature)
                              revert(0, BadContractSignature_error_length)
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { OrderParameters } from "./ConsiderationStructs.sol";
              import { ConsiderationBase } from "./ConsiderationBase.sol";
              import "./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.
                          // prettier-ignore
                          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),
                              mul(offerLength, OneWord)
                          )
                      }
                      // 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).
                          // prettier-ignore
                          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),
                              mul(originalConsiderationLength, OneWord)
                          )
                      }
                      // 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) {
                      // prettier-ignore
                      return block.chainid == _CHAIN_ID
                          ? _DOMAIN_SEPARATOR
                          : _deriveDomainSeparator();
                  }
                  /**
                   * @dev Internal view function to 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()
                      internal
                      view
                      returns (
                          string memory version,
                          bytes32 domainSeparator,
                          address conduitController
                      )
                  {
                      // Derive the domain separator.
                      domainSeparator = _domainSeparator();
                      // Declare variable as immutables cannot be accessed within assembly.
                      conduitController = address(_CONDUIT_CONTROLLER);
                      // Allocate a string with the intended length.
                      version = new string(Version_length);
                      // Set the version as data on the newly allocated string.
                      assembly {
                          mstore(add(version, OneWord), shl(Version_shift, Version))
                      }
                  }
                  /**
                   * @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;
              // prettier-ignore
              import {
                  ConsiderationEventsAndErrors
              } from "../interfaces/ConsiderationEventsAndErrors.sol";
              import { ReentrancyGuard } from "./ReentrancyGuard.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 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() internal returns (uint256 newCounter) {
                      // Ensure that the reentrancy guard is not currently set.
                      _assertNonReentrant();
                      // Skip overflow check as counter cannot be incremented that far.
                      unchecked {
                          // Increment current counter for the supplied offerer.
                          newCounter = ++_counters[msg.sender];
                      }
                      // 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;
              // prettier-ignore
              import {
                  ConduitControllerInterface
              } from "../interfaces/ConduitControllerInterface.sol";
              // prettier-ignore
              import {
                  ConsiderationEventsAndErrors
              } from "../interfaces/ConsiderationEventsAndErrors.sol";
              import "./ConsiderationConstants.sol";
              /**
               * @title ConsiderationBase
               * @author 0age
               * @notice ConsiderationBase contains immutable constants and constructor logic.
               */
              contract ConsiderationBase is 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 The derived domain separator.
                   */
                  function _deriveDomainSeparator() internal view returns (bytes32) {
                      // prettier-ignore
                      return keccak256(
                          abi.encode(
                              _EIP_712_DOMAIN_TYPEHASH,
                              _NAME_HASH,
                              _VERSION_HASH,
                              block.chainid,
                              address(this)
                          )
                      );
                  }
                  /**
                   * @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.1"));
                      // Construct the OfferItem type string.
                      // prettier-ignore
                      bytes memory offerItemTypeString = abi.encodePacked(
                          "OfferItem(",
                              "uint8 itemType,",
                              "address token,",
                              "uint256 identifierOrCriteria,",
                              "uint256 startAmount,",
                              "uint256 endAmount",
                          ")"
                      );
                      // Construct the ConsiderationItem type string.
                      // prettier-ignore
                      bytes memory considerationItemTypeString = abi.encodePacked(
                          "ConsiderationItem(",
                              "uint8 itemType,",
                              "address token,",
                              "uint256 identifierOrCriteria,",
                              "uint256 startAmount,",
                              "uint256 endAmount,",
                              "address recipient",
                          ")"
                      );
                      // Construct the OrderComponents type string, not including the above.
                      // prettier-ignore
                      bytes memory orderComponentsPartialTypeString = abi.encodePacked(
                          "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.
                      // prettier-ignore
                      eip712DomainTypehash = keccak256(
                          abi.encodePacked(
                              "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);
                      // Derive OrderItem type hash via combination of relevant type strings.
                      orderTypehash = keccak256(
                          abi.encodePacked(
                              orderComponentsPartialTypeString,
                              considerationItemTypeString,
                              offerItemTypeString
                          )
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              import { SpentItem, ReceivedItem } 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 offerer   The offerer of the validated order.
                   * @param zone      The zone of the validated order.
                   */
                  event OrderValidated(
                      bytes32 orderHash,
                      address indexed offerer,
                      address indexed zone
                  );
                  /**
                   * @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.
                   */
                  error InvalidTime();
                  /**
                   * @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 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 ether is supplied as part of
                   *      msg.value when fulfilling orders.
                   */
                  error InsufficientEtherSupplied();
                  /**
                   * @dev Revert with an error when an ether transfer reverts.
                   */
                  error EtherTransferGenericFailure(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.
                   */
                  error InvalidCanceller();
                  /**
                   * @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 ETH outside of matching orders.
                   */
                  error InvalidNativeOfferItem();
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { ReentrancyErrors } from "../interfaces/ReentrancyErrors.sol";
              import "./ConsiderationConstants.sol";
              /**
               * @title ReentrancyGuard
               * @author 0age
               * @notice ReentrancyGuard contains a storage variable and related functionality
               *         for protecting against reentrancy.
               */
              contract ReentrancyGuard is ReentrancyErrors {
                  // 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 the sentinel value for the
                   *      reentrancy guard is not currently set and, if not, to set the
                   *      sentinel value for the reentrancy guard.
                   */
                  function _setReentrancyGuard() internal {
                      // Ensure that the reentrancy guard is not already set.
                      _assertNonReentrant();
                      // Set the reentrancy guard.
                      _reentrancyGuard = _ENTERED;
                  }
                  /**
                   * @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 the 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) {
                          revert NoReentrantCalls();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              /**
               * @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.7;
              interface EIP1271Interface {
                  function isValidSignature(bytes32 digest, bytes calldata signature)
                      external
                      view
                      returns (bytes4);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              /**
               * @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;
              import "./ConsiderationConstants.sol";
              /**
               * @title LowLevelHelpers
               * @author 0age
               * @notice LowLevelHelpers contains logic for performing various low-level
               *         operations.
               */
              contract LowLevelHelpers {
                  /**
                   * @dev Internal view function to staticcall an arbitrary target with given
                   *      calldata. Note that no data is written to memory and no contract
                   *      size check is performed.
                   *
                   * @param target   The account to staticcall.
                   * @param callData The calldata to supply when staticcalling the target.
                   *
                   * @return success The status of the staticcall to the target.
                   */
                  function _staticcall(address target, bytes memory callData)
                      internal
                      view
                      returns (bool success)
                  {
                      assembly {
                          // Perform the staticcall.
                          success := staticcall(
                              gas(),
                              target,
                              add(callData, OneWord),
                              mload(callData),
                              0,
                              0
                          )
                      }
                  }
                  /**
                   * @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 := 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(mload(FreeMemoryPointerSlot), 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())
                              }
                          }
                      }
                  }
                  /**
                   * @dev Internal pure function to determine if the first word of returndata
                   *      matches an expected magic value.
                   *
                   * @param expected The expected magic value.
                   *
                   * @return A boolean indicating whether the expected value matches the one
                   *         located in the first word of returndata.
                   */
                  function _doesNotMatchMagic(bytes4 expected) internal pure returns (bool) {
                      // Declare a variable for the value held by the return data buffer.
                      bytes4 result;
                      // Utilize assembly in order to read directly from returndata buffer.
                      assembly {
                          // Only put result on stack if return data is exactly one word.
                          if eq(returndatasize(), OneWord) {
                              // Copy the word directly from return data into scratch space.
                              returndatacopy(0, 0, OneWord)
                              // Take value from scratch space and place it on the stack.
                              result := mload(0)
                          }
                      }
                      // Return a boolean indicating whether expected and located value match.
                      return result != expected;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { ItemType, Side } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  OfferItem,
                  ConsiderationItem,
                  ReceivedItem,
                  OrderParameters,
                  AdvancedOrder,
                  Execution,
                  FulfillmentComponent
              } from "./ConsiderationStructs.sol";
              import "./ConsiderationConstants.sol";
              // prettier-ignore
              import {
                  FulfillmentApplicationErrors
              } from "../interfaces/FulfillmentApplicationErrors.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.
                   *
                   * @return execution The transfer performed as a result of the fulfillment.
                   */
                  function _applyFulfillment(
                      AdvancedOrder[] memory advancedOrders,
                      FulfillmentComponent[] calldata offerComponents,
                      FulfillmentComponent[] calldata considerationComponents
                  ) internal pure returns (Execution memory execution) {
                      // Ensure 1+ of both offer and consideration components are supplied.
                      if (
                          offerComponents.length == 0 || considerationComponents.length == 0
                      ) {
                          revert OfferAndConsiderationRequiredOnFulfillment();
                      }
                      // 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;
                      // 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 and consideration share types, tokens and identifiers.
                      if (
                          execution.item.itemType != considerationItem.itemType ||
                          execution.item.token != considerationItem.token ||
                          execution.item.identifier != considerationItem.identifier
                      ) {
                          revert MismatchedFulfillmentOfferAndConsiderationComponents();
                      }
                      // 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);
                          }
                          // Reduce total consideration amount to equal the offer amount.
                          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) {
                              revert MissingFulfillmentComponentOnAggregation(side);
                          }
                          // If the fulfillment components are offer components...
                          if (side == Side.OFFER) {
                              // Set the supplied recipient on the execution item.
                              execution.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 if execution
                          // amount is zero. This will cause the execution item to be skipped.
                          if (execution.item.amount == 0) {
                              execution.offerer = address(0);
                              execution.item.recipient = payable(0);
                          }
                      }
                  }
                  /**
                   * @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 function for reverts on invalid fulfillment data.
                          function throwInvalidFulfillmentComponentData() {
                              // Store the InvalidFulfillmentComponentData error signature.
                              mstore(0, InvalidFulfillmentComponentData_error_signature)
                              // Return, supplying InvalidFulfillmentComponentData signature.
                              revert(0, InvalidFulfillmentComponentData_error_len)
                          }
                          // Declare function for reverts due to arithmetic overflows.
                          function throwOverflow() {
                              // Store the Panic error signature.
                              mstore(0, Panic_error_signature)
                              // Store the arithmetic (0x11) panic code as initial argument.
                              mstore(Panic_error_offset, Panic_arithmetic)
                              // Return, supplying Panic signature and arithmetic code.
                              revert(0, Panic_error_length)
                          }
                          // Get position in offerComponents head.
                          let fulfillmentHeadPtr := add(offerComponents, 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), mul(orderIndex, OneWord))
                          )
                          // Read the pointer to OrderParameters from the AdvancedOrder.
                          let paramsPtr := mload(orderPtr)
                          // Load the offer array pointer.
                          let offerArrPtr := mload(
                              add(paramsPtr, OrderParameters_offer_head_offset)
                          )
                          // Retrieve item index using an offset of the fulfillment pointer.
                          let itemIndex := mload(
                              add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
                          )
                          // Only continue if the fulfillment is not invalid.
                          if iszero(lt(itemIndex, mload(offerArrPtr))) {
                              throwInvalidFulfillmentComponentData()
                          }
                          // Retrieve consideration item pointer using the item index.
                          let offerItemPtr := mload(
                              add(
                                  // Get pointer to beginning of receivedItem.
                                  add(offerArrPtr, OneWord),
                                  // Calculate offset to pointer for desired order.
                                  mul(itemIndex, OneWord)
                              )
                          )
                          // Declare a variable for the final aggregated item amount.
                          let amount := 0
                          // Create variable to track errors encountered with amount.
                          let errorBuffer := 0
                          // Only add offer amount to execution amount on a nonzero numerator.
                          if mload(add(orderPtr, AdvancedOrder_numerator_offset)) {
                              // Retrieve amount pointer using consideration item pointer.
                              let amountPtr := add(offerItemPtr, Common_amount_offset)
                              // Set the amount.
                              amount := mload(amountPtr)
                              // Zero out amount on item to indicate it is credited.
                              mstore(amountPtr, 0)
                              // Buffer indicating whether issues were found.
                              errorBuffer := iszero(amount)
                          }
                          // Retrieve the received item pointer.
                          let receivedItemPtr := mload(execution)
                          // Set the item type on the received item.
                          mstore(receivedItemPtr, mload(offerItemPtr))
                          // Set the token on the received item.
                          mstore(
                              add(receivedItemPtr, Common_token_offset),
                              mload(add(offerItemPtr, Common_token_offset))
                          )
                          // Set the identifier on the received item.
                          mstore(
                              add(receivedItemPtr, Common_identifier_offset),
                              mload(add(offerItemPtr, Common_identifier_offset))
                          )
                          // Set the offerer on returned execution using order pointer.
                          mstore(add(execution, Execution_offerer_offset), mload(paramsPtr))
                          // Set conduitKey on returned execution via offset of order pointer.
                          mstore(
                              add(execution, Execution_conduit_offset),
                              mload(add(paramsPtr, OrderParameters_conduit_offset))
                          )
                          // Calculate the hash of (itemType, token, identifier).
                          let dataHash := keccak256(
                              receivedItemPtr,
                              ReceivedItem_CommonParams_size
                          )
                          // Get position one word past last element in head of array.
                          let endPtr := add(
                              offerComponents,
                              mul(mload(offerComponents), OneWord)
                          )
                          // Iterate over remaining offer components.
                          // prettier-ignore
                          for {} lt(fulfillmentHeadPtr,  endPtr) {} {
                              // Increment the pointer to the fulfillment head by one word.
                              fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
                              // Get the order index using the fulfillment pointer.
                              orderIndex := mload(mload(fulfillmentHeadPtr))
                              // Ensure the order index is in range.
                              if iszero(lt(orderIndex, mload(advancedOrders))) {
                                throwInvalidFulfillmentComponentData()
                              }
                              // Get pointer to AdvancedOrder element.
                              orderPtr := mload(
                                  add(
                                      add(advancedOrders, OneWord),
                                      mul(orderIndex, OneWord)
                                  )
                              )
                              // Only continue if numerator is not zero.
                              if iszero(mload(
                                  add(orderPtr, AdvancedOrder_numerator_offset)
                              )) {
                                continue
                              }
                              // Read the pointer to OrderParameters from the AdvancedOrder.
                              paramsPtr := mload(orderPtr)
                              // Load offer array pointer.
                              offerArrPtr := mload(
                                  add(
                                      paramsPtr,
                                      OrderParameters_offer_head_offset
                                  )
                              )
                              // Get the item index using the fulfillment pointer.
                              itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord))
                              // Throw if itemIndex is out of the range of array.
                              if iszero(
                                  lt(itemIndex, mload(offerArrPtr))
                              ) {
                                  throwInvalidFulfillmentComponentData()
                              }
                              // Retrieve offer item pointer using index.
                              offerItemPtr := mload(
                                  add(
                                      // Get pointer to beginning of receivedItem.
                                      add(offerArrPtr, OneWord),
                                      // Use offset to pointer for desired order.
                                      mul(itemIndex, OneWord)
                                  )
                              )
                              // Retrieve amount pointer using offer item pointer.
                              let amountPtr := add(
                                    offerItemPtr,
                                    Common_amount_offset
                              )
                              // Add offer 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 credited.
                              mstore(amountPtr, 0)
                              // Ensure the indicated item matches original item.
                              if iszero(
                                  and(
                                      and(
                                        // The offerer must match on both items.
                                        eq(
                                            mload(paramsPtr),
                                            mload(
                                                add(execution, Execution_offerer_offset)
                                            )
                                        ),
                                        // The conduit key must match on both items.
                                        eq(
                                            mload(
                                                add(
                                                    paramsPtr,
                                                    OrderParameters_conduit_offset
                                                )
                                            ),
                                            mload(
                                                add(
                                                    execution,
                                                    Execution_conduit_offset
                                                )
                                            )
                                        )
                                      ),
                                      // The itemType, token, and identifier must match.
                                      eq(
                                          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 the MissingItemAmount error signature.
                                  mstore(0, MissingItemAmount_error_signature)
                                  // Return, supplying MissingItemAmount signature.
                                  revert(0, MissingItemAmount_error_len)
                              }
                              // If errorBuffer is not 1 or 0, the sum overflowed.
                              // Panic!
                              throwOverflow()
                          }
                      }
                  }
                  /**
                   * @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.
                   *
                   * @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 function for reverts on invalid fulfillment data.
                          function throwInvalidFulfillmentComponentData() {
                              // Store the InvalidFulfillmentComponentData error signature.
                              mstore(0, InvalidFulfillmentComponentData_error_signature)
                              // Return, supplying InvalidFulfillmentComponentData signature.
                              revert(0, InvalidFulfillmentComponentData_error_len)
                          }
                          // Declare function for reverts due to arithmetic overflows.
                          function throwOverflow() {
                              // Store the Panic error signature.
                              mstore(0, Panic_error_signature)
                              // Store the arithmetic (0x11) panic code as initial argument.
                              mstore(Panic_error_offset, Panic_arithmetic)
                              // Return, supplying Panic signature and arithmetic code.
                              revert(0, Panic_error_length)
                          }
                          // Get position in considerationComponents head.
                          let fulfillmentHeadPtr := add(considerationComponents, 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), mul(orderIndex, OneWord))
                          )
                          // Load consideration array pointer.
                          let considerationArrPtr := mload(
                              add(
                                  // Read pointer to OrderParameters from the AdvancedOrder.
                                  mload(orderPtr),
                                  OrderParameters_consideration_head_offset
                              )
                          )
                          // Retrieve item index using an offset of the fulfillment pointer.
                          let itemIndex := mload(
                              add(mload(fulfillmentHeadPtr), Fulfillment_itemIndex_offset)
                          )
                          // Ensure that the order index is not out of range.
                          if iszero(lt(itemIndex, mload(considerationArrPtr))) {
                              throwInvalidFulfillmentComponentData()
                          }
                          // Retrieve consideration item pointer using the item index.
                          let considerationItemPtr := mload(
                              add(
                                  // Get pointer to beginning of receivedItem.
                                  add(considerationArrPtr, OneWord),
                                  // Calculate offset to pointer for desired order.
                                  mul(itemIndex, OneWord)
                              )
                          )
                          // Declare a variable for the final aggregated item amount.
                          let amount := 0
                          // Create variable to track errors encountered with amount.
                          let errorBuffer := 0
                          // Only add consideration amount to execution amount if numerator is
                          // greater than zero.
                          if mload(add(orderPtr, AdvancedOrder_numerator_offset)) {
                              // Retrieve amount pointer using consideration item pointer.
                              let amountPtr := add(considerationItemPtr, Common_amount_offset)
                              // Set the amount.
                              amount := mload(amountPtr)
                              // Set error bit if amount is zero.
                              errorBuffer := iszero(amount)
                              // Zero out amount on item to indicate it is credited.
                              mstore(amountPtr, 0)
                          }
                          // Retrieve ReceivedItem pointer from Execution.
                          let receivedItem := mload(execution)
                          // 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.
                          mstore(
                              add(receivedItem, ReceivedItem_recipient_offset),
                              mload(
                                  add(
                                      considerationItemPtr,
                                      ConsiderationItem_recipient_offset
                                  )
                              )
                          )
                          // Calculate the hash of (itemType, token, identifier).
                          let dataHash := keccak256(
                              receivedItem,
                              ReceivedItem_CommonParams_size
                          )
                          // Get position one word past last element in head of array.
                          let endPtr := add(
                              considerationComponents,
                              mul(mload(considerationComponents), OneWord)
                          )
                          // Iterate over remaining offer components.
                          // prettier-ignore
                          for {} lt(fulfillmentHeadPtr,  endPtr) {} {
                              // Increment position in considerationComponents head.
                              fulfillmentHeadPtr := add(fulfillmentHeadPtr, OneWord)
                              // Get the order index using the fulfillment pointer.
                              orderIndex := mload(mload(fulfillmentHeadPtr))
                              // Ensure the order index is in range.
                              if iszero(lt(orderIndex, mload(advancedOrders))) {
                                throwInvalidFulfillmentComponentData()
                              }
                              // Get pointer to AdvancedOrder element.
                              orderPtr := mload(
                                  add(
                                      add(advancedOrders, OneWord),
                                      mul(orderIndex, OneWord)
                                  )
                              )
                              // Only continue if numerator is not zero.
                              if iszero(
                                  mload(add(orderPtr, AdvancedOrder_numerator_offset))
                              ) {
                                continue
                              }
                              // Load consideration array pointer from OrderParameters.
                              considerationArrPtr := mload(
                                  add(
                                      // Get pointer to OrderParameters from AdvancedOrder.
                                      mload(orderPtr),
                                      OrderParameters_consideration_head_offset
                                  )
                              )
                              // Get the item index using the fulfillment pointer.
                              itemIndex := mload(add(mload(fulfillmentHeadPtr), OneWord))
                              // Check if itemIndex is within the range of array.
                              if iszero(lt(itemIndex, mload(considerationArrPtr))) {
                                  throwInvalidFulfillmentComponentData()
                              }
                              // Retrieve consideration item pointer using index.
                              considerationItemPtr := mload(
                                  add(
                                      // Get pointer to beginning of receivedItem.
                                      add(considerationArrPtr, OneWord),
                                      // Use offset to pointer for desired order.
                                      mul(itemIndex, OneWord)
                                  )
                              )
                              // Retrieve amount pointer using consideration item pointer.
                              let amountPtr := add(
                                    considerationItemPtr,
                                    Common_amount_offset
                              )
                              // Add offer 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 credited.
                              mstore(amountPtr, 0)
                              // Ensure the indicated item matches original item.
                              if iszero(
                                  and(
                                      // Item recipients must match.
                                      eq(
                                          mload(
                                              add(
                                                  considerationItemPtr,
                                                  ConsiderItem_recipient_offset
                                              )
                                          ),
                                          mload(
                                              add(
                                                  receivedItem,
                                                  ReceivedItem_recipient_offset
                                              )
                                          )
                                      ),
                                      // The itemType, token, identifier must match.
                                      eq(
                                        dataHash,
                                        keccak256(
                                          considerationItemPtr,
                                          ReceivedItem_CommonParams_size
                                        )
                                      )
                                  )
                              ) {
                                  // Throw if any of the requirements are not met.
                                  throwInvalidFulfillmentComponentData()
                              }
                          }
                          // 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 the MissingItemAmount error signature.
                                  mstore(0, MissingItemAmount_error_signature)
                                  // Return, supplying MissingItemAmount signature.
                                  revert(0, MissingItemAmount_error_len)
                              }
                              // If errorBuffer is not 1 or 0, the sum overflowed.
                              // Panic!
                              throwOverflow()
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              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.
                   */
                  error MismatchedFulfillmentOfferAndConsiderationComponents();
                  /**
                   * @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.13;
              import { ItemType, Side } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  OfferItem,
                  ConsiderationItem,
                  OrderParameters,
                  AdvancedOrder,
                  CriteriaResolver
              } from "./ConsiderationStructs.sol";
              import "./ConsiderationConstants.sol";
              // prettier-ignore
              import {
                  CriteriaResolutionErrors
              } from "../interfaces/CriteriaResolutionErrors.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) {
                                  revert OrderCriteriaResolverOutOfRange();
                              }
                              // Skip criteria resolution for order if not fulfilled.
                              if (advancedOrders[orderIndex].numerator == 0) {
                                  continue;
                              }
                              // Retrieve the parameters for the order.
                              OrderParameters memory orderParameters = (
                                  advancedOrders[orderIndex].parameters
                              );
                              // Read component index from memory and place it on the stack.
                              uint256 componentIndex = criteriaResolver.index;
                              // Declare values for item's type and criteria.
                              ItemType itemType;
                              uint256 identifierOrCriteria;
                              // If the criteria resolver refers to an offer item...
                              if (criteriaResolver.side == Side.OFFER) {
                                  // Retrieve the offer.
                                  OfferItem[] memory offer = orderParameters.offer;
                                  // Ensure that the component index is in range.
                                  if (componentIndex >= offer.length) {
                                      revert OfferCriteriaResolverOutOfRange();
                                  }
                                  // Retrieve relevant item using the component index.
                                  OfferItem memory offerItem = offer[componentIndex];
                                  // Read item type and criteria from memory & place on stack.
                                  itemType = offerItem.itemType;
                                  identifierOrCriteria = offerItem.identifierOrCriteria;
                                  // Optimistically 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;
                                  // Optimistically update identifier w/ supplied identifier.
                                  offerItem.identifierOrCriteria = criteriaResolver
                                      .identifier;
                              } else {
                                  // Otherwise, the resolver refers to a consideration item.
                                  ConsiderationItem[] memory consideration = (
                                      orderParameters.consideration
                                  );
                                  // Ensure that the component index is in range.
                                  if (componentIndex >= consideration.length) {
                                      revert ConsiderationCriteriaResolverOutOfRange();
                                  }
                                  // Retrieve relevant item using order and component index.
                                  ConsiderationItem memory considerationItem = (
                                      consideration[componentIndex]
                                  );
                                  // Read item type and criteria from memory & place on stack.
                                  itemType = considerationItem.itemType;
                                  identifierOrCriteria = (
                                      considerationItem.identifierOrCriteria
                                  );
                                  // Optimistically 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))
                                  }
                                  considerationItem.itemType = newItemType;
                                  // Optimistically update identifier w/ supplied identifier.
                                  considerationItem.identifierOrCriteria = (
                                      criteriaResolver.identifier
                                  );
                              }
                              // Ensure the specified item type indicates criteria usage.
                              if (!_isItemWithCriteria(itemType)) {
                                  revert CriteriaNotEnabledForItem();
                              }
                              // If criteria is not 0 (i.e. a collection-wide offer)...
                              if (identifierOrCriteria != uint256(0)) {
                                  // Verify identifier inclusion in criteria root using proof.
                                  _verifyProof(
                                      criteriaResolver.identifier,
                                      identifierOrCriteria,
                                      criteriaResolver.criteriaProof
                                  );
                              }
                          }
                          // 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
                                      )
                                  ) {
                                      revert UnresolvedConsiderationCriteria();
                                  }
                              }
                              // 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)
                                  ) {
                                      revert UnresolvedOfferCriteria();
                                  }
                              }
                          }
                      }
                  }
                  /**
                   * @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)
                          // Based on: https://github.com/Rari-Capital/solmate/blob/v7/src/utils/MerkleProof.sol
                          // 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(5, 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(5, 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) {
                          revert InvalidProof();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              /**
               * @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.
                   */
                  error OrderCriteriaResolverOutOfRange();
                  /**
                   * @dev Revert with an error if an offer item still has unresolved criteria
                   *      after applying all criteria resolvers.
                   */
                  error UnresolvedOfferCriteria();
                  /**
                   * @dev Revert with an error if a consideration item still has unresolved
                   *      criteria after applying all criteria resolvers.
                   */
                  error UnresolvedConsiderationCriteria();
                  /**
                   * @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.7;
              import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
              // prettier-ignore
              import {
                  AdvancedOrder,
                  CriteriaResolver
              } from "../lib/ConsiderationStructs.sol";
              contract TestZone is ZoneInterface {
                  function isValidOrder(
                      bytes32 orderHash,
                      address caller,
                      address offerer,
                      bytes32 zoneHash
                  ) external pure override returns (bytes4 validOrderMagicValue) {
                      orderHash;
                      caller;
                      offerer;
                      if (zoneHash == bytes32(uint256(1))) {
                          revert("Revert on zone hash 1");
                      } else if (zoneHash == bytes32(uint256(2))) {
                          assembly {
                              revert(0, 0)
                          }
                      }
                      validOrderMagicValue = zoneHash != bytes32(uint256(3))
                          ? ZoneInterface.isValidOrder.selector
                          : bytes4(0xffffffff);
                  }
                  function isValidOrderIncludingExtraData(
                      bytes32 orderHash,
                      address caller,
                      AdvancedOrder calldata order,
                      bytes32[] calldata priorOrderHashes,
                      CriteriaResolver[] calldata criteriaResolvers
                  ) external pure override returns (bytes4 validOrderMagicValue) {
                      orderHash;
                      caller;
                      order;
                      priorOrderHashes;
                      criteriaResolvers;
                      if (order.extraData.length == 4) {
                          revert("Revert on extraData length 4");
                      } else if (order.extraData.length == 5) {
                          assembly {
                              revert(0, 0)
                          }
                      }
                      validOrderMagicValue = order.parameters.zoneHash != bytes32(uint256(3))
                          ? ZoneInterface.isValidOrder.selector
                          : bytes4(0xffffffff);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              // prettier-ignore
              import {
                  AdvancedOrder,
                  CriteriaResolver
              } from "../lib/ConsiderationStructs.sol";
              interface ZoneInterface {
                  // Called by Consideration whenever extraData is not provided by the caller.
                  function isValidOrder(
                      bytes32 orderHash,
                      address caller,
                      address offerer,
                      bytes32 zoneHash
                  ) external view returns (bytes4 validOrderMagicValue);
                  // Called by Consideration whenever any extraData is provided by the caller.
                  function isValidOrderIncludingExtraData(
                      bytes32 orderHash,
                      address caller,
                      AdvancedOrder calldata order,
                      bytes32[] calldata priorOrderHashes,
                      CriteriaResolver[] calldata criteriaResolvers
                  ) external view returns (bytes4 validOrderMagicValue);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
              import { OrderType } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import { AdvancedOrder, CriteriaResolver } from "./ConsiderationStructs.sol";
              import "./ConsiderationConstants.sol";
              // prettier-ignore
              import {
                  ZoneInteractionErrors
              } from "../interfaces/ZoneInteractionErrors.sol";
              import { LowLevelHelpers } from "./LowLevelHelpers.sol";
              /**
               * @title ZoneInteraction
               * @author 0age
               * @notice ZoneInteraction contains logic related to interacting with zones.
               */
              contract ZoneInteraction is ZoneInteractionErrors, LowLevelHelpers {
                  /**
                   * @dev Internal view function to determine if an order has a restricted
                   *      order type and, if so, to ensure that either the offerer or the zone
                   *      are the fulfiller or that a staticcall to `isValidOrder` on the zone
                   *      returns a magic value indicating that the order is currently valid.
                   *
                   * @param orderHash The hash of the order.
                   * @param zoneHash  The hash to provide upon calling the zone.
                   * @param orderType The type of the order.
                   * @param offerer   The offerer in question.
                   * @param zone      The zone in question.
                   */
                  function _assertRestrictedBasicOrderValidity(
                      bytes32 orderHash,
                      bytes32 zoneHash,
                      OrderType orderType,
                      address offerer,
                      address zone
                  ) internal view {
                      // Order type 2-3 require zone or offerer be caller or zone to approve.
                      if (
                          uint256(orderType) > 1 &&
                          msg.sender != zone &&
                          msg.sender != offerer
                      ) {
                          // Perform minimal staticcall to the zone.
                          _callIsValidOrder(zone, orderHash, offerer, zoneHash);
                      }
                  }
                  function _callIsValidOrder(
                      address zone,
                      bytes32 orderHash,
                      address offerer,
                      bytes32 zoneHash
                  ) internal view {
                      // Perform minimal staticcall to the zone.
                      bool success = _staticcall(
                          zone,
                          abi.encodeWithSelector(
                              ZoneInterface.isValidOrder.selector,
                              orderHash,
                              msg.sender,
                              offerer,
                              zoneHash
                          )
                      );
                      // Ensure call was successful and returned the correct magic value.
                      _assertIsValidOrderStaticcallSuccess(success, orderHash);
                  }
                  /**
                   * @dev Internal view function to determine whether an order is a restricted
                   *      order and, if so, to ensure that it was either submitted by the
                   *      offerer or the zone for the order, or that the zone returns the
                   *      expected magic value upon performing a staticcall to `isValidOrder`
                   *      or `isValidOrderIncludingExtraData` depending on whether the order
                   *      fulfillment specifies extra data or criteria resolvers.
                   *
                   * @param advancedOrder     The advanced order in question.
                   * @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 priorOrderHashes  The order hashes of each order supplied prior to
                   *                          the current order as part of a "match" variety
                   *                          of order fulfillment (e.g. this array will be
                   *                          empty for single or "fulfill available").
                   * @param orderHash         The hash of the order.
                   * @param zoneHash          The hash to provide upon calling the zone.
                   * @param orderType         The type of the order.
                   * @param offerer           The offerer in question.
                   * @param zone              The zone in question.
                   */
                  function _assertRestrictedAdvancedOrderValidity(
                      AdvancedOrder memory advancedOrder,
                      CriteriaResolver[] memory criteriaResolvers,
                      bytes32[] memory priorOrderHashes,
                      bytes32 orderHash,
                      bytes32 zoneHash,
                      OrderType orderType,
                      address offerer,
                      address zone
                  ) internal view {
                      // Order type 2-3 require zone or offerer be caller or zone to approve.
                      if (
                          uint256(orderType) > 1 &&
                          msg.sender != zone &&
                          msg.sender != offerer
                      ) {
                          // If no extraData or criteria resolvers are supplied...
                          if (
                              advancedOrder.extraData.length == 0 &&
                              criteriaResolvers.length == 0
                          ) {
                              // Perform minimal staticcall to the zone.
                              _callIsValidOrder(zone, orderHash, offerer, zoneHash);
                          } else {
                              // Otherwise, extra data or criteria resolvers were supplied; in
                              // that event, perform a more verbose staticcall to the zone.
                              bool success = _staticcall(
                                  zone,
                                  abi.encodeWithSelector(
                                      ZoneInterface.isValidOrderIncludingExtraData.selector,
                                      orderHash,
                                      msg.sender,
                                      advancedOrder,
                                      priorOrderHashes,
                                      criteriaResolvers
                                  )
                              );
                              // Ensure call was successful and returned correct magic value.
                              _assertIsValidOrderStaticcallSuccess(success, orderHash);
                          }
                      }
                  }
                  /**
                   * @dev Internal view function to ensure that a staticcall to `isValidOrder`
                   *      or `isValidOrderIncludingExtraData` as part of validating a
                   *      restricted order that was not submitted by the named offerer or zone
                   *      was successful and returned the required magic value.
                   *
                   * @param success   A boolean indicating the status of the staticcall.
                   * @param orderHash The order hash of the order in question.
                   */
                  function _assertIsValidOrderStaticcallSuccess(
                      bool success,
                      bytes32 orderHash
                  ) internal view {
                      // If the call failed...
                      if (!success) {
                          // Revert and pass reason along if one was returned.
                          _revertWithReasonIfOneIsReturned();
                          // Otherwise, revert with a generic error message.
                          revert InvalidRestrictedOrder(orderHash);
                      }
                      // Ensure result was extracted and matches isValidOrder magic value.
                      if (_doesNotMatchMagic(ZoneInterface.isValidOrder.selector)) {
                          revert InvalidRestrictedOrder(orderHash);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              /**
               * @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 staticcall to `isValidOrder`.
                   *
                   * @param orderHash The order hash for the invalid restricted order.
                   */
                  error InvalidRestrictedOrder(bytes32 orderHash);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { ConduitInterface } from "../interfaces/ConduitInterface.sol";
              // prettier-ignore
              import {
                  OrderType,
                  ItemType,
                  BasicOrderRouteType
              } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  AdditionalRecipient,
                  BasicOrderParameters,
                  OfferItem,
                  ConsiderationItem,
                  SpentItem,
                  ReceivedItem
              } from "./ConsiderationStructs.sol";
              import { OrderValidator } from "./OrderValidator.sol";
              import "./ConsiderationConstants.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;
                      // 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 Eth (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) {
                              revert InvalidMsgValue(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 Eth (0).
                              receivedItemType := add(
                                  mul(sub(route, 2), gt(route, 2)),
                                  eq(route, 2)
                              )
                              // If route > 3, offeredItemType is ERC20 (1). Route is 2 or 3,
                              // offeredItemType = route. Route is 0 or 1, it is route + 2.
                              offeredItemType := sub(
                                  add(route, mul(iszero(additionalRecipientsItemType), 2)),
                                  mul(
                                      offerTypeIsAdditionalRecipientsType,
                                      add(receivedItemType, 1)
                                  )
                              )
                          }
                          // Derive & validate order using parameters and update order status.
                          _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,
                                  mul(offerTypeIsAdditionalRecipientsType, OneWord)
                              )
                          )
                      }
                      // Transfer tokens based on the route.
                      if (additionalRecipientsItemType == ItemType.NATIVE) {
                          // Ensure neither the token nor the identifier parameters are set.
                          if (
                              (uint160(parameters.considerationToken) |
                                  parameters.considerationIdentifier) != 0
                          ) {
                              revert UnusedItemParameters();
                          }
                          // Transfer the ERC721 or ERC1155 item, bypassing the accumulator.
                          _transferIndividual721Or1155Item(
                              offeredItemType,
                              parameters.offerToken,
                              parameters.offerer,
                              msg.sender,
                              parameters.offerIdentifier,
                              parameters.offerAmount,
                              conduitKey
                          );
                          // Transfer native to recipients, return excess to caller & wrap up.
                          _transferEthAndFinalize(
                              parameters.considerationAmount,
                              parameters.offerer,
                              parameters.additionalRecipients
                          );
                      } 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(
                              parameters.offerer,
                              parameters,
                              offerTypeIsAdditionalRecipientsType,
                              accumulator
                          );
                          // Trigger any remaining accumulated transfers via call to conduit.
                          _triggerIfArmed(accumulator);
                      }
                      // 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. It does not clear the expanded
                   *      memory regions used, nor does it update the free memory pointer, so
                   *      other direct memory access must not assume that unused memory is
                   *      empty.
                   *
                   * @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.
                   */
                  function _prepareBasicFulfillmentFromCalldata(
                      BasicOrderParameters calldata parameters,
                      OrderType orderType,
                      ItemType receivedItemType,
                      ItemType additionalRecipientsItemType,
                      address additionalRecipientsToken,
                      ItemType offeredItemType
                  ) internal {
                      // Ensure this function cannot be triggered during a reentrant call.
                      _setReentrancyGuard();
                      // Ensure current timestamp falls between order start time and end time.
                      _verifyTime(parameters.startTime, parameters.endTime, true);
                      // Verify that calldata offsets for all dynamic types were produced by
                      // default encoding. This ensures that the constants we use 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();
                      // Ensure supplied consideration array length is not less than original.
                      _assertConsiderationLengthIsNotLessThanOriginalConsiderationLength(
                          parameters.additionalRecipients.length,
                          parameters.totalOriginalAdditionalRecipients
                      );
                      // Declare stack element for the order hash.
                      bytes32 orderHash;
                      {
                          /**
                           * 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,
                                  mul(totalAdditionalRecipients, OneWord)
                              )
                              // Set the length of the consideration array to the number of
                              // additional recipients, plus one for the primary consideration
                              // item.
                              mstore(
                                  eventConsiderationArrPtr,
                                  add(
                                      calldataload(
                                          BasicOrder_additionalRecipients_length_cdPtr
                                      ),
                                      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 considerations signed by
                               * the offerer aside from the primary consideration 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)
                              // Read length of the additionalRecipients array from calldata
                              // and iterate.
                              totalAdditionalRecipients := calldataload(
                                  BasicOrder_totalOriginalAdditionalRecipients_cdPtr
                              )
                              let i := 0
                              // prettier-ignore
                              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(AdditionalRecipients_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,
                                      AdditionalRecipients_size
                                  )
                                  // 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,
                                      mul(add(totalAdditionalRecipients, 1), OneWord)
                                  )
                              )
                              /*
                               * 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
                              )
                              // prettier-ignore
                              for {} lt(i, totalAdditionalRecipients) {
                                  i := add(i, 1)
                              } {
                                  // Retrieve calldata pointer for additional recipient.
                                  let additionalRecipientCdPtr := add(
                                      BasicOrder_additionalRecipients_data_cdPtr,
                                      mul(AdditionalRecipients_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
                                  )
                              }
                          }
                      }
                      {
                          /**
                           * 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,
                                  mul(
                                      calldataload(
                                          BasicOrder_additionalRecipients_length_cdPtr
                                      ),
                                      OneWord
                                  )
                              )
                              // 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, AdditionalRecipients_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,
                              mul(
                                  calldataload(BasicOrder_additionalRecipients_length_cdPtr),
                                  OneWord
                              )
                          )
                          // 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)
                      }
                      // Determine whether order is restricted and, if so, that it is valid.
                      _assertRestrictedBasicOrderValidity(
                          orderHash,
                          parameters.zoneHash,
                          orderType,
                          parameters.offerer,
                          parameters.zone
                      );
                      // Verify and update the status of the derived order.
                      _validateBasicOrderAndUpdateStatus(
                          orderHash,
                          parameters.offerer,
                          parameters.signature
                      );
                  }
                  /**
                   * @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.
                   *
                   * @param amount               The amount to transfer.
                   * @param to                   The recipient of the native token transfer.
                   * @param additionalRecipients The additional recipients of the order.
                   */
                  function _transferEthAndFinalize(
                      uint256 amount,
                      address payable to,
                      AdditionalRecipient[] calldata additionalRecipients
                  ) internal {
                      // Put ether value supplied by the caller on the stack.
                      uint256 etherRemaining = msg.value;
                      // Retrieve total number of additional recipients and place on stack.
                      uint256 totalAdditionalRecipients = additionalRecipients.length;
                      // Skip overflow check as for loop is indexed starting at zero.
                      unchecked {
                          // Iterate over each additional recipient.
                          for (uint256 i = 0; i < totalAdditionalRecipients; ++i) {
                              // Retrieve the additional recipient.
                              AdditionalRecipient calldata additionalRecipient = (
                                  additionalRecipients[i]
                              );
                              // Read ether amount to transfer to recipient & place on stack.
                              uint256 additionalRecipientAmount = additionalRecipient.amount;
                              // Ensure that sufficient Ether is available.
                              if (additionalRecipientAmount > etherRemaining) {
                                  revert InsufficientEtherSupplied();
                              }
                              // Transfer Ether to the additional recipient.
                              _transferEth(
                                  additionalRecipient.recipient,
                                  additionalRecipientAmount
                              );
                              // Reduce ether value available. Skip underflow check as
                              // subtracted value is confirmed above as less than remaining.
                              etherRemaining -= additionalRecipientAmount;
                          }
                      }
                      // Ensure that sufficient Ether is still available.
                      if (amount > etherRemaining) {
                          revert InsufficientEtherSupplied();
                      }
                      // Transfer Ether to the offerer.
                      _transferEth(to, amount);
                      // If any Ether remains after transfers, return it to the caller.
                      if (etherRemaining > amount) {
                          // Skip underflow check as etherRemaining > amount.
                          unchecked {
                              // Transfer remaining Ether to the caller.
                              _transferEth(payable(msg.sender), etherRemaining - amount);
                          }
                      }
                  }
                  /**
                   * @dev Internal function to transfer ERC20 tokens to a given recipient as
                   *      part of basic order fulfillment.
                   *
                   * @param offerer     The offerer of the fulfiller order.
                   * @param parameters  The basic order parameters.
                   * @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(
                      address offerer,
                      BasicOrderParameters calldata parameters,
                      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 and msg.sender as to value.
                              from = offerer;
                              to = msg.sender;
                              // Use offer token and related values if token is from offerer.
                              token = parameters.offerToken;
                              identifier = parameters.offerIdentifier;
                              amount = parameters.offerAmount;
                          } else {
                              // Use msg.sender as from value and offerer as to value.
                              from = msg.sender;
                              to = offerer;
                              // Otherwise, use consideration token and related values.
                              token = parameters.considerationToken;
                              identifier = parameters.considerationIdentifier;
                              amount = parameters.considerationAmount;
                          }
                          // Ensure that no identifier is supplied.
                          if (identifier != 0) {
                              revert UnusedItemParameters();
                          }
                      }
                      // 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,
                                  mul(fromOfferer, OneWord)
                              )
                          )
                      }
                      // Retrieve total number of additional recipients and place on stack.
                      uint256 totalAdditionalRecipients = (
                          parameters.additionalRecipients.length
                      );
                      // Iterate over each additional recipient.
                      for (uint256 i = 0; i < totalAdditionalRecipients; ) {
                          // Retrieve the additional recipient.
                          AdditionalRecipient calldata additionalRecipient = (
                              parameters.additionalRecipients[i]
                          );
                          uint256 additionalRecipientAmount = additionalRecipient.amount;
                          // Decrement the amount to transfer to fulfiller if indicated.
                          if (fromOfferer) {
                              amount -= additionalRecipientAmount;
                          }
                          // Transfer ERC20 tokens to additional recipient given approval.
                          _transferERC20(
                              token,
                              from,
                              additionalRecipient.recipient,
                              additionalRecipientAmount,
                              conduitKey,
                              accumulator
                          );
                          // Skip overflow check as for loop is indexed starting at zero.
                          unchecked {
                              ++i;
                          }
                      }
                      // 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 { OrderType } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  OrderParameters,
                  Order,
                  AdvancedOrder,
                  OrderComponents,
                  OrderStatus,
                  CriteriaResolver
              } from "./ConsiderationStructs.sol";
              import "./ConsiderationConstants.sol";
              import { Executor } from "./Executor.sol";
              import { ZoneInteraction } from "./ZoneInteraction.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;
                  /**
                   * @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.
                   *
                   * @param orderHash The hash of the order.
                   * @param offerer   The offerer of the order.
                   * @param signature A signature from the offerer indicating that the order
                   *                  has been approved.
                   */
                  function _validateBasicOrderAndUpdateStatus(
                      bytes32 orderHash,
                      address offerer,
                      bytes memory signature
                  ) internal {
                      // 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 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 revertOnInvalid   A boolean indicating whether to revert if the
                   *                          order is invalid due to the time or status.
                   * @param priorOrderHashes  The order hashes of each order supplied prior to
                   *                          the current order as part of a "match" variety
                   *                          of order fulfillment (e.g. this array will be
                   *                          empty for single or "fulfill available").
                   *
                   * @return orderHash      The order hash.
                   * @return newNumerator   A value indicating the portion of the order that
                   *                        will be filled.
                   * @return newDenominator A value indicating the total size of the order.
                   */
                  function _validateOrderAndUpdateStatus(
                      AdvancedOrder memory advancedOrder,
                      CriteriaResolver[] memory criteriaResolvers,
                      bool revertOnInvalid,
                      bytes32[] memory priorOrderHashes
                  )
                      internal
                      returns (
                          bytes32 orderHash,
                          uint256 newNumerator,
                          uint256 newDenominator
                      )
                  {
                      // 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.
                      uint256 numerator = uint256(advancedOrder.numerator);
                      uint256 denominator = uint256(advancedOrder.denominator);
                      // Ensure that the supplied numerator and denominator are valid.
                      if (numerator > denominator || numerator == 0) {
                          revert BadFraction();
                      }
                      // If attempting partial fill (n < d) check order type & ensure support.
                      if (
                          numerator < denominator &&
                          _doesNotSupportPartialFills(orderParameters.orderType)
                      ) {
                          // Revert if partial fill was attempted on an unsupported order.
                          revert PartialFillsNotEnabledForOrder();
                      }
                      // Retrieve current counter & use it w/ parameters to derive order hash.
                      orderHash = _assertConsiderationLengthAndGetOrderHash(orderParameters);
                      // Ensure restricted orders have a valid submitter or pass a zone check.
                      _assertRestrictedAdvancedOrderValidity(
                          advancedOrder,
                          criteriaResolvers,
                          priorOrderHashes,
                          orderHash,
                          orderParameters.zoneHash,
                          orderParameters.orderType,
                          orderParameters.offerer,
                          orderParameters.zone
                      );
                      // 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
                          );
                      }
                      // Read filled amount as numerator and denominator and put on the stack.
                      uint256 filledNumerator = orderStatus.numerator;
                      uint256 filledDenominator = orderStatus.denominator;
                      // If order (orderStatus) currently has a non-zero denominator it is
                      // partially filled.
                      if (filledDenominator != 0) {
                          // If denominator of 1 supplied, fill all remaining amount on order.
                          if (denominator == 1) {
                              // Scale numerator & denominator to match current denominator.
                              numerator = filledDenominator;
                              denominator = filledDenominator;
                          }
                          // Otherwise, if supplied denominator differs from current one...
                          else if (filledDenominator != denominator) {
                              // scale current numerator by the supplied denominator, then...
                              filledNumerator *= denominator;
                              // the supplied numerator & denominator by current denominator.
                              numerator *= filledDenominator;
                              denominator *= filledDenominator;
                          }
                          // Once adjusted, if current+supplied numerator exceeds denominator:
                          if (filledNumerator + numerator > denominator) {
                              // Skip underflow check: denominator >= orderStatus.numerator
                              unchecked {
                                  // Reduce current numerator so it + supplied = denominator.
                                  numerator = denominator - filledNumerator;
                              }
                          }
                          // Increment the filled numerator by the new numerator.
                          filledNumerator += numerator;
                          // Use assembly to ensure fractional amounts are below max uint120.
                          assembly {
                              // 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 {
                                      for {
                                      } _b {
                                      } {
                                          let _c := _b
                                          _b := mod(_a, _c)
                                          _a := _c
                                      }
                                      out := _a
                                  }
                                  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_signature)
                                      // Set arithmetic (0x11) panic code as initial argument.
                                      mstore(Panic_error_offset, Panic_arithmetic)
                                      // Return, supplying Panic signature & arithmetic code.
                                      revert(0, Panic_error_length)
                                  }
                              }
                          }
                          // Skip overflow check: checked above unless numerator is reduced.
                          unchecked {
                              // Update order status and fill amount, packing struct values.
                              orderStatus.isValidated = true;
                              orderStatus.isCancelled = false;
                              orderStatus.numerator = uint120(filledNumerator);
                              orderStatus.denominator = uint120(denominator);
                          }
                      } else {
                          // Update order status and fill amount, packing struct values.
                          orderStatus.isValidated = true;
                          orderStatus.isCancelled = false;
                          orderStatus.numerator = uint120(numerator);
                          orderStatus.denominator = uint120(denominator);
                      }
                      // Return order hash, a modified numerator, and a modified denominator.
                      return (orderHash, numerator, denominator);
                  }
                  /**
                   * @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`.
                   *
                   * @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;
                      address offerer;
                      address zone;
                      // 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];
                              offerer = order.offerer;
                              zone = order.zone;
                              // Ensure caller is either offerer or zone of the order.
                              if (msg.sender != offerer && msg.sender != zone) {
                                  revert InvalidCanceller();
                              }
                              // Derive order hash using the order parameters and the counter.
                              bytes32 orderHash = _deriveOrderHash(
                                  OrderParameters(
                                      offerer,
                                      zone,
                                      order.offer,
                                      order.consideration,
                                      order.orderType,
                                      order.startTime,
                                      order.endTime,
                                      order.zoneHash,
                                      order.salt,
                                      order.conduitKey,
                                      order.consideration.length
                                  ),
                                  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;
                          }
                      }
                      // 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[] calldata 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; ) {
                              // Retrieve the order.
                              Order calldata order = orders[i];
                              // Retrieve the order parameters.
                              OrderParameters calldata orderParameters = order.parameters;
                              // 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) {
                                  // 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,
                                      offerer,
                                      orderParameters.zone
                                  );
                              }
                              // Increment counter inside body of the loop for gas efficiency.
                              ++i;
                          }
                      }
                      // 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 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.
                   *
                   * @return isFullOrder A boolean indicating whether the order type only
                   *                     supports full fills.
                   */
                  function _doesNotSupportPartialFills(OrderType orderType)
                      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.
                      assembly {
                          // Equivalent to `uint256(orderType) & 1 == 0`.
                          isFullOrder := iszero(and(orderType, 1))
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              import { ItemType } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  OfferItem,
                  ConsiderationItem,
                  SpentItem,
                  ReceivedItem,
                  OrderParameters,
                  Order,
                  AdvancedOrder,
                  CriteriaResolver
              } from "./ConsiderationStructs.sol";
              import { BasicOrderFulfiller } from "./BasicOrderFulfiller.sol";
              import { CriteriaResolution } from "./CriteriaResolution.sol";
              import { AmountDeriver } from "./AmountDeriver.sol";
              import "./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();
                      // Declare empty bytes32 array (unused, will remain empty).
                      bytes32[] memory priorOrderHashes;
                      // Validate order, update status, and determine fraction to fill.
                      (
                          bytes32 orderHash,
                          uint256 fillNumerator,
                          uint256 fillDenominator
                      ) = _validateOrderAndUpdateStatus(
                              advancedOrder,
                              criteriaResolvers,
                              true,
                              priorOrderHashes
                          );
                      // 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
                      );
                      // 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 in order to recast the type of a function, we need to
                      // create a local variable to reference the internal function definition
                      // (using the same type) and a local variable with the desired type,
                      // and then cast the original function pointer 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 {
                          // Declare a virtual function pointer taking an OfferItem argument.
                          function(OfferItem memory, address, bytes32, bytes memory)
                              internal _transferOfferItem;
                          {
                              // Assign _transfer function to a new function pointer (it takes
                              // a ReceivedItem as its initial argument)
                              function(ReceivedItem memory, address, bytes32, bytes memory)
                                  internal _transferReceivedItem = _transfer;
                              // Utilize assembly to override the virtual function pointer.
                              assembly {
                                  // Cast initial ReceivedItem type to an OfferItem type.
                                  _transferOfferItem := _transferReceivedItem
                              }
                          }
                          // Read offer array length from memory and place on stack.
                          uint256 totalOfferItems = orderParameters.offer.length;
                          // 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.
                              if (offerItem.itemType == ItemType.NATIVE) {
                                  revert InvalidNativeOfferItem();
                              }
                              // 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.
                              _transferOfferItem(
                                  offerItem,
                                  orderParameters.offerer,
                                  orderParameters.conduitKey,
                                  accumulator
                              );
                          }
                      }
                      // Put ether value supplied by the caller on the stack.
                      uint256 etherRemaining = msg.value;
                      /**
                       * 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 {
                          // Declare virtual function pointer with ConsiderationItem argument.
                          function(ConsiderationItem memory, address, bytes32, bytes memory)
                              internal _transferConsiderationItem;
                          {
                              // Reassign _transfer function to a new function pointer (it
                              // takes a ReceivedItem as its initial argument).
                              function(ReceivedItem memory, address, bytes32, bytes memory)
                                  internal _transferReceivedItem = _transfer;
                              // Utilize assembly to override the virtual function pointer.
                              assembly {
                                  // Cast ReceivedItem type to ConsiderationItem type.
                                  _transferConsiderationItem := _transferReceivedItem
                              }
                          }
                          // 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
                                          )
                                      )
                                  )
                              }
                              // Reduce available value if offer spent ETH or a native token.
                              if (considerationItem.itemType == ItemType.NATIVE) {
                                  // Ensure that sufficient native tokens are still available.
                                  if (amount > etherRemaining) {
                                      revert InsufficientEtherSupplied();
                                  }
                                  // Skip underflow check as a comparison has just been made.
                                  etherRemaining -= amount;
                              }
                              // Transfer item from caller to recipient specified by the item.
                              _transferConsiderationItem(
                                  considerationItem,
                                  msg.sender,
                                  fulfillerConduitKey,
                                  accumulator
                              );
                          }
                      }
                      // Trigger any remaining accumulated transfers via call to the conduit.
                      _triggerIfArmed(accumulator);
                      // If any ether remains after fulfillments...
                      if (etherRemaining != 0) {
                          // return it to the caller.
                          _transferEth(payable(msg.sender), etherRemaining);
                      }
                  }
                  /**
                   * @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 fulfiller     The fulfiller 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 fulfiller,
                      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,
                          fulfiller,
                          spentItems,
                          receivedItems
                      );
                  }
                  /**
                   * @dev Internal pure function to convert an order to an advanced order with
                   *      numerator and denominator of 1 and empty extraData.
                   *
                   * @param order The order to convert.
                   *
                   * @return advancedOrder The new advanced order.
                   */
                  function _convertOrderToAdvanced(Order calldata order)
                      internal
                      pure
                      returns (AdvancedOrder memory advancedOrder)
                  {
                      // Convert to partial order (1/1 or full fill) and return new value.
                      advancedOrder = AdvancedOrder(
                          order.parameters,
                          1,
                          1,
                          order.signature,
                          ""
                      );
                  }
                  /**
                   * @dev Internal pure function to convert an array of orders to an array of
                   *      advanced orders with numerator and denominator of 1.
                   *
                   * @param orders The orders to convert.
                   *
                   * @return advancedOrders The new array of partial orders.
                   */
                  function _convertOrdersToAdvanced(Order[] calldata orders)
                      internal
                      pure
                      returns (AdvancedOrder[] memory advancedOrders)
                  {
                      // Read the number of orders from calldata and place on the stack.
                      uint256 totalOrders = orders.length;
                      // Allocate new empty array for each partial order in memory.
                      advancedOrders = new AdvancedOrder[](totalOrders);
                      // Skip overflow check as the index for the loop starts at zero.
                      unchecked {
                          // Iterate over the given orders.
                          for (uint256 i = 0; i < totalOrders; ++i) {
                              // Convert to partial order (1/1 or full fill) and update array.
                              advancedOrders[i] = _convertOrderToAdvanced(orders[i]);
                          }
                      }
                      // Return the array of advanced orders.
                      return advancedOrders;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              // prettier-ignore
              import {
                  AmountDerivationErrors
              } from "../interfaces/AmountDerivationErrors.sol";
              import "./ConsiderationConstants.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) {
                              mstore(0, InexactFraction_error_signature)
                              revert(0, InexactFraction_error_len)
                          }
                      }
                      // 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.7;
              /**
               * @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.13;
              import { Side, ItemType } from "./ConsiderationEnums.sol";
              // prettier-ignore
              import {
                  OfferItem,
                  ConsiderationItem,
                  ReceivedItem,
                  OrderParameters,
                  Fulfillment,
                  FulfillmentComponent,
                  Execution,
                  Order,
                  AdvancedOrder,
                  CriteriaResolver
              } from "./ConsiderationStructs.sol";
              import { OrderFulfiller } from "./OrderFulfiller.sol";
              import { FulfillmentApplier } from "./FulfillmentApplier.sol";
              import "./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[][] calldata offerFulfillments,
                      FulfillmentComponent[][] calldata considerationFulfillments,
                      bytes32 fulfillerConduitKey,
                      address recipient,
                      uint256 maximumFulfilled
                  )
                      internal
                      returns (bool[] memory availableOrders, Execution[] memory executions)
                  {
                      // Validate orders, apply amounts, & determine if they utilize conduits.
                      _validateOrdersAndPrepareToFulfill(
                          advancedOrders,
                          criteriaResolvers,
                          false, // Signifies that invalid orders should NOT revert.
                          maximumFulfilled,
                          recipient
                      );
                      // Aggregate used offer and consideration items and execute transfers.
                      (availableOrders, executions) = _executeAvailableFulfillments(
                          advancedOrders,
                          offerFulfillments,
                          considerationFulfillments,
                          fulfillerConduitKey,
                          recipient
                      );
                      // Return order fulfillment details and executions.
                      return (availableOrders, executions);
                  }
                  /**
                   * @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.
                   *
                   * @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 received items.
                   */
                  function _validateOrdersAndPrepareToFulfill(
                      AdvancedOrder[] memory advancedOrders,
                      CriteriaResolver[] memory criteriaResolvers,
                      bool revertOnInvalid,
                      uint256 maximumFulfilled,
                      address recipient
                  ) internal {
                      // Ensure this function cannot be triggered during a reentrant call.
                      _setReentrancyGuard();
                      // Read length of orders array and place on the stack.
                      uint256 totalOrders = advancedOrders.length;
                      // Track the order hash for each order being fulfilled.
                      bytes32[] memory orderHashes = new bytes32[](totalOrders);
                      // Override orderHashes length to zero after memory has been allocated.
                      assembly {
                          mstore(orderHashes, 0)
                      }
                      // Declare an error buffer indicating status of any native offer items.
                      // {00} == 0 => In a match function, no native offer items: allow.
                      // {01} == 1 => In a match function, some native offer items: allow.
                      // {10} == 2 => Not in a match function, no native offer items: allow.
                      // {11} == 3 => Not in a match function, some native offer items: THROW.
                      uint256 invalidNativeOfferItemErrorBuffer;
                      // Use assembly to set the value for the second bit of the error buffer.
                      assembly {
                          // Use the second bit of the error buffer to indicate whether the
                          // current function is not matchAdvancedOrders or matchOrders.
                          invalidNativeOfferItemErrorBuffer := shl(
                              1,
                              gt(
                                  // Take the remainder of the selector modulo a magic value.
                                  mod(
                                      shr(NumBitsAfterSelector, calldataload(0)),
                                      NonMatchSelector_MagicModulus
                                  ),
                                  // Check if remainder is higher than the greatest remainder
                                  // of the two match selectors modulo the magic value.
                                  NonMatchSelector_MagicRemainder
                              )
                          )
                      }
                      // 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 current order.
                              AdvancedOrder memory advancedOrder = 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;
                                  // Update the length of the orderHashes array.
                                  assembly {
                                      mstore(orderHashes, add(i, 1))
                                  }
                                  // Continue iterating through the remaining orders.
                                  continue;
                              }
                              // Validate it, update status, and determine fraction to fill.
                              (
                                  bytes32 orderHash,
                                  uint256 numerator,
                                  uint256 denominator
                              ) = _validateOrderAndUpdateStatus(
                                      advancedOrder,
                                      criteriaResolvers,
                                      revertOnInvalid,
                                      orderHashes
                                  );
                              // Update the length of the orderHashes array.
                              assembly {
                                  mstore(orderHashes, add(i, 1))
                              }
                              // 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.
                              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.
                              OfferItem[] memory offer = advancedOrder.parameters.offer;
                              // Read length of offer array and place on the stack.
                              uint256 totalOfferItems = offer.length;
                              // Iterate over each offer item on the order.
                              for (uint256 j = 0; j < totalOfferItems; ++j) {
                                  // Retrieve the offer item.
                                  OfferItem memory offerItem = offer[j];
                                  assembly {
                                      // If the offer item is for the native token, set the
                                      // first bit of the error buffer to true.
                                      invalidNativeOfferItemErrorBuffer := or(
                                          invalidNativeOfferItemErrorBuffer,
                                          iszero(mload(offerItem))
                                      )
                                  }
                                  // 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
                                      );
                                  }
                                  // Update end amount in memory to match the derived amount.
                                  offerItem.endAmount = endAmount;
                                  // Adjust offer amount using current time; round down.
                                  offerItem.startAmount = _locateCurrentAmount(
                                      offerItem.startAmount,
                                      offerItem.endAmount,
                                      startTime,
                                      endTime,
                                      false // round down
                                  );
                              }
                              // Retrieve array of consideration items for order in question.
                              ConsiderationItem[] memory 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
                                      );
                                  }
                                  // Update end amount in memory to match the derived amount.
                                  considerationItem.endAmount = endAmount;
                                  // Adjust consideration amount using current time; round up.
                                  considerationItem.startAmount = (
                                      _locateCurrentAmount(
                                          considerationItem.startAmount,
                                          considerationItem.endAmount,
                                          startTime,
                                          endTime,
                                          true // round up
                                      )
                                  );
                                  // Utilize assembly to manually "shift" the recipient value.
                                  assembly {
                                      // 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(
                                              add(
                                                  considerationItem,
                                                  ConsiderationItem_recipient_offset
                                              )
                                          )
                                      )
                                  }
                              }
                          }
                      }
                      // If the first bit is set, a native offer item was encountered. If the
                      // second bit is set in the error buffer, the current function is not
                      // matchOrders or matchAdvancedOrders. If the value is three, both the
                      // first and second bits were set; in that case, revert with an error.
                      if (invalidNativeOfferItemErrorBuffer == 3) {
                          revert InvalidNativeOfferItem();
                      }
                      // 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 {
                          // Iterate over each order.
                          for (uint256 i = 0; i < totalOrders; ++i) {
                              // Do not emit an event if no order hash is present.
                              if (orderHashes[i] == bytes32(0)) {
                                  continue;
                              }
                              // Retrieve parameters for the order in question.
                              OrderParameters memory orderParameters = (
                                  advancedOrders[i].parameters
                              );
                              // Emit an OrderFulfilled event.
                              _emitOrderFulfilledEvent(
                                  orderHashes[i],
                                  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 received
                   *                                  items.
                   *
                   * @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
                  )
                      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; ++i) {
                              /// Retrieve the offer fulfillment components in question.
                              FulfillmentComponent[] memory components = (
                                  offerFulfillments[i]
                              );
                              // Derive aggregated execution corresponding with fulfillment.
                              Execution memory execution = _aggregateAvailable(
                                  advancedOrders,
                                  Side.OFFER,
                                  components,
                                  fulfillerConduitKey,
                                  recipient
                              );
                              // If offerer and recipient on the execution are the same...
                              if (execution.item.recipient == execution.offerer) {
                                  // Increment total filtered executions.
                                  ++totalFilteredExecutions;
                              } else {
                                  // Otherwise, assign the execution to the executions array.
                                  executions[i - totalFilteredExecutions] = execution;
                              }
                          }
                          // Iterate over each consideration fulfillment.
                          for (uint256 i = 0; i < totalConsiderationFulfillments; ++i) {
                              /// Retrieve consideration fulfillment components in question.
                              FulfillmentComponent[] memory components = (
                                  considerationFulfillments[i]
                              );
                              // Derive aggregated execution corresponding with fulfillment.
                              Execution memory execution = _aggregateAvailable(
                                  advancedOrders,
                                  Side.CONSIDERATION,
                                  components,
                                  fulfillerConduitKey,
                                  address(0) // unused
                              );
                              // If offerer and recipient on the execution are the same...
                              if (execution.item.recipient == execution.offerer) {
                                  // Increment total filtered executions.
                                  ++totalFilteredExecutions;
                              } else {
                                  // Otherwise, assign the execution to the executions array.
                                  executions[
                                      i + totalOfferFulfillments - 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)
                                  )
                              }
                          }
                      }
                      // Revert if no orders are available.
                      if (executions.length == 0) {
                          revert NoSpecifiedOrdersAvailable();
                      }
                      // Perform final checks and return.
                      availableOrders = _performFinalChecksAndExecuteOrders(
                          advancedOrders,
                          executions
                      );
                      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.
                   *
                   * @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
                  ) 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.
                      availableOrders = new bool[](totalOrders);
                      // Skip overflow checks as all for loops are indexed starting at zero.
                      unchecked {
                          // Iterate over orders to ensure all considerations are met.
                          for (uint256 i = 0; i < totalOrders; ++i) {
                              // Retrieve the order in question.
                              AdvancedOrder memory advancedOrder = advancedOrders[i];
                              // Skip consideration item checks for order if not fulfilled.
                              if (advancedOrder.numerator == 0) {
                                  // Note: orders do not need to be marked as unavailable as a
                                  // new memory region has been allocated. Review carefully if
                                  // altering compiler version or managing memory manually.
                                  continue;
                              }
                              // Mark the order as available.
                              availableOrders[i] = true;
                              // Retrieve consideration items to ensure they are fulfilled.
                              ConsiderationItem[] memory consideration = (
                                  advancedOrder.parameters.consideration
                              );
                              // Read length of consideration array and place on the stack.
                              uint256 totalConsiderationItems = consideration.length;
                              // Iterate over each consideration item to ensure it is met.
                              for (uint256 j = 0; j < totalConsiderationItems; ++j) {
                                  // Retrieve remaining amount on the consideration item.
                                  uint256 unmetAmount = consideration[j].startAmount;
                                  // Revert if the remaining amount is not zero.
                                  if (unmetAmount != 0) {
                                      revert ConsiderationNotMet(i, j, unmetAmount);
                                  }
                              }
                          }
                      }
                      // Put ether value supplied by the caller on the stack.
                      uint256 etherRemaining = msg.value;
                      // 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);
                      // 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) {
                              // Ensure that sufficient native tokens are still available.
                              if (item.amount > etherRemaining) {
                                  revert InsufficientEtherSupplied();
                              }
                              // Skip underflow check as amount is less than ether remaining.
                              unchecked {
                                  etherRemaining -= item.amount;
                              }
                          }
                          // 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;
                          }
                      }
                      // Trigger any remaining accumulated transfers via call to the conduit.
                      _triggerIfArmed(accumulator);
                      // If any ether remains after fulfillments, return it to the caller.
                      if (etherRemaining != 0) {
                          _transferEth(payable(msg.sender), etherRemaining);
                      }
                      // Clear the reentrancy guard.
                      _clearReentrancyGuard();
                      // Return the array containing available orders.
                      return (availableOrders);
                  }
                  /**
                   * @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.
                   *
                   * @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[] calldata fulfillments
                  ) internal returns (Execution[] memory executions) {
                      // Validate orders, update order status, and determine item amounts.
                      _validateOrdersAndPrepareToFulfill(
                          advancedOrders,
                          criteriaResolvers,
                          true, // Signifies that invalid orders should revert.
                          advancedOrders.length,
                          address(0) // OrderFulfilled event has no recipient when matching.
                      );
                      // Fulfill the orders using the supplied fulfillments.
                      return _fulfillAdvancedOrders(advancedOrders, fulfillments);
                  }
                  /**
                   * @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.
                   *
                   * @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[] calldata fulfillments
                  ) 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 calldata fulfillment = fulfillments[i];
                              // Derive the execution corresponding with the fulfillment.
                              Execution memory execution = _applyFulfillment(
                                  advancedOrders,
                                  fulfillment.offerComponents,
                                  fulfillment.considerationComponents
                              );
                              // If offerer and recipient on the execution are the same...
                              if (execution.item.recipient == execution.offerer) {
                                  // 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);
                      // Return the executions array.
                      return (executions);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.13;
              // prettier-ignore
              import {
                  ConsiderationInterface
              } from "../interfaces/ConsiderationInterface.sol";
              // prettier-ignore
              import {
                  OrderComponents,
                  BasicOrderParameters,
                  OrderParameters,
                  Order,
                  AdvancedOrder,
                  OrderStatus,
                  CriteriaResolver,
                  Fulfillment,
                  FulfillmentComponent,
                  Execution
              } from "./ConsiderationStructs.sol";
              import { OrderCombiner } from "./OrderCombiner.sol";
              /**
               * @title Consideration
               * @author 0age
               * @custom:coauthor d1ll0n
               * @custom:coauthor transmissions11
               * @custom:version 1.1
               * @notice Consideration is a generalized ETH/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 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 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 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 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 (and 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
                      override
                      returns (bool fulfilled)
                  {
                      // Convert order to "advanced" order, then validate and fulfill it.
                      fulfilled = _validateAndFulfillAdvancedOrder(
                          _convertOrderToAdvanced(order),
                          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.
                   *
                   * @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.
                   * @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
                   *                            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 override returns (bool fulfilled) {
                      // Validate and fulfill the order.
                      fulfilled = _validateAndFulfillAdvancedOrder(
                          advancedOrder,
                          criteriaResolvers,
                          fulfillerConduitKey,
                          recipient == address(0) ? msg.sender : 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).
                   *
                   * @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 (and
                   *                                  direct approvals set on Consideration).
                   * @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(
                      Order[] calldata orders,
                      FulfillmentComponent[][] calldata offerFulfillments,
                      FulfillmentComponent[][] calldata considerationFulfillments,
                      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(
                              _convertOrdersToAdvanced(orders), // Convert to advanced orders.
                              new CriteriaResolver[](0), // No criteria resolvers supplied.
                              offerFulfillments,
                              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.
                   *
                   * @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` 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, 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.
                   */
                  function fulfillAvailableAdvancedOrders(
                      AdvancedOrder[] memory advancedOrders,
                      CriteriaResolver[] calldata criteriaResolvers,
                      FulfillmentComponent[][] calldata offerFulfillments,
                      FulfillmentComponent[][] calldata considerationFulfillments,
                      bytes32 fulfillerConduitKey,
                      address recipient,
                      uint256 maximumFulfilled
                  )
                      external
                      payable
                      override
                      returns (bool[] memory availableOrders, Execution[] memory executions)
                  {
                      // Fulfill all available orders.
                      return
                          _fulfillAvailableAdvancedOrders(
                              advancedOrders,
                              criteriaResolvers,
                              offerFulfillments,
                              considerationFulfillments,
                              fulfillerConduitKey,
                              recipient == address(0) ? msg.sender : 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).
                   *
                   * @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` in order to
                   *                          receive ERC1155 tokens.
                   * @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.
                   *
                   * @return executions An array of elements indicating the sequence of
                   *                    transfers performed as part of matching the given
                   *                    orders.
                   */
                  function matchOrders(
                      Order[] calldata orders,
                      Fulfillment[] calldata fulfillments
                  ) external payable override returns (Execution[] memory executions) {
                      // Convert to advanced, validate, and match orders using fulfillments.
                      return
                          _matchAdvancedOrders(
                              _convertOrdersToAdvanced(orders),
                              new CriteriaResolver[](0), // No criteria resolvers supplied.
                              fulfillments
                          );
                  }
                  /**
                   * @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.
                   *
                   * @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.
                   *
                   * @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[] calldata criteriaResolvers,
                      Fulfillment[] calldata fulfillments
                  ) external payable override returns (Execution[] memory executions) {
                      // Validate and match the advanced orders using supplied fulfillments.
                      return
                          _matchAdvancedOrders(
                              advancedOrders,
                              criteriaResolvers,
                              fulfillments
                          );
                  }
                  /**
                   * @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.
                   *
                   * @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
                      override
                      returns (bool validated)
                  {
                      // Validate the orders.
                      validated = _validate(orders);
                  }
                  /**
                   * @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.
                      newCounter = _incrementCounter();
                  }
                  /**
                   * @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
                      override
                      returns (bytes32 orderHash)
                  {
                      // Derive order hash by supplying order parameters along with counter.
                      orderHash = _deriveOrderHash(
                          OrderParameters(
                              order.offerer,
                              order.zone,
                              order.offer,
                              order.consideration,
                              order.orderType,
                              order.startTime,
                              order.endTime,
                              order.zoneHash,
                              order.salt,
                              order.conduitKey,
                              order.consideration.length
                          ),
                          order.counter
                      );
                  }
                  /**
                   * @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
                      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();
                  }
                  /**
                   * @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.
                      contractName = _name();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.7;
              // prettier-ignore
              import {
                  BasicOrderParameters,
                  OrderComponents,
                  Fulfillment,
                  FulfillmentComponent,
                  Execution,
                  Order,
                  AdvancedOrder,
                  OrderStatus,
                  CriteriaResolver
              } from "../lib/ConsiderationStructs.sol";
              /**
               * @title ConsiderationInterface
               * @author 0age
               * @custom:version 1.1
               * @notice Consideration is a generalized ETH/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.
                   */
                  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.
                   */
                  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 as 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).
                   *
                   * @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.
                   */
                  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.
                   *
                   * @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.
                   *
                   * @return executions An array of elements indicating the sequence of
                   *                    transfers performed as part of matching the given
                   *                    orders.
                   */
                  function matchAdvancedOrders(
                      AdvancedOrder[] calldata orders,
                      CriteriaResolver[] calldata criteriaResolvers,
                      Fulfillment[] calldata fulfillments
                  ) 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 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
                      );
                  /**
                   * @notice Retrieve the name of this contract.
                   *
                   * @return contractName The name of this contract.
                   */
                  function name() external view returns (string memory contractName);
              }
              

              File 2 of 6: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./TransparentUpgradeableProxy.sol";
              import "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 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");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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 () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      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 {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = 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");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              // SPDX-License-Identifier: MIT
              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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
               * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
               * continuation of the upgradability.
               *
               * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
               *
               * _Available since v4.1._
               */
              abstract contract UUPSUpgradeable is ERC1967Upgrade {
                  function upgradeTo(address newImplementation) external virtual {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallSecure(newImplementation, bytes(""), false);
                  }
                  function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallSecure(newImplementation, data, true);
                  }
                  function _authorizeUpgrade(address newImplementation) internal virtual;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
              abstract contract Proxiable is UUPSUpgradeable {
                  function _authorizeUpgrade(address newImplementation) internal override {
                      _beforeUpgrade(newImplementation);
                  }
                  function _beforeUpgrade(address newImplementation) internal virtual;
              }
              contract ChildOfProxiable is Proxiable {
                  function _beforeUpgrade(address newImplementation) internal virtual override {}
              }
              

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

              File 4 of 6: CaptainzV2
              // 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) (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.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 (last updated v4.8.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 is IERC165 {
                  /**
                   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                   */
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                   */
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes calldata data
                  ) external;
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                   * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                   * understand this adds an external call which potentially creates a reentrancy vulnerability.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts 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
              /*
                                                  $MEMELAND
                                                 kevinLAND$$$
                                              mVp           meme
                                             meME      BRIAN6 9$
                                            $sa|ya$   ISdicKbuTT
                                            !mVP$! $ca pta  inM$
                                            !j$oe$y!! TREASUREM$
                                             !MV $P!$         M$
                                             M$zpotatozmvp    M$           Cumm
                                             A$C$$staking$    M$          CHRIS
                                             R$H       MVP $d ontLAND   mvpR m$
                                             C$U       $$$ 69 T$rustverify IMvP
                                             O$N      9gag ceo   captain  aS$o
                                             !9GA     pOTATOZ$       RAY$9999$
                                               G!     !!$DICK$!         dyno
                                               poTa    BUTTZ!           m$
                                                 to                   9gag
                                                  LETSFUCKING!  GROW!!!
                                                  RAY$    ISMVP ME
                                                 DERE K!   lanD me
                                               kArEnc IS j6r9an LA
                                               tReASURE$ meMelandnD
              */
              pragma solidity ^0.8.16;
              import "./Guardian/Erc721LockRegistry.sol";
              import "./OPR/upgradeable/DefaultOperatorFiltererUpgradeable.sol";
              import "./interfaces/IPotatoz.sol";
              import "./interfaces/ICaptainz.sol";
              import "./interfaces/IMVP.sol";
              contract CaptainzV2 is ERC721x, DefaultOperatorFiltererUpgradeable, ICaptainz {
                  string public baseTokenURI;
                  string public tokenURISuffix;
                  string public tokenURIOverride;
                  uint256 public MAX_SUPPLY;
                  event QuestStarted(uint256 indexed tokenId, uint256 questStartedAt, uint256[] crews);
                  event QuestEdited(uint256 indexed tokenId, uint256 questStartedAt, uint256[] crews, uint256 questEditedAt);
                  event QuestStopped(
                      uint256 indexed tokenId,
                      uint256 questStartedAt,
                      uint256 questStoppedAt
                  );
                  event ChestRevealed(uint256 indexed tokenId);
                  IPotatoz public potatozContract;
                  uint256 public MAX_CREWS;
                  bool public canQuest;
                  mapping(uint256 => uint256) public tokensLastQuestedAt; // captainz tokenId => timestamp
                  mapping(uint256 => uint256[]) public questCrews; // captainz tokenId => potatoz tokenIds
                  mapping(uint256 => uint256[]) public potatozCrew; // potatoz tokenId => captainz tokenId [array of 1 uint256]
                  mapping(uint256 => bool) public revealed; // captains tokenId => revealed
                  IMVP public mvpContract;
                  /// @custom:oz-upgrades-unsafe-allow constructor
                  constructor() {
                      _disableInitializers();
                  }
                  function initialize(string memory baseURI) public initializer {
                      DefaultOperatorFiltererUpgradeable.__DefaultOperatorFilterer_init();
                      ERC721x.__ERC721x_init("Captainz", "Captainz");
                      baseTokenURI = baseURI;
                      MAX_SUPPLY = 9999;
                  }
                  function initializeV2() public onlyOwner reinitializer(2) {
                      MAX_CREWS = 3;
                  }
                  function safeMint(address receiver, uint256 quantity) internal {
                      require(_totalMinted() + quantity <= MAX_SUPPLY, "exceed MAX_SUPPLY");
                      _mint(receiver, quantity);
                  }
                  // =============== Airdrop ===============
                  function airdropWithAmounts(
                      address[] memory receivers,
                      uint256[] memory amounts
                  ) external onlyOwner {
                      require(receivers.length >= 1, "at least 1 receiver");
                      for (uint256 i; i < receivers.length; i++) {
                          address receiver = receivers[i];
                          safeMint(receiver, amounts[i]);
                      }
                  }
                  // =============== URI ===============
                  function compareStrings(string memory a, string memory b)
                      public
                      pure
                      returns (bool)
                  {
                      return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
                  }
                  function _baseURI() internal view virtual override returns (string memory) {
                      return baseTokenURI;
                  }
                  function tokenURI(uint256 _tokenId)
                      public
                      view
                      override(ERC721AUpgradeable, IERC721AUpgradeable)
                      returns (string memory)
                  {
                      if (bytes(tokenURIOverride).length > 0) {
                          return tokenURIOverride;
                      }
                      return string.concat(super.tokenURI(_tokenId), tokenURISuffix);
                  }
                  function setBaseURI(string calldata baseURI) external onlyOwner {
                      baseTokenURI = baseURI;
                  }
                  function setTokenURISuffix(string calldata _tokenURISuffix)
                      external
                      onlyOwner
                  {
                      if (compareStrings(_tokenURISuffix, "!empty!")) {
                          tokenURISuffix = "";
                      } else {
                          tokenURISuffix = _tokenURISuffix;
                      }
                  }
                  function setTokenURIOverride(string calldata _tokenURIOverride)
                      external
                      onlyOwner
                  {
                      if (compareStrings(_tokenURIOverride, "!empty!")) {
                          tokenURIOverride = "";
                      } else {
                          tokenURIOverride = _tokenURIOverride;
                      }
                  }
                  // =============== Stake + MARKETPLACE CONTROL ===============
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public override(ERC721x) onlyAllowedOperator(from) {
                      require(
                          tokensLastQuestedAt[tokenId] == 0,
                          "Cannot transfer questing token"
                      );
                      super.transferFrom(from, to, tokenId);
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) public override(ERC721x) onlyAllowedOperator(from) {
                      require(
                          tokensLastQuestedAt[tokenId] == 0,
                          "Cannot transfer questing token"
                      );
                      super.safeTransferFrom(from, to, tokenId, _data);
                  }
                  // =============== Questing ===============
                  struct QuestInfo {
                      uint256 tokenId;
                      uint256[] potatozTokenIds;
                  }
                  function batchStartQuest(QuestInfo[] calldata questInfos) external {
                      uint256 batch = questInfos.length;
                      for (uint256 i; i < batch;) {
                          startQuest(questInfos[i].tokenId, questInfos[i].potatozTokenIds);
                          unchecked { ++i; }
                      }
                  }
                  function batchEditQuest(QuestInfo[] calldata questInfos) external {
                      require(canQuest, "questing not open");
                      require(address(potatozContract) != address(0), "potatozContract not set");
                      uint256 batch = questInfos.length;
                      for (uint256 i; i < batch;) {
                          uint256 tokenId = questInfos[i].tokenId;
                          require(msg.sender == ownerOf(tokenId), "not owner of [captainz tokenId]");
                          require(tokensLastQuestedAt[tokenId] > 0, "quested not started for [captainz tokenId]");
                          _resetCrew(tokenId);
                          unchecked { ++i; }
                      }
                      for (uint256 i; i < batch;) {
                          uint256 tokenId = questInfos[i].tokenId;
                          uint256[] calldata potatozTokenIds = questInfos[i].potatozTokenIds;
                          require(potatozTokenIds.length <= MAX_CREWS, "too many crews [potatozTokenIds]");
                          _addCrew(tokenId, potatozTokenIds);
                          emit QuestEdited(tokenId, tokensLastQuestedAt[tokenId], potatozTokenIds, block.timestamp);
                          unchecked { ++i; }
                      }
                  }
                  function batchStopQuest(uint256[] calldata tokenIds) external {
                      uint256 batch = tokenIds.length;
                      for (uint256 i; i < batch;) {
                          stopQuest(tokenIds[i]);
                          unchecked { ++i; }
                      }
                  }
                  function startQuest(uint256 tokenId, uint256[] calldata potatozTokenIds) public {
                      require(canQuest, "questing not open");
                      require(address(potatozContract) != address(0), "potatozContract not set");
                      require(msg.sender == ownerOf(tokenId), "not owner of [captainz tokenId]");
                      require(tokensLastQuestedAt[tokenId] == 0, "quested already started for [captainz tokenId]");
                      require(potatozTokenIds.length <= MAX_CREWS, "too many crews [potatozTokenIds]");
                      _addCrew(tokenId, potatozTokenIds);
                      tokensLastQuestedAt[tokenId] = block.timestamp;
                      emit QuestStarted(tokenId, block.timestamp, potatozTokenIds);
                      if (!revealed[tokenId]) {
                          revealed[tokenId] = true;
                          emit ChestRevealed(tokenId);
                      }
                  }
                  function editQuest(uint256 tokenId, uint256[] calldata potatozTokenIds) public {
                      require(canQuest, "questing not open");
                      require(address(potatozContract) != address(0), "potatozContract not set");
                      require(msg.sender == ownerOf(tokenId), "not owner of [captainz tokenId]");
                      require(tokensLastQuestedAt[tokenId] > 0, "quested not started for [captainz tokenId]");
                      require(potatozTokenIds.length <= MAX_CREWS, "too many crews [potatozTokenIds]");
                      _resetCrew(tokenId);
                      _addCrew(tokenId, potatozTokenIds);
                      emit QuestEdited(tokenId, tokensLastQuestedAt[tokenId], potatozTokenIds, block.timestamp);
                  }
                  function _addCrew(uint256 tokenId, uint256[] calldata potatozTokenIds) private {
                      uint256 crews = potatozTokenIds.length;
                      if (crews >= 1) {
                          uint256[] memory wrapper = new uint256[](1);
                          wrapper[0] = tokenId;
                          for (uint256 i; i < crews;) {
                              uint256 pTokenId = potatozTokenIds[i];
                              require(potatozContract.nftOwnerOf(pTokenId) == msg.sender, "not owner of [potatoz tokenId]");
                              if (!potatozContract.isPotatozStaking(pTokenId)) {
                                  potatozContract.stakeExternal(pTokenId);
                              }
                              uint256[] storage existCheck = potatozCrew[pTokenId];
                              if (existCheck.length != 0) {
                                  removeCrew(pTokenId);
                              }
                              potatozCrew[pTokenId] = wrapper;
                              unchecked { ++i; }
                          }
                          questCrews[tokenId] = potatozTokenIds;
                      }
                  }
                  function removeCrew(uint256 potatozTokenId) public {
                      require(address(potatozContract) != address(0), "potatozContract not set");
                      require(
                          msg.sender == potatozContract.nftOwnerOf(potatozTokenId) || msg.sender == address(potatozContract),
                          "caller must be any: potatoz owner, potatoz"
                      );
                      uint256[] storage existCheck = potatozCrew[potatozTokenId];
                      require(existCheck.length != 0, "potatozTokenId not questing");
                      uint256 tokenId = existCheck[0];
                      uint256 empty = MAX_SUPPLY;
                      uint256[] memory pTokenIds = questCrews[tokenId];
                      uint256 crews = pTokenIds.length;
                      uint256 crewLength = pTokenIds.length;
                      for (uint256 i; i < crews;) {
                          uint256 pTokenId = pTokenIds[i];
                          if (pTokenId == potatozTokenId) {
                              pTokenIds[i] = empty;
                              crewLength--;
                          }
                          unchecked { ++i; }
                      }
                      require(pTokenIds.length != crewLength, "potatozTokenId not in crew");
                      uint256[] memory newCrews = new uint256[](crewLength);
                      uint256 activeIdx;
                      for (uint256 i; i < crews;) {
                          if (pTokenIds[i] != empty) {
                              newCrews[activeIdx++] = pTokenIds[i];
                          }
                          unchecked { ++i; }
                      }
                      questCrews[tokenId] = newCrews;
                      potatozCrew[potatozTokenId] = new uint256[](0);
                  }
                  function _resetCrew(uint256 tokenId) private {
                      uint256[] storage potatozTokenIds = questCrews[tokenId];
                      uint256 crews = potatozTokenIds.length;
                      if (crews >= 1) {
                          uint256[] memory empty = new uint256[](0);
                          for (uint256 i; i < crews;) {
                              uint256 pTokenId = potatozTokenIds[i];
                              potatozCrew[pTokenId] = empty;
                              unchecked { ++i; }
                          }
                          questCrews[tokenId] = empty;
                      }
                  }
                  function stopQuest(uint256 tokenId) public {
                      require(msg.sender == ownerOf(tokenId) || msg.sender == owner(), "not owner of [captainz tokenId]");
                      require(tokensLastQuestedAt[tokenId] > 0, "quested not started for [captainz tokenId]");
                      if (address(mvpContract) != address(0) && mvpContract.isCaptainzBoosting(tokenId)) {
                          mvpContract.removeCaptainz(tokenId);
                      }
                      _resetCrew(tokenId);
                      uint256 tlqa = tokensLastQuestedAt[tokenId];
                      tokensLastQuestedAt[tokenId] = 0;
                      emit QuestStopped(tokenId, tlqa, block.timestamp);
                  }
                  function isPotatozQuesting(uint256 tokenId) external view returns (bool) {
                      uint256[] storage existCheck = potatozCrew[tokenId];
                      return existCheck.length > 0;
                  }
                  function getTokenInfo(uint256 tokenId) external view returns (uint256 lastQuestedAt, uint256[] memory crewTokenIds, bool hasRevealed) {
                      return (tokensLastQuestedAt[tokenId], questCrews[tokenId], revealed[tokenId]);
                  }
                  function getActiveCrews(uint256 tokenId) external view returns (uint256[] memory) {
                      require(address(potatozContract) != address(0), "potatozContract not set");
                      address owner = ownerOf(tokenId);
                      uint256[] memory pTokenIds = questCrews[tokenId];
                      uint256 crews = pTokenIds.length;
                      uint256 activeLength = pTokenIds.length;
                      uint256 empty = MAX_SUPPLY;
                      for (uint256 i; i < crews;) {
                          uint256 pTokenId = pTokenIds[i];
                          if (potatozContract.nftOwnerOf(pTokenId) != owner || !potatozContract.isPotatozStaking(pTokenId)) {
                              pTokenIds[i] = empty;
                              activeLength--;
                          }
                          unchecked { ++i; }
                      }
                      uint256[] memory activeCrews = new uint256[](activeLength);
                      uint256 activeIdx;
                      for (uint256 i; i < crews;) {
                          if (pTokenIds[i] != empty) {
                              activeCrews[activeIdx++] = pTokenIds[i];
                          }
                          unchecked { ++i; }
                      }
                      return activeCrews;
                  }
                  // =============== Admin ===============
                  function setCanQuest(bool b) external onlyOwner {
                      canQuest = b;
                  }
                  function setPotatozContract(address addr) external onlyOwner {
                      potatozContract = IPotatoz(addr);
                  }
                  function setMvpContract(address addr) external onlyOwner {
                      mvpContract = IMVP(addr);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.14;
              import "erc721a-upgradeable/contracts/extensions/ERC721AQueryableUpgradeable.sol";
              contract ERC721ANamable is ERC721AQueryableUpgradeable {
                  mapping(uint256 => string) public bio;
                  // Mapping from token ID to name
                  mapping(uint256 => string) private _tokenName;
                  // Mapping if certain name string has already been reserved
                  mapping(string => bool) private _nameReserved;
                  event NameChange(uint256 indexed tokenId, string newName);
                  event BioChange(uint256 indexed tokenId, string bio);
                  function __ERC721ANamable_init(string memory _name, string memory _symbol)
                      internal
                      initializerERC721A
                  {
                      ERC721AUpgradeable.__ERC721A_init(_name, _symbol);
                  }
                  function changeBio(uint256 _tokenId, string memory _bio) public virtual {
                      address owner = ownerOf(_tokenId);
                      require(msg.sender == owner, "ERC721: caller is not the owner");
                      bio[_tokenId] = _bio;
                      emit BioChange(_tokenId, _bio);
                  }
                  function changeName(uint256 tokenId, string memory newName) public virtual {
                      address owner = ownerOf(tokenId);
                      require(msg.sender == owner, "ERC721: caller is not the owner");
                      require(validateName(newName) == true, "Not a valid new name");
                      require(
                          sha256(bytes(newName)) != sha256(bytes(_tokenName[tokenId])),
                          "New name is same as the current one"
                      );
                      require(isNameReserved(newName) == false, "Name already reserved");
                      // If already named, dereserve old name
                      if (bytes(_tokenName[tokenId]).length > 0) {
                          toggleReserveName(_tokenName[tokenId], false);
                      }
                      toggleReserveName(newName, true);
                      _tokenName[tokenId] = newName;
                      emit NameChange(tokenId, newName);
                  }
                  /**
                   * @dev Reserves the name if isReserve is set to true, de-reserves if set to false
                   */
                  function toggleReserveName(string memory str, bool isReserve) internal {
                      _nameReserved[toLower(str)] = isReserve;
                  }
                  /**
                   * @dev Returns name of the NFT at index.
                   */
                  function tokenNameByIndex(uint256 index)
                      public
                      view
                      returns (string memory)
                  {
                      return _tokenName[index];
                  }
                  /**
                   * @dev Returns if the name has been reserved.
                   */
                  function isNameReserved(string memory nameString)
                      public
                      view
                      returns (bool)
                  {
                      return _nameReserved[toLower(nameString)];
                  }
                  function validateName(string memory str) public pure returns (bool) {
                      bytes memory b = bytes(str);
                      if (b.length < 1) return false;
                      if (b.length > 25) return false; // Cannot be longer than 25 characters
                      if (b[0] == 0x20) return false; // Leading space
                      if (b[b.length - 1] == 0x20) return false; // Trailing space
                      bytes1 lastChar = b[0];
                      for (uint256 i; i < b.length; i++) {
                          bytes1 char = b[i];
                          if (char == 0x20 && lastChar == 0x20) return false; // Cannot contain continous spaces
                          if (
                              !(char >= 0x30 && char <= 0x39) && //9-0
                              !(char >= 0x41 && char <= 0x5A) && //A-Z
                              !(char >= 0x61 && char <= 0x7A) && //a-z
                              !(char == 0x20) //space
                          ) return false;
                          lastChar = char;
                      }
                      return true;
                  }
                  /**
                   * @dev Converts the string to lowercase
                   */
                  function toLower(string memory str) public pure returns (string memory) {
                      bytes memory bStr = bytes(str);
                      bytes memory bLower = new bytes(bStr.length);
                      for (uint256 i = 0; i < bStr.length; i++) {
                          // Uppercase character
                          if ((uint8(bStr[i]) >= 65) && (uint8(bStr[i]) <= 90)) {
                              bLower[i] = bytes1(uint8(bStr[i]) + 32);
                          } else {
                              bLower[i] = bStr[i];
                          }
                      }
                      return string(bLower);
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.14;
              /*
               *     ,_,
               *    (',')
               *    {/"\\\\}
               *    -"-"-
               */
              import "./ERC721ANamable.sol";
              import "./LockRegistry.sol";
              import "./IERC721x.sol";
              contract ERC721x is ERC721ANamable, LockRegistry {
                  /*
                   *     bytes4(keccak256('freeId(uint256,address)')) == 0x94d216d6
                   *     bytes4(keccak256('isUnlocked(uint256)')) == 0x72abc8b7
                   *     bytes4(keccak256('lockCount(uint256)')) == 0x650b00f6
                   *     bytes4(keccak256('lockId(uint256)')) == 0x2799cde0
                   *     bytes4(keccak256('lockMap(uint256,uint256)')) == 0x2cba8123
                   *     bytes4(keccak256('lockMapIndex(uint256,address)')) == 0x09308e5d
                   *     bytes4(keccak256('unlockId(uint256)')) == 0x40a9c8df
                   *     bytes4(keccak256('approvedContract(address)')) == 0xb1a6505f
                   *
                   *     => 0x94d216d6 ^ 0x72abc8b7 ^ 0x650b00f6 ^ 0x2799cde0 ^
                   *        0x2cba8123 ^ 0x09308e5d ^ 0x40a9c8df ^ 0xb1a6505f == 0x706e8489
                   */
                  bytes4 private constant _INTERFACE_ID_ERC721x = 0x706e8489;
                  function __ERC721x_init(string memory _name, string memory _symbol)
                      internal
                      onlyInitializing
                  {
                      ERC721ANamable.__ERC721ANamable_init(_name, _symbol);
                      LockRegistry.__LockRegistry_init();
                  }
                  function supportsInterface(bytes4 _interfaceId)
                      public
                      view
                      virtual
                      override(ERC721AUpgradeable, IERC721AUpgradeable)
                      returns (bool)
                  {
                      return
                          _interfaceId == _INTERFACE_ID_ERC721x ||
                          super.supportsInterface(_interfaceId);
                  }
                  function transferFrom(
                      address _from,
                      address _to,
                      uint256 _tokenId
                  ) public virtual override(ERC721AUpgradeable, IERC721AUpgradeable) {
                      require(isUnlocked(_tokenId), "Token is locked");
                      ERC721AUpgradeable.transferFrom(_from, _to, _tokenId);
                  }
                  function safeTransferFrom(
                      address _from,
                      address _to,
                      uint256 _tokenId,
                      bytes memory _data
                  ) public virtual override(ERC721AUpgradeable, IERC721AUpgradeable) {
                      require(isUnlocked(_tokenId), "Token is locked");
                      ERC721AUpgradeable.safeTransferFrom(
                          _from,
                          _to,
                          _tokenId,
                          _data
                      );
                  }
                  function lockId(uint256 _id) external virtual override {
                      require(_exists(_id), "Token !exist");
                      _lockId(_id);
                  }
                  function unlockId(uint256 _id) external virtual override {
                      require(_exists(_id), "Token !exist");
                      _unlockId(_id);
                  }
                  function freeId(uint256 _id, address _contract) external virtual override {
                      require(_exists(_id), "Token !exist");
                      _freeId(_id, _contract);
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.14;
              interface IERC721x {
                  /**
                   * @dev Returns if the token is locked (non-transferrable) or not.
                   */
                  function isUnlocked(uint256 _id) external view returns (bool);
                  /**
                   * @dev Returns the amount of locks on the token.
                   */
                  function lockCount(uint256 _tokenId) external view returns (uint256);
                  /**
                   * @dev Returns if a contract is allowed to lock/unlock tokens.
                   */
                  function approvedContract(address _contract) external view returns (bool);
                  /**
                   * @dev Returns the contract that locked a token at a specific index in the mapping.
                   */
                  function lockMap(uint256 _tokenId, uint256 _index)
                      external
                      view
                      returns (address);
                  /**
                   * @dev Returns the mapping index of a contract that locked a token.
                   */
                  function lockMapIndex(uint256 _tokenId, address _contract)
                      external
                      view
                      returns (uint256);
                  /**
                   * @dev Locks a token, preventing it from being transferrable
                   */
                  function lockId(uint256 _id) external;
                  /**
                   * @dev Unlocks a token.
                   */
                  function unlockId(uint256 _id) external;
                  /**
                   * @dev Unlocks a token from a given contract if the contract is no longer approved.
                   */
                  function freeId(uint256 _id, address _contract) external;
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.14;
              /*
               *     ,_,
               *    (',')
               *    {/"\\\\}
               *    -"-"-
               */
              import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
              // import "@openzeppelin/contracts/access/Ownable.sol";
              import "./IERC721x.sol";
              abstract contract LockRegistry is OwnableUpgradeable, IERC721x {
                  mapping(address => bool) public override approvedContract;
                  mapping(uint256 => uint256) public override lockCount;
                  mapping(uint256 => mapping(uint256 => address)) public override lockMap;
                  mapping(uint256 => mapping(address => uint256))
                      public
                      override lockMapIndex;
                  event TokenLocked(
                      uint256 indexed tokenId,
                      address indexed approvedContract
                  );
                  event TokenUnlocked(
                      uint256 indexed tokenId,
                      address indexed approvedContract
                  );
                  function __LockRegistry_init() internal onlyInitializing {
                      OwnableUpgradeable.__Ownable_init();
                  }
                  function isUnlocked(uint256 _id) public view override returns (bool) {
                      return lockCount[_id] == 0;
                  }
                  function updateApprovedContracts(
                      address[] calldata _contracts,
                      bool[] calldata _values
                  ) external onlyOwner {
                      require(_contracts.length == _values.length, "!length");
                      for (uint256 i = 0; i < _contracts.length; i++)
                          approvedContract[_contracts[i]] = _values[i];
                  }
                  function _lockId(uint256 _id) internal {
                      require(approvedContract[msg.sender], "Cannot update map");
                      require(
                          lockMapIndex[_id][msg.sender] == 0,
                          "ID already locked by caller"
                      );
                      uint256 count = lockCount[_id] + 1;
                      lockMap[_id][count] = msg.sender;
                      lockMapIndex[_id][msg.sender] = count;
                      lockCount[_id]++;
                      emit TokenLocked(_id, msg.sender);
                  }
                  function _unlockId(uint256 _id) internal {
                      require(approvedContract[msg.sender], "Cannot update map");
                      uint256 index = lockMapIndex[_id][msg.sender];
                      require(index != 0, "ID not locked by caller");
                      uint256 last = lockCount[_id];
                      if (index != last) {
                          address lastContract = lockMap[_id][last];
                          lockMap[_id][index] = lastContract;
                          lockMap[_id][last] = address(0);
                          lockMapIndex[_id][lastContract] = index;
                      } else lockMap[_id][index] = address(0);
                      lockMapIndex[_id][msg.sender] = 0;
                      lockCount[_id]--;
                      emit TokenUnlocked(_id, msg.sender);
                  }
                  function _freeId(uint256 _id, address _contract) internal {
                      require(!approvedContract[_contract], "Cannot update map");
                      uint256 index = lockMapIndex[_id][_contract];
                      require(index != 0, "ID not locked");
                      uint256 last = lockCount[_id];
                      if (index != last) {
                          address lastContract = lockMap[_id][last];
                          lockMap[_id][index] = lastContract;
                          lockMap[_id][last] = address(0);
                          lockMapIndex[_id][lastContract] = index;
                      } else lockMap[_id][index] = address(0);
                      lockMapIndex[_id][_contract] = 0;
                      lockCount[_id]--;
                      emit TokenUnlocked(_id, _contract);
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.16;
              import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
              interface ICaptainz {
                  function isPotatozQuesting(uint256 tokenId) external view returns (bool);
                  function removeCrew(uint256 potatozTokenId) external;
              }// SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.16;
              import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
              interface IMVP {
                  function isCaptainzBoosting(uint256 tokenId) external view returns (bool);
                  function removeCaptainz(uint256 captainzTokenId) external;
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.16;
              import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
              interface IPotatoz {
                  function isPotatozStaking(uint256 tokenId) external view returns (bool);
                  function stakeExternal(uint256 tokenId) external;
                  function nftOwnerOf(uint256 tokenId) external view returns (address);
              }// 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 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 {OperatorFiltererUpgradeable} from "./OperatorFiltererUpgradeable.sol";
              abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                  address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                  function __DefaultOperatorFilterer_init() public onlyInitializing {
                      OperatorFiltererUpgradeable.__OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                  }
              }
              // 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) public 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)
                                      && operatorFilterRegistry.isOperatorAllowed(address(this), from)
                              )
                          ) {
                              revert OperatorNotAllowed(msg.sender);
                          }
                      }
                      _;
                  }
              }
              // 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 {
                  // Reference type for token approval.
                  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.2
              // 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) {
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < ERC721AStorage.layout()._currentIndex) {
                                  uint256 packed = ERC721AStorage.layout()._packedOwnerships[curr];
                                  // If not burned.
                                  if (packed & _BITMASK_BURNED == 0) {
                                      // Invariant:
                                      // There will always be an initialized ownership slot
                                      // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                                      // before an unintialized ownership slot
                                      // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                                      // Hence, `curr` will not underflow.
                                      //
                                      // We can directly compare the packed value.
                                      // If the address is zero, packed will be zero.
                                      while (packed == 0) {
                                          packed = ERC721AStorage.layout()._packedOwnerships[--curr];
                                      }
                                      return packed;
                                  }
                              }
                      }
                      revert OwnerQueryForNonexistentToken();
                  }
                  /**
                   * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
                   */
                  function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
                      ownership.addr = address(uint160(packed));
                      ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
                      ownership.burned = packed & _BITMASK_BURNED != 0;
                      ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
                  }
                  /**
                   * @dev Packs ownership data into a single uint256.
                   */
                  function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
                      assembly {
                          // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          owner := and(owner, _BITMASK_ADDRESS)
                          // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                          result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
                      }
                  }
                  /**
                   * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
                   */
                  function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
                      // For branchless setting of the `nextInitialized` flag.
                      assembly {
                          // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                          result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
                      }
                  }
                  // =============================================================
                  //                      APPROVAL OPERATIONS
                  // =============================================================
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the
                   * zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) public virtual override {
                      address owner = ownerOf(tokenId);
                      if (_msgSenderERC721A() != owner)
                          if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                      ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
                      emit Approval(owner, to, tokenId);
                  }
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) public view virtual override returns (address) {
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return 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 {
                      if (operator == _msgSenderERC721A()) revert ApproveToCaller();
                      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 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 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 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`.
                              )
                              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, '');
                  }
                  // =============================================================
                  //                        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 0x80 bytes to keep the free memory pointer 32-byte word aligned.
                          // We will need 1 32-byte word to store the length,
                          // and 3 32-byte words to store a maximum of 78 digits. Total: 0x20 + 3 * 0x20 = 0x80.
                          str := add(mload(0x40), 0x80)
                          // Update the free memory pointer to allocate.
                          mstore(0x40, str)
                          // 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.2
              // 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.2
              // 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.2
              // 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();
                  /**
                   * The caller cannot approve to their own address.
                   */
                  error ApproveToCaller();
                  /**
                   * 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;
                  /**
                   * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @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;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the
                   * zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom}
                   * for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
                  // =============================================================
                  //                        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);
              }
              

              File 5 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);
              }
              

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