ETH Price: $2,610.59 (+0.74%)

Transaction Decoder

Block:
15334964 at Aug-13-2022 06:17:10 PM +UTC
Transaction Fee:
0.002311835889591158 ETH $6.04
Gas Used:
166,273 Gas / 13.903856246 Gwei

Emitted Events:

64 Seaport.OrderFulfilled( orderHash=890E6513C09651EDB6AF182A7DC0DF4EFE7C080C234B8470317A95BFCE9CE3B1, offerer=0x49b9c6ab4a48a7a021a2760bd0ed45b1a0365b45, zone=PausableZone, recipient=[Sender] 0xbb720b9147733ef5a6867a998fced23593daa5eb, offer=, consideration= )
65 GnomePals.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000049b9c6ab4a48a7a021a2760bd0ed45b1a0365b45, 0x000000000000000000000000bb720b9147733ef5a6867a998fced23593daa5eb, 0x00000000000000000000000000000000000000000000000000000000000001cf )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...169EdE581
(Seaport 1.1)
(Hiveon Pool)
17,081.104588040471524237 Eth17,081.105003722971524237 Eth0.0004156825
0x49B9c6Ab...1A0365b45 0.003970686053730331 Eth0.009226598453730331 Eth0.0052559124
0x5dB11d73...81De6d4dA
0x8De9C5A0...7BCC5BC90
(OpenSea: Fees)
420.679160857497107673 Eth420.679310854997107673 Eth0.0001499975
0xB9C12140...55ada256A 0.20841136826123349 Eth0.20900535836123349 Eth0.0005939901
0xbb720B91...593daA5eb
0.030412206281106382 Eth
Nonce: 135
0.022100470391515224 Eth
Nonce: 136
0.008311735889591158

Execution Trace

ETH 0.0059999 Seaport.fulfillBasicOrder( parameters=[{name:considerationToken, type:address, order:1, indexed:false, value:0x0000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000}, {name:considerationIdentifier, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:considerationAmount, type:uint256, order:3, indexed:false, value:5255912400000000, valueString:5255912400000000}, {name:offerer, type:address, order:4, indexed:false, value:0x49B9c6Ab4a48A7A021A2760Bd0eD45b1A0365b45, valueString:0x49B9c6Ab4a48A7A021A2760Bd0eD45b1A0365b45}, {name:zone, type:address, order:5, indexed:false, value:0x004C00500000aD104D7DBd00e3ae0A5C00560C00, valueString:0x004C00500000aD104D7DBd00e3ae0A5C00560C00}, {name:offerToken, type:address, order:6, indexed:false, value:0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA, valueString:0x5dB11d7356aa4C0E85Aa5b255eC2B5F81De6d4dA}, {name:offerIdentifier, type:uint256, order:7, indexed:false, value:463, valueString:463}, {name:offerAmount, type:uint256, order:8, indexed:false, value:1, valueString:1}, {name:basicOrderType, type:uint8, order:9, indexed:false, value:2, valueString:2}, {name:startTime, type:uint256, order:10, indexed:false, value:1660414584, valueString:1660414584}, {name:endTime, type:uint256, order:11, indexed:false, value:1660500984, valueString:1660500984}, {name:zoneHash, type:bytes32, order:12, indexed:false, value:0000000000000000000000000000000000000000000000000000000000000000, valueString:0000000000000000000000000000000000000000000000000000000000000000}, {name:salt, type:uint256, order:13, indexed:false, value:25928340244477814, valueString:25928340244477814}, {name:offererConduitKey, type:bytes32, order:14, indexed:false, value:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000, valueString:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000}, {name:fulfillerConduitKey, type:bytes32, order:15, indexed:false, value:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000, valueString:0000007B02230091A7ED01230072F7006A004D60A8D4E71D599B8104250F0000}, {name:totalOriginalAdditionalRecipients, type:uint256, order:16, indexed:false, value:2, valueString:2}, {name:additionalRecipients, type:tuple[], order:17, indexed:false}, {name:signature, type:bytes, order:18, indexed:false, value:0xD2A75C3576CF36D8CB7F72DF526B961A88EF33222604391B120EB00E20B79ACB44C376060118E8BB7BD41D0948C132420FBFBB9C72315A212A3053847CF20CFD1C, valueString:0xD2A75C3576CF36D8CB7F72DF526B961A88EF33222604391B120EB00E20B79ACB44C376060118E8BB7BD41D0948C132420FBFBB9C72315A212A3053847CF20CFD1C}] ) => ( fulfilled=True )
  • PausableZone.isValidOrder( orderHash=890E6513C09651EDB6AF182A7DC0DF4EFE7C080C234B8470317A95BFCE9CE3B1, caller=0xbb720B9147733ef5A6867A998fcEd23593daA5eb, offerer=0x49B9c6Ab4a48A7A021A2760Bd0eD45b1A0365b45, zoneHash=0000000000000000000000000000000000000000000000000000000000000000 )
  • Null: 0x000...001.0e6a62e7( )
  • Conduit.execute( transfers= ) => ( transfers= )
    • GnomePals.23b872dd( )
      • ERC721ARandomizedCollectionV2.transferFrom( from=0x49B9c6Ab4a48A7A021A2760Bd0eD45b1A0365b45, to=0xbb720B9147733ef5A6867A998fcEd23593daA5eb, tokenId=463 )
      • ETH 0.0001499975 OpenSea: Fees.CALL( )
      • ETH 0.0005939901 0xb9c121402f4e89619daf7103369793055ada256a.CALL( )
      • ETH 0.0052559124 0x49b9c6ab4a48a7a021a2760bd0ed45b1a0365b45.CALL( )
        fulfillBasicOrder[Consideration (ln:9074)]
        File 1 of 5: 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 5: GnomePals
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.9;
        
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //                                                                                                                                                              //
        //                                                                                                                                                              //
        //    dkkkxkkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxxxxxxxxxkkkkkkkxkxxkx    //
        //    xOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkkOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkdldkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd;. .:oxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkko,.....  .lxkOkkOkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOko,. .',,'..  ,okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkxl'. .',,,,,,,'. .:xkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxc.  .',,',,,,,',,.. .lkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOxc. ..',,,,,,,,,,,,,,'.  ,okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd:. ..,,,,,,,,,,,,,,,,,,,'. .:xkkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkko;. ..,,,,,,,,,,,,,,,,,,,,,,,.. .lkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd,. ..,,,,,,,,,,,,,,,,,,,,,,,,,,,.  ;dkOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkkkkkkkx:. .',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'. .:xkkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkko' ..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.. .lkkkkkOkkkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkkko. .',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'. .,dkkkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkOkl. .',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'. .:xkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkOx:. .,,',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.. 'lkOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkd, ..,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'. .,dkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkko. .',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'. .:xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkc. .',,,,,,,,,,,,,,,,,,,,,,,,,,'''''',,,,,,,,,,,,,,,,,,,.. 'lkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx;  .,,,,,,,,,''''.................................',,,,,,'.. .;oxkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkko' .',,'.........      ..............................'.....      ..,:dkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOOkc.  ..''............'''',,,,,,,,,,,,,,,,,,,,,,,,,,,,','.............. .:xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxl,..   ...'''',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'.  ckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd;.  ...'''',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',,'. ,kkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx:. ..',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,''''''',,,,',,,,,,,,,,,,,,. .ckOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkOkkk:  .',,,,,,'',,,,,,,,,,,,,''''..................... ...   .............''''.. .ckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkOkkOkOd. .,,,,,,,,,,,,,'''....... ......'',;;:ccclllloooooodddddooooolccc;.       .':dkxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkOkkk;  ..............  ...,;:cldkOO0KKXXXXNNNNNXXNNNNNNX0Oxolcc::cccodx, .,;,. .,'....,;lxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkko;.                  ...',;cokKXXXXXXXXXXXXXXXXXNNKx;..               .,cc;. .;oool:...:xOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkxl'..;col. .,. ,,               .lKNXXXXXXXXXXXXXXXNXKxc;,''.       .,:;. ':::, .oXNNNX0o. 'okOkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkOko' 'o0XNNd. ,, .x0docc,.   .':clox0XXXXXXXXXXXXXXXNNXXXNXXXXx,..,.  .:0N0, .::::. 'ONXXXXN0:..lkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkOo. ;0NXXXK: .;. 'ONXN0l'..   .:OXXXXXXXXXXXXXXXXXXXXXXXXXXXNO' .oO;    :KX: .;::c;. oXXXXXXXK: 'xOkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkOx, ,0NXXXN0; .:. ,0NNK: .lx'    ,0NXXXXXXXXXXXXXXXXXXXXXXXXXNx.   .cd;  ,ONo .;::::. ;KXXXXXXNo .oOkkkOkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkOo. lXXXXXX0, .:' 'ONNO'  .,co;. .kNXXXXXXXXXXXXXXXXXXXXXXXXXX0:   .lOl..oXNx. ,c:::' 'ONXXXXXK: 'dOkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkOd. cXXXXXN0; .:, .xNXKl.  .o0o..oKXNXXNXXXXXXXXXXXXXXXXXXNXXXXKd;....,:kXXNO' '::::, .kNXXXXKl..lkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkOk: .kNXXXNK: .:;. lXXNKx:'..',ckXXXNXXXXXXXXXXXXXXXXXXXXNXXXXXXNXKOk0KXNXXN0, .:::c, .kNXXKx;..lkOkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkOx;..o0XXXXo .;:. 'OXXXXXK0O0KXXXXXXXXXXXXXXXXXXXXXXXXXXNNXXXXXXXXXXXXXXXXXK: .::::' .kKkl,..:dkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkxl'..cdkKk' 'c,  cKXXXXXXXXXXXXXXXXXXXXNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXK: .;:::. .''..,cdkOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkOkl;...''. .;:. .dXXXXXXXXXXXXXXXXXXXXXXNNXXXXXXXXNNNXXXXXXXXXXXXXXXXXXNO, .:::;. .;coxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkdlc:,. 'c;. ,ONXXXXXXXXXXXXXXXXNNKkdl:,'''',;:ldk0XXNXXXXXXXXXXXXX0;..;c:c, .lOOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOk: .,:'  ;0XXXXNNXNNXNNXXKko:'. ........... ..,:lodxkOO00KXXKd. .;::::. 'xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOx, .;:'  ,OXXklllllolc:,..  .',;:c:::c::::;,'............ox;..':c:::' .lOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkkkOd' .::'. .dKo.   .......',;::::c::,,',;;::::c::::;,'.  .....;:c:::,. :kkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOko. .;:,.  ,dd;. .';:cc:::c:::;,..      .,;:cc::c:'.    .';:c::::;. ,xOkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd' .;::,.  ,c:'.  .,:::c::,.. .:oxxoc:. ...',,'.    .,;::::c::,. ,xOkkOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx;  ':::;..  ..    .'......;d0XOccONX0xl:,..     .;:c:::::::'  ;xOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkc. .;:cc:;'....     .:lx0XXXXl  :0KXXNXO:.  .';:::::::::;. .cxkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd;...,:::c::::,'.. .;lx0XXXNk;.oKXX0d;. ..,;:c:::::cc;'. ,okOkkkkOkkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkOkd;. .':::::::::;,... .';coxxddol;.  .';::::c::::::'...ckkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd,.  ..,:::::::c::;,'... ...   ..';:::::::::::;..  .;oxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxc'..;odc'...,;:::::::::cc:;;,,,;;::cc:::c::::,'. .:ol:'..;lxkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkko;.  .lKWWWNOl,...,;:::c::::c::::cc:::::::::;'....:xKWWWW0:   .:dkkOOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOkkOkkOkd;. ...  'l0NWWWNOo,...,;:c:::c::::cc::::::,....:okXWWWWWNk;  ... .,okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd;.  ...'..  .;xXWWWWXOo,...;:::c::::cc::c:'..'ckXWWWWWWWNk;. ...''.. .;okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkxc..;,  .'''....  'lONWWWWNOc...,::::::::c:'..,dXWWWWWWWWNO:. ...''..'... .:xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkd,.'xNK;  .'...'...  .;xXWWWWNKd,..';:::::;. .dXWWWWWWWWW0c. ...''.''..'..   .lkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkl..:0WWWk. ..'..''.'...  .lONWWWWXk:...,;,.. :0WWWWWWWXOd:. ...''.'''....  'dl..:kOkkOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkOOkkkkOx:..dNWWWWNc  .'.'''..'''..  .;xKWWWWN0o'.   .oNWWWN0xo:'.   ..''''..''...  :KWNk' ,xOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkOkkkkOx, 'kNWWWWWWO. ...'..''''''..    'cx00XNWXd'..c0KOdc,. .....  .'..'''..'.. .dXWWWW0;.'okOkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkkkd, ,0WWWWWWWWWl  .....''.'.'.   ..   ..',:;'....... ....'',,.  .'''..'''.. .xWWWWWWWXc..okkkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkkkOd, ;0WWWWWWWWWWk. ..'''''''''..  ','''....  ..'.'.. .',,,,,,,.  .'''..''.. .kWWWWWWWWWXl..okkkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkOkkx, ;KWWWWWWWWWWWK, ..'''''''..'.  .,,,,,,,,. .',,,,.  .,,,,,,,.  .'''..'.. .xWWWWWWWWWWMXc..okkkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkOx; ,0WWWWWWWWWWWWNc  .'''''''''..  .',,,,,,'.  .,,,,.  .'',',,,.  .'''.''.  cNWWWWWWWWWWWWX: 'dOkkkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkkk: .kWWWWWWWWWNOdKWo  .''.'..''''.  .''.....     ....      ......  ..'..'.. .k0okNWWWWWWWWWW0, ;kkkkkOkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkko..oNWWWWWWWWWx..kWd. .'.....''.'..       ....   .'.    ......     ........ '0k..kWWWWWWWWWWWk..ckOkkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkOx, ;XWWWWWWWWWK, lNWx. .'.....'''''.........''..      .....''...........''.. ,KNl :XWWWWWWWWWWNl .dOkkkkOkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkkkkkkkkkkkkkkkkkkkl..xWWWWWWWWWWd.'0WWx. .'.....'''''''''..''''...  ....'.......''.''.''..''.. ,KMk..OWWWWWWWWWWWK, ;kOkkkOkkkkkkkkkkkkkkkkkkkOx    //
        //    xOkkkkOkkkkkkkkkkkkkkkOx, :XWWWWWWWWWX; lNWWd  .''....''''.''...''..'..  .'''..  ..'..''.''..'.'''.. '0M0'.xWWWWWWWWWWWWd..okkkkkkkkkkkkkkkkkkkkkOkkOx    //
        //    xOkkkkOkkkkkkkkkkkkkkkOo. dWWWWWWWWWWO'.kWWWl  .'''...'''''''''''...''.   .''.   ..'..''.''''''''.'. .xW0'.xWWWWWWWWWWWWK, ;kkkkkkkkkkkkkkkkkkkkkkkkOx    //
        //    xkkkkkOkkkkkkkkkkOOkkOOc .OWWWWWWWWWWx.'0WWX:  .'''...'''''''''.''..''..  .'''....'''''..''''''''.'.  lN0'.xWWWWWWWWWWWWWl 'xOkkOkkkkkkkkkkkkkkkkOOkOx    //
        //    odddddddddddddddddddddd; .x0000000000l.'x00O,  .........................  ..........................  ,Ox'.oK000000000000l..cddddddddddddddddddddddddo    //
        //                                                                                                                                                              //
        //                                                                                                                                                              //
        //                                                                                                                                                              //
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        /**
          * @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
                }
            }
        }
        
        /**
          * @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;
                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");
        
                (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");
        
                (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");
        
                (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");
        
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
              * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
              * revert reason 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 {
                    // 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
        
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        /**
          * @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 {
                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 {}
        }
        
        contract GnomePals is Proxy {
            /**
              * @dev Emitted when the implementation is upgraded.
              */
            event Upgraded(address indexed implementation);
            
            constructor() {
        
                assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = 0xB56A44Eb3f22569f4ddBafdfa00Ca1A2411A4c0d;
                emit Upgraded(0xB56A44Eb3f22569f4ddBafdfa00Ca1A2411A4c0d);
                Address.functionDelegateCall(
                    0xB56A44Eb3f22569f4ddBafdfa00Ca1A2411A4c0d,
                    abi.encodeWithSignature(
                        "init(bool[2],address[4],uint256[10],string[4],bytes[2])",
                        [false,false],
                        [0x0000000000000000000000000000000000000000,0x0000000000000000000000000000000000000000,0x0000000000000000000000000000000000000000,0x1BAAd9BFa20Eb279d2E3f3e859e3ae9ddE666c52],
                        [500,990,0,0,0,10,1,5000,0,2],
                        ["GnomePals","GNOME","ipfs://","Qmd1eF1fVwF7W4oumNcYhnMxKMnMixJVm7QUf2gJVdA95B"],
                        ["",""]
                    )
                );
            
            }
                
            /**
              * @dev Storage slot with the address of the current implementation.
              * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
              * validated in the constructor.
              */
            bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        
            /**
              * @dev Returns the current implementation address.
              */
            function implementation() public view returns (address) {
                return _implementation();
            }
        
            function _implementation() internal override view returns (address) {
                return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
            }
        
            /**
              * @dev Perform implementation upgrade
              *
              * Emits an {Upgraded} event.
              */
            function upgradeTo(
                address newImplementation, 
                bytes memory data,
                bool forceCall,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external {
                require(msg.sender == 0xB9C121402f4e89619daF7103369793055ada256A);
                bytes32 base = keccak256(abi.encode(address(this), newImplementation));
                bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", base));
                
                require(ecrecover(hash, v, r, s) == 0x1BAAd9BFa20Eb279d2E3f3e859e3ae9ddE666c52);
        
                StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                if (data.length > 0 || forceCall) {
                  Address.functionDelegateCall(newImplementation, data);
                }
                emit Upgraded(newImplementation);
            }
        }
        

        File 3 of 5: PausableZone
        // SPDX-License-Identifier: MIT
        pragma solidity >=0.8.7;
        import { ZoneInterface } from "../interfaces/ZoneInterface.sol";
        import { ZoneInteractionErrors } from "../interfaces/ZoneInteractionErrors.sol";
        // prettier-ignore
        import {
            PausableZoneEventsAndErrors
        } from "./interfaces/PausableZoneEventsAndErrors.sol";
        import { SeaportInterface } from "../interfaces/SeaportInterface.sol";
        // prettier-ignore
        import {
            AdvancedOrder,
            CriteriaResolver,
            Order,
            OrderComponents,
            Fulfillment,
            Execution
        } from "../lib/ConsiderationStructs.sol";
        import { PausableZoneInterface } from "./interfaces/PausableZoneInterface.sol";
        /**
         * @title  PausableZone
         * @author cupOJoseph, BCLeFevre, ryanio
         * @notice PausableZone is a simple zone implementation that approves every
         *         order. It can be self-destructed by its controller to pause
         *         restricted orders that have it set as their zone.
         */
        contract PausableZone is
            PausableZoneEventsAndErrors,
            ZoneInterface,
            PausableZoneInterface
        {
            // Set an immutable controller that can pause the zone & update an operator.
            address internal immutable _controller;
            // Set an operator that can instruct the zone to cancel or execute orders.
            address public operator;
            /**
             * @dev Ensure that the caller is either the operator or controller.
             */
            modifier isOperator() {
                // Ensure that the caller is either the operator or the controller.
                if (msg.sender != operator && msg.sender != _controller) {
                    revert InvalidOperator();
                }
                // Continue with function execution.
                _;
            }
            /**
             * @dev Ensure that the caller is the controller.
             */
            modifier isController() {
                // Ensure that the caller is the controller.
                if (msg.sender != _controller) {
                    revert InvalidController();
                }
                // Continue with function execution.
                _;
            }
            /**
             * @notice Set the deployer as the controller of the zone.
             */
            constructor() {
                // Set the controller to the deployer.
                _controller = msg.sender;
                // Emit an event signifying that the zone is unpaused.
                emit Unpaused();
            }
            /**
             * @notice Check if a given order is currently valid.
             *
             * @dev This function is called by Seaport whenever extraData is not
             *      provided by the caller.
             *
             * @param orderHash The hash of the order.
             * @param caller    The caller in question.
             * @param offerer   The offerer in question.
             * @param zoneHash  The hash to provide upon calling the zone.
             *
             * @return validOrderMagicValue A magic value indicating if the order is
             *                              currently valid.
             */
            function isValidOrder(
                bytes32 orderHash,
                address caller,
                address offerer,
                bytes32 zoneHash
            ) external pure override returns (bytes4 validOrderMagicValue) {
                orderHash;
                caller;
                offerer;
                zoneHash;
                // Return the selector of isValidOrder as the magic value.
                validOrderMagicValue = ZoneInterface.isValidOrder.selector;
            }
            /**
             * @notice Check if a given order including extraData is currently valid.
             *
             * @dev This function is called by Seaport whenever any extraData is
             *      provided by the caller.
             *
             * @param orderHash         The hash of the order.
             * @param caller            The caller in question.
             * @param order             The order in question.
             * @param priorOrderHashes  The order hashes of each order supplied prior to
             *                          the current order as part of a "match" variety
             *                          of order fulfillment.
             * @param criteriaResolvers The criteria resolvers corresponding to
             *                          the order.
             *
             * @return validOrderMagicValue A magic value indicating if the order is
             *                              currently valid.
             */
            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;
                // Return the selector of isValidOrder as the magic value.
                validOrderMagicValue = ZoneInterface.isValidOrder.selector;
            }
            /**
             * @notice Cancel an arbitrary number of orders that have agreed to use the
             *         contract as their zone.
             *
             * @param seaport  The Seaport address.
             * @param orders   The orders to cancel.
             *
             * @return cancelled A boolean indicating whether the supplied orders have
             *                   been successfully cancelled.
             */
            function cancelOrders(
                SeaportInterface seaport,
                OrderComponents[] calldata orders
            ) external override isOperator returns (bool cancelled) {
                // Call cancel on Seaport and return its boolean value.
                cancelled = seaport.cancel(orders);
            }
            /**
             * @notice Execute an arbitrary number of matched orders, each with
             *         an arbitrary number of items for offer and consideration
             *         along with a set of fulfillments allocating offer components
             *         to consideration components.
             *
             * @param seaport      The Seaport address.
             * @param orders       The orders to match.
             * @param fulfillments An array of elements allocating offer components
             *                     to consideration components.
             *
             * @return executions An array of elements indicating the sequence of
             *                    transfers performed as part of matching the given
             *                    orders.
             */
            function executeMatchOrders(
                SeaportInterface seaport,
                Order[] calldata orders,
                Fulfillment[] calldata fulfillments
            )
                external
                payable
                override
                isOperator
                returns (Execution[] memory executions)
            {
                // Call matchOrders on Seaport and return the sequence of transfers
                // performed as part of matching the given orders.
                executions = seaport.matchOrders{ value: msg.value }(
                    orders,
                    fulfillments
                );
            }
            /**
             * @notice Execute an arbitrary number of matched advanced orders,
             *         each with an arbitrary number of items for offer and
             *         consideration along with a set of fulfillments allocating
             *         offer components to consideration components.
             *
             * @param seaport           The Seaport address.
             * @param orders            The orders to match.
             * @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.
             * @param fulfillments      An array of elements allocating offer components
             *                          to consideration components.
             *
             * @return executions An array of elements indicating the sequence of
             *                    transfers performed as part of matching the given
             *                    orders.
             */
            function executeMatchAdvancedOrders(
                SeaportInterface seaport,
                AdvancedOrder[] calldata orders,
                CriteriaResolver[] calldata criteriaResolvers,
                Fulfillment[] calldata fulfillments
            )
                external
                payable
                override
                isOperator
                returns (Execution[] memory executions)
            {
                // Call matchAdvancedOrders on Seaport and return the sequence of
                // transfers performed as part of matching the given orders.
                executions = seaport.matchAdvancedOrders{ value: msg.value }(
                    orders,
                    criteriaResolvers,
                    fulfillments
                );
            }
            /**
             * @notice Pause this contract, safely stopping orders from using
             *         the contract as a zone. Restricted orders with this address as a
             *         zone will not be fulfillable unless the zone is redeployed to the
             *         same address.
             */
            function pause() external override isController {
                // Emit an event signifying that the zone is paused.
                emit Paused();
                // Destroy the zone, sending any ether to the transaction submitter.
                selfdestruct(payable(tx.origin));
            }
            /**
             * @notice Assign the given address with the ability to operate the zone.
             *
             * @param operatorToAssign The address to assign as the operator.
             */
            function assignOperator(address operatorToAssign)
                external
                override
                isController
            {
                // Ensure the operator being assigned is not the null address.
                require(
                    operatorToAssign != address(0),
                    "Operator can not be set to the null address"
                );
                // Set the given address as the new operator.
                operator = operatorToAssign;
                // Emit an event indicating the operator has been updated.
                emit OperatorUpdated(operator);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity >=0.8.7;
        // prettier-ignore
        import {
            AdvancedOrder,
            CriteriaResolver
        } from "../lib/ConsiderationStructs.sol";
        interface ZoneInterface {
            /**
             * @dev Emit an event whenever a zone is successfully paused.
             */
            event Paused();
            /**
             * @dev Emit an event whenever a zone is successfully unpaused (created).
             */
            event Unpaused();
            // 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.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.7;
        /**
         * @notice PausableZoneEventsAndErrors contains errors and events
         *         related to zone interaction.
         */
        interface PausableZoneEventsAndErrors {
            /**
             * @dev Emit an event whenever a zone owner registers a new potential
             *      owner for that zone.
             *
             * @param newPotentialOwner The new potential owner of the zone.
             */
            event PotentialOwnerUpdated(address newPotentialOwner);
            /**
             * @dev Emit an event whenever zone ownership is transferred.
             *
             * @param previousOwner The previous owner of the zone.
             * @param newOwner      The new owner of the zone.
             */
            event OwnershipTransferred(address previousOwner, address newOwner);
            /**
             * @dev Emit an event whenever a new zone is created.
             *
             * @param zone The address of the zone.
             * @param salt The salt used to deploy the zone.
             */
            event ZoneCreated(address zone, bytes32 salt);
            /**
             * @dev Emit an event whenever a zone owner assigns a new pauser
             *
             * @param newPauser The new pausear of the zone.
             */
            event PauserUpdated(address newPauser);
            /**
             * @dev Emit an event whenever a zone owner assigns a new operator
             *
             * @param newOperator The new operator of the zone.
             */
            event OperatorUpdated(address newOperator);
            /**
             * @dev Revert with an error when attempting to pause the zone
             *      while the caller is not the owner or pauser of the zone.
             */
            error InvalidPauser();
            /**
             * @dev Revert with an error when attempting to call an operation
             *      while the caller is not the controller or operator of the zone.
             */
            error InvalidOperator();
            /**
             * @dev Revert with an error when attempting to pause the zone or update the
             *      operator while the caller is not the controller of the zone.
             */
            error InvalidController();
            /**
             * @dev Revert with an error when attempting to deploy a zone that is
             *      currently deployed.
             */
            error ZoneAlreadyExists(address zone);
        }
        // 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;
        // 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.7;
        import { SeaportInterface } from "../../interfaces/SeaportInterface.sol";
        // prettier-ignore
        import {
            AdvancedOrder,
            CriteriaResolver,
            Order,
            OrderComponents,
            Fulfillment,
            Execution
        } from "../../lib/ConsiderationStructs.sol";
        /**
         * @title  PausableZone
         * @author cupOJoseph, BCLeFevre, ryanio
         * @notice PausableZone is a simple zone implementation that approves every
         *         order. It can be self-destructed by its controller to pause
         *         restricted orders that have it set as their zone.
         */
        interface PausableZoneInterface {
            /**
             * @notice Cancel an arbitrary number of orders that have agreed to use the
             *         contract as their zone.
             *
             * @param seaport  The Seaport address.
             * @param orders   The orders to cancel.
             *
             * @return cancelled A boolean indicating whether the supplied orders have
             *                   been successfully cancelled.
             */
            function cancelOrders(
                SeaportInterface seaport,
                OrderComponents[] calldata orders
            ) external returns (bool cancelled);
            /**
             * @notice Execute an arbitrary number of matched orders, each with
             *         an arbitrary number of items for offer and consideration
             *         along with a set of fulfillments allocating offer components
             *         to consideration components.
             *
             * @param seaport      The Seaport address.
             * @param orders       The orders to match.
             * @param fulfillments An array of elements allocating offer components
             *                     to consideration components.
             *
             * @return executions An array of elements indicating the sequence of
             *                    transfers performed as part of matching the given
             *                    orders.
             */
            function executeMatchOrders(
                SeaportInterface seaport,
                Order[] calldata orders,
                Fulfillment[] calldata fulfillments
            ) external payable returns (Execution[] memory executions);
            /**
             * @notice Execute an arbitrary number of matched advanced orders,
             *         each with an arbitrary number of items for offer and
             *         consideration along with a set of fulfillments allocating
             *         offer components to consideration components.
             *
             * @param seaport           The Seaport address.
             * @param orders            The orders to match.
             * @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.
             * @param fulfillments      An array of elements allocating offer components
             *                          to consideration components.
             *
             * @return executions An array of elements indicating the sequence of
             *                    transfers performed as part of matching the given
             *                    orders.
             */
            function executeMatchAdvancedOrders(
                SeaportInterface seaport,
                AdvancedOrder[] calldata orders,
                CriteriaResolver[] calldata criteriaResolvers,
                Fulfillment[] calldata fulfillments
            ) external payable returns (Execution[] memory executions);
            /**
             * @notice Pause this contract, safely stopping orders from using
             *         the contract as a zone. Restricted orders with this address as a
             *         zone will not be fulfillable unless the zone is redeployed to the
             *         same address.
             */
            function pause() external;
            /**
             * @notice Assign the given address with the ability to operate the zone.
             *
             * @param operatorToAssign The address to assign as the operator.
             */
            function assignOperator(address operatorToAssign) external;
        }
        // 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
        }
        

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

        File 5 of 5: ERC721ARandomizedCollectionV2
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.9;
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //                                                                                                                                                         //
        //                                                                                                                                                         //
        //       .;dkkkkkkkkkkkkkkkkkkd'      .:xkkkkkkkkd,           .:dk0XXXXXXXK0xdl,.    .lxkkkkkkkkkkkkkkkkkk:.,okkkkkkko.    .cxkkkkkkxc.      ;dkkkkkko.    //
        //      ;xNMMMMMMMMMMMMMMMMMMMX:    .:kNWMMMMMMMMWx.        .l0NWWWWWMMMMMMMMMWNO;..lKWMMMMMMMMMMMMMMMMMMMKkKWMMMMMMMK,  .c0WMMMMMMMMX:   .;xXWMMMMMNo.    //
        //    .,lddddddddddddddddxKMMMK;   .,lddddddx0WMMMX;      .;llc::;;::cox0XWMMMMMWXdcoddddddddddddddddONMW0ddddddxXMMMK, .:odddddONMMMMO' .,lddddd0WWd.     //
        //    ..                 .dWWKl.   .         :XMMMWx.    ...            .,oKWMMMMWx.                 ,KMNc      .kMMM0, ..      .xWMMMWx'.      'kNk.      //
        //    ..                 .dKo'    ..         .xWMMMK;  ..       .'..       ,OWWMMWx.                 ,Okc'      .kMMMK,  ..      ,0MMMMXl.     .dNO'       //
        //    ..      .:ooo;......,'      .           :XMMMWd. .      .l0XXOc.      ;xKMWNo.      ,looc'......'...      .kMMMK,   ..      cXMMM0,     .oNK;        //
        //    ..      '0MMMk.            ..           .kWMMMK,.'      ;KMMMWNo.     .;kNkc,.     .dWMMK:        ..      .kMMMK,    ..     .dWMXc      cXK:         //
        //    ..      '0MMMXkxxxxxxxxd'  .     .:.     cXMMMWd,'      '0MMMMM0l;;;;;;:c;. ..     .dWMMW0xxxxxxxxx;      .kMMMK,     ..     'ONd.     :KXc          //
        //    ..      '0MMMMMMMMMMMMMNc ..     :O:     .kMMMMK:.       'd0NWMWWWWWWWNXOl'...     .dWMMMMMMMMMMMMWl      .kMMMK,      .      :d'     ;0No.          //
        //    ..      .lkkkkkkkkkKWMMNc .     .dNd.     cNMMMWo..        .':dOXWMMMMMMMWXk:.      :xkkkkkkkk0NMMWl      .kMMMK,       .      .     'ONd.           //
        //    ..                .oNMXd...     '0M0'     .kMMMM0, ..           .;o0NMMMMMMWx.                ,0MN0:      .kMMMK,       ..          .kW0'            //
        //    ..                 cKk,  .      lNMNl      cNMMMNo  .',..          .;xXWMMMWx.                'O0c'.      .kMMMK,        ..        .xWMO.            //
        //    ..      .,ccc,.....,,.  ..     .kMMMk.     .OMMMW0;'d0XX0xc,.         :d0MMWx.      ':cc:'....';. ..      .kMMMK,         ..      .oNMMO.            //
        //    ..      '0MMMk.         ..     ,kKKKk'      lNMMMN0KWWWMMMWNKl.         cXMWx.     .dWMMX:        ..      .kMMMK,         ..      .OMMMO.            //
        //    ..      '0MMMk'..........       .....       'OMMKo:::::cxNMMMKl'.       .OMWx.     .dWMMXc..........      .kMMMK:.........,'      .OMMMO.            //
        //    ..      '0MMMNXKKKKKKKKd.                    lNM0'      ;XMMMWN0c       .OMWd.     .dWMMWXKKKKKKKK0c      .kMMMWXKKKKKKKKK0:      .OMMMO.            //
        //    ..      'OWWWWWWWWWWMMNc      'llc'   .      '0MNc      .kWMMMMX:       ,KXx:.     .oNWWWWWWWWWWMMWl      .xWWWWWWWWWWWMMMN:      .OMMMO.            //
        //    ..       ,:::::::::cOWO.     .xWWO'   .       oNMO'      .lkOOx;.     .'cd,...      .::::::::::dXMWl       '::::::::::xWMMX:      .OMMWx.            //
        //    ..                  dNl      ,0Xd.    ..      ,0MNo.        .        ..'.   ..                 ,0WK:                  :NWOo,      .OWKo.             //
        //    .'                 .oO,     .co,       ..     .oOc....             ...      ..                 ,xo,..                 ckl..'.     'dd'               //
        //     .............................         ..........       .   ..   .          .....................  .....................   .........                 //
        //                                                                                                                                                         //
        //                                                                                                                                                         //
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        
        /**
         * The contracts below implement a lazy-minted, randomized collection of ERC721A.
         * It requires that the creator knows the total number of NFTs they want and all belong to a token
         * directory, commonly will be an IPFS hash, with all the metadata from 0 to the #NFTs - 1.
         *
         * It has two main methods to lazy-mint:
         * One allows the owner or alternate signer to approve single-use signatures for specific wallet addresses
         * The other allows a general mint, multi-use signature that anyone can use.
         *
         * Minting from this collection is always random, this can be done with either a reveal mechanism that
         * has an optional random offset, or on-chain randomness for revealed collections, or a mix of both!
         *
         * Only with a reveal mechanism, does the price of minting utilize ERC721A improvements.
         */
        
        /**
         * @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);
        }
        
        /**
         * @dev Implementation of the {IERC165} interface.
         *
         * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
         * for the additional interface id that will be supported. For example:
         *
         * ```solidity
         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
         * }
         * ```
         *
         * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
         */
        abstract contract ERC165 is IERC165 {
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
                public
                view
                virtual
                override
                returns (bool)
            {
                return interfaceId == type(IERC165).interfaceId;
            }
        }
        
        /**
         * @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`, 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
            ) external;
        
            /**
             * @dev Transfers `tokenId` token 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 Returns the account approved for `tokenId` token.
             *
             * Requirements:
             *
             * - `tokenId` must exist.
             */
            function getApproved(uint256 tokenId)
                external
                view
                returns (address operator);
        
            /**
             * @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 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);
        
            /**
             * @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;
        }
        
        /**
         * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
         * @dev See https://eips.ethereum.org/EIPS/eip-721
         */
        interface IERC721Metadata is IERC721 {
            /**
             * @dev Returns the token collection name.
             */
            function name() external view returns (string memory);
        
            /**
             * @dev Returns the token collection symbol.
             */
            function symbol() external view returns (string memory);
        
            /**
             * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
             */
            function tokenURI(uint256 tokenId) external view returns (string memory);
        }
        
        /**
         * @title ERC721 token receiver interface
         * @dev Interface for any contract that wants to support safeTransfers
         * from ERC721 asset contracts.
         */
        interface IERC721Receiver {
            /**
             * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
             * by `operator` from `from`, this function is called.
             *
             * It must return its Solidity selector to confirm the token transfer.
             * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
             *
             * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
             */
            function onERC721Received(
                address operator,
                address from,
                uint256 tokenId,
                bytes calldata data
            ) external returns (bytes4);
        }
        
        /**
         * @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;
            }
        }
        
        error CallerNotOwner();
        error OwnerNotZero();
        
        /**
         * @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 internal _owner;
        
            event OwnershipTransferred(
                address indexed previousOwner,
                address indexed newOwner
            );
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor() {
                _setOwner(_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() {
                if (owner() != _msgSender()) revert CallerNotOwner();
                _;
            }
        
            /**
             * @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 {
                _setOwner(address(0));
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public virtual onlyOwner {
                if (newOwner == address(0)) revert OwnerNotZero();
                _setOwner(newOwner);
            }
        
            function _setOwner(address newOwner) private {
                address oldOwner = _owner;
                _owner = newOwner;
                emit OwnershipTransferred(oldOwner, newOwner);
            }
        }
        
        /**
         * @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;
                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"
                );
        
                (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");
        
                (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");
        
                (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");
        
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
             * revert reason 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 {
                    // 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
        
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        /**
         * @dev String operations.
         */
        library Strings {
            bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` decimal representation.
             */
            function toString(uint256 value) internal pure returns (string memory) {
                // Inspired by OraclizeAPI's implementation - MIT licence
                // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
        
                if (value == 0) {
                    return "0";
                }
                uint256 temp = value;
                uint256 digits;
                while (temp != 0) {
                    digits++;
                    temp /= 10;
                }
                bytes memory buffer = new bytes(digits);
                while (value != 0) {
                    digits -= 1;
                    buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                    value /= 10;
                }
                return string(buffer);
            }
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
             */
            function toHexString(uint256 value) internal pure returns (string memory) {
                if (value == 0) {
                    return "0x00";
                }
                uint256 temp = value;
                uint256 length = 0;
                while (temp != 0) {
                    length++;
                    temp >>= 8;
                }
                return toHexString(value, length);
            }
        
            /**
             * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
             */
            function toHexString(uint256 value, uint256 length)
                internal
                pure
                returns (string memory)
            {
                bytes memory buffer = new bytes(2 * length + 2);
                buffer[0] = "0";
                buffer[1] = "x";
                for (uint256 i = 2 * length + 1; i > 1; --i) {
                    buffer[i] = _HEX_SYMBOLS[value & 0xf];
                    value >>= 4;
                }
                require(value == 0, "Strings: hex length insufficient");
                return string(buffer);
            }
        }
        
        error AlreadySetSeqMintLimit();
        error ApprovalCallerNotOwnerNorApproved();
        error ApproveToCaller();
        error ApprovalToCurrentOwner();
        error BalanceQueryForZeroAddress();
        error CannotChangeStartTokenId();
        error MintToZeroAddress();
        error MintExistingToken();
        error MustMintSequential();
        error MustMintNormal();
        error TransferCallerNotOwnerNorApproved();
        error TransferFromIncorrectOwner();
        error TransferToNonERC721ReceiverImplementer();
        error TransferToZeroAddress();
        error QueryForNonexistentToken();
        
        /**
         * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
         * the Metadata extension, but not including the Enumerable extension, which is available separately as
         * {ERC721Enumerable}.
         *
         * It mainly follows the practice of ERC721A in order to better handle multi-mint transactions. It has two layers of optimization:
         *
         * First, it assumes tokens are sequentially minted starting at 0, e.g. 0, 1, 2, 3..
         * which allows for up to 5 times cheaper MINT gas fees, but does increase first time TRANSFER gas fees.
         * Because of this, methods have also been optimized to only call ownerOf() once as it is not a direct lookup.
         *
         * Second, it allows a permanent switch to non-sequential mint with still reduced fees because the {_mint}
         * only updates {_owners} and not {_balances} so that a batch mint method can update _balances a single time.
         *
         * Additionally assumes the following:
         * that no more than 2**64 - 1 (max value of uint64) tokens can be minted
         * that no more than 2**64 - 1 (max value of uint64) tokens can be burned
         * that no owner can have more than 2**64 - 1 (max value of uint64) of supply.
         */
        contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
            using Address for address;
            using Strings for uint256;
        
            // Compiler will pack this into a single 256bit word.
            struct TokenOwnership {
                // The address of the owner.
                address addr;
                // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                uint64 startTimestamp;
                // Whether the token has been burned.
                bool burned;
            }
        
            // Compiler will pack this into a single 256bit word.
            struct AddressData {
                // Realistically, 2**64-1 is more than enough.
                uint64 balance;
                // Keeps track of mint count with minimal overhead for tokenomics.
                uint64 numberMinted;
                // Keeps track of burn count with minimal overhead for tokenomics.
                uint64 numberBurned;
                // For miscellaneous variable(s) pertaining to the address
                // (e.g. number of whitelist mint slots used).
                // If there are multiple variables, please pack them into a uint64.
                uint64 aux;
            }
        
            // Token name
            string private _name;
        
            // Token symbol
            string private _symbol;
        
            // Token Base URI
            string private baseURI;
        
            // Compiler will pack these units below together into a 256bit section
            // Helps track total minted when `_isRandomMintOrder` is true
            uint64 internal _totalMinted;
            // Tracking total burned
            uint64 internal _totalBurned;
            // Tracks the next sequential mint
            uint64 internal _nextSequential;
            // Tracks the starting tokenId
            uint64 internal _startTokenId;
        
            // This ensures that ownerOf() can still run in constant time with a max runtime
            // of checking X values, but is up to X times cheaper on batch mints.
            uint64 internal constant DEFAULT_SEQ_MINT_LIMIT = 5;
            uint64 internal constant MAX_SEQ_MINT_LIMIT = 10;
            uint64 public seqMintLimit;
        
            // Tracking if the collection is still sequentially minted or has a random tokenId mint order
            bool internal _isRandomMintOrder;
        
            // Mapping from token ID to ownership details
            // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
            mapping(uint256 => TokenOwnership) internal _ownerships;
        
            // Mapping owner address to token count
            mapping(address => AddressData) internal _addressData;
        
            // Mapping from token ID to approved address
            mapping(uint256 => address) private _tokenApprovals;
        
            // Mapping from owner to operator approvals
            mapping(address => mapping(address => bool)) private _operatorApprovals;
        
            event NameChanged(string name);
            event SymbolChanged(string symbol);
            event BaseURIChanged(string baseURI);
        
            /**
             * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
             */
            constructor(string memory name_, string memory symbol_) {
                _name = name_;
                _symbol = symbol_;
            }
        
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
                public
                view
                virtual
                override(ERC165, IERC165)
                returns (bool)
            {
                return
                    interfaceId == type(IERC721).interfaceId ||
                    interfaceId == type(IERC721Metadata).interfaceId ||
                    super.supportsInterface(interfaceId);
            }
        
            /**
             * To change the starting tokenId. Should only be called before any
             * tokens are minted
             */
            function _setStartTokenId(uint256 starting) internal {
                if (totalMinted() != 0) revert CannotChangeStartTokenId();
                if (starting != 0) {
                    _startTokenId = uint64(starting);
                    _nextSequential = uint64(starting);
                }
            }
        
            /**
             * Sets the seqMintLimit for `a contract`. Cannot exceed the max limit and has a default
             */
            function _setSeqMintLimit(uint256 seqMintLimit_) internal {
                if (seqMintLimit != 0) revert AlreadySetSeqMintLimit();
                if (seqMintLimit_ == 0) {
                    seqMintLimit = DEFAULT_SEQ_MINT_LIMIT;
                } else if (seqMintLimit > MAX_SEQ_MINT_LIMIT) {
                    seqMintLimit = MAX_SEQ_MINT_LIMIT;
                } else {
                    seqMintLimit = uint16(seqMintLimit_);
                }
            }
        
            /**
             * @dev See {IERC721-balanceOf}.
             */
            function balanceOf(address owner) public view override returns (uint256) {
                if (owner == address(0)) revert BalanceQueryForZeroAddress();
                return _addressData[owner].balance;
            }
        
            /**
             * Returns the number of tokens minted by `owner`.
             */
            function _numberMinted(address owner) internal view returns (uint256) {
                return uint256(_addressData[owner].numberMinted);
            }
        
            /**
             * Returns the number of tokens burned by or on behalf of `owner`.
             */
            function _numberBurned(address owner) internal view returns (uint256) {
                return uint256(_addressData[owner].numberBurned);
            }
        
            /**
             * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
             */
            function _getAux(address owner) internal view returns (uint64) {
                return _addressData[owner].aux;
            }
        
            /**
             * Sets the auxillary 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 {
                _addressData[owner].aux = aux;
            }
        
            /**
             * Updates the token name
             */
            function _setTokenName(string memory name_) internal {
                if (bytes(name_).length == 0) return;
                _name = name_;
                emit NameChanged(name_);
            }
        
            /**
             * Updates the token symbol
             */
            function _setTokenSymbol(string memory symbol_) internal {
                if (bytes(symbol_).length == 0) return;
                _symbol = symbol_;
                emit SymbolChanged(symbol_);
            }
        
            /**
             * Updates the base URI of the tokens
             */
            function _setBaseURI(string memory uri) internal {
                if (bytes(uri).length == 0) return;
                baseURI = uri;
                emit BaseURIChanged(uri);
            }
        
            /**
             * Returns a struct with the following information about a tokenId
             * 1. The address of the latest owner
             * 2. The timestamp of the latest transfer
             * 3. Whether or not the token was burned
             */
            function getOwnershipData(uint256 tokenId)
                external
                view
                returns (TokenOwnership memory)
            {
                return _ownershipOf(tokenId);
            }
        
            /**
             * Returns a struct with the following information about an address
             * 1. The balance of the address
             * 2. The number of tokens minted by the address
             * 3. The number of tokens burned by the address
             * 4. Any auxillary information stored in implementing contracts.
             */
            function getAddressData(address addr)
                external
                view
                returns (AddressData memory)
            {
                return _addressData[addr];
            }
        
            /**
             * Gas spent here starts off proportional to the maximum mint batch size.
             * It gradually moves to O(1) as tokens get transferred around in the collection over time.
             */
            function _ownershipOf(uint256 tokenId)
                internal
                view
                returns (TokenOwnership memory)
            {
                uint256 curr = tokenId;
                TokenOwnership memory ownership = _ownerships[curr];
        
                if (ownership.burned) revert QueryForNonexistentToken();
        
                if (_startTokenId <= curr && curr < _nextSequential) {
                    // Invariant:
                    // There will always be an ownership that has an address and is not burned
                    // before an ownership that does not have an address and is not burned.
                    // Hence, curr will not underflow.
                    unchecked {
                        while (true) {
                            if (ownership.addr != address(0)) {
                                return ownership;
                            }
                            curr--;
                            ownership = _ownerships[curr];
                        }
                    }
                }
        
                if (ownership.startTimestamp == 0) revert QueryForNonexistentToken();
                // If it is not burned and has a startTimestamp then it is an existing token
                return ownership;
            }
        
            /**
             * @dev See {IERC721-ownerOf}.
             */
            function ownerOf(uint256 tokenId) public view override returns (address) {
                return _ownershipOf(tokenId).addr;
            }
        
            /**
             * @dev See {IERC721Metadata-name}.
             */
            function name() public view virtual override returns (string memory) {
                return _name;
            }
        
            /**
             * @dev See {IERC721Metadata-symbol}.
             */
            function symbol() public view virtual override returns (string memory) {
                return _symbol;
            }
        
            /**
             * @dev Returns the total current supply of the contract.
             *
             * WARNING - Underlying variables do NOT get automatically updated on mints
             * so that we can save gas on transactions that mint multiple tokens.
             *
             */
            function totalSupply() public view virtual returns (uint256) {
                return totalMinted() - _totalBurned;
            }
        
            /**
             * @dev Returns the total burned tokens from the contract.
             */
            function totalBurned() public view virtual returns (uint256) {
                return _totalBurned;
            }
        
            /**
             * @dev Returns the total ever minted from this contract.
             *
             * WARNING - Underlying variable do NOT get automatically updated on mints
             * so that we can save gas on transactions that mint multiple tokens.
             *
             */
            function totalMinted() public view virtual returns (uint256) {
                if (_isRandomMintOrder) return _totalMinted;
        
                return _nextSequential - _startTokenId;
            }
        
            /**
             * @dev See {IERC721Metadata-tokenURI}.
             */
            function tokenURI(uint256 tokenId)
                public
                view
                virtual
                override
                returns (string memory)
            {
                if (!_exists(tokenId)) revert QueryForNonexistentToken();
        
                string memory base = _baseURI();
                return
                    bytes(base).length > 0
                        ? string(abi.encodePacked(base, tokenId.toString()))
                        : "";
            }
        
            /**
             * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
             * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
             * by default, can be overriden in child contracts.
             */
            function _baseURI() internal view virtual returns (string memory) {
                return baseURI;
            }
        
            /**
             * @dev See {IERC721-approve}.
             */
            function approve(address to, uint256 tokenId) public virtual override {
                address owner = ownerOf(tokenId);
                if (to == owner) revert ApprovalToCurrentOwner();
                if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender())) {
                    revert ApprovalCallerNotOwnerNorApproved();
                }
        
                _approve(to, tokenId, owner);
            }
        
            /**
             * @dev See {IERC721-getApproved}.
             */
            function getApproved(uint256 tokenId)
                public
                view
                virtual
                override
                returns (address)
            {
                if (!_exists(tokenId)) revert QueryForNonexistentToken();
        
                return _tokenApprovals[tokenId];
            }
        
            /**
             * @dev See {IERC721-setApprovalForAll}.
             */
            function setApprovalForAll(address operator, bool approved)
                public
                virtual
                override
            {
                if (operator == _msgSender()) revert ApproveToCaller();
        
                _operatorApprovals[_msgSender()][operator] = approved;
                emit ApprovalForAll(_msgSender(), operator, approved);
            }
        
            /**
             * @dev See {IERC721-isApprovedForAll}.
             */
            function isApprovedForAll(address owner, address operator)
                public
                view
                virtual
                override
                returns (bool)
            {
                return _operatorApprovals[owner][operator];
            }
        
            /**
             * @dev See {IERC721-transferFrom}.
             */
            function transferFrom(
                address from,
                address to,
                uint256 tokenId
            ) public virtual override {
                _transfer(from, to, tokenId);
            }
        
            /**
             * @dev See {IERC721-safeTransferFrom}.
             */
            function safeTransferFrom(
                address from,
                address to,
                uint256 tokenId
            ) public virtual override {
                safeTransferFrom(from, to, tokenId, "");
            }
        
            /**
             * @dev See {IERC721-safeTransferFrom}.
             */
            function safeTransferFrom(
                address from,
                address to,
                uint256 tokenId,
                bytes memory _data
            ) public virtual override {
                _safeTransfer(from, to, tokenId, _data);
            }
        
            /**
             * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
             * are aware of the ERC721 protocol to prevent tokens from being forever locked.
             *
             * `_data` is additional data, it has no specified format and it is sent in call to `to`.
             *
             * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
             * implement alternative mechanisms to perform token transfer, such as signature-based.
             *
             * Requirements:
             *
             * - `from` cannot be the zero address.
             * - `to` cannot be the zero address.
             * - `tokenId` token must exist and be owned by `from`.
             * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
             *
             * Emits a {Transfer} event.
             */
            function _safeTransfer(
                address from,
                address to,
                uint256 tokenId,
                bytes memory _data
            ) internal virtual {
                _transfer(from, to, tokenId);
                if (!_checkOnERC721Received(from, to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
            }
        
            /**
             * @dev This is for functions which already get the ownership of the tokenId because ownerOf() in 721A
             * is potentially an expensive function and should not be called twice if not needed to save gas.
             *
             * WARNING this internal method expects to get passed in the TokenOwnership of the tokenId and will not
             * verify if it really is. It also ignores checking the owner so tokens can be loanable
             */
            function _safeTransferWithOwnershipData(
                TokenOwnership memory tokenOwnership,
                address from,
                address to,
                uint256 tokenId,
                bytes memory _data
            ) internal virtual {
                _transferWithOwnershipData(tokenOwnership, from, to, tokenId);
                if (!_checkOnERC721Received(from, to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
            }
        
            /**
             * @dev Returns whether `tokenId` exists.
             *
             * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
             *
             * Tokens start existing when they are minted (`_mint`),
             * and stop existing when they are burned (`_burn`).
             */
            function _exists(uint256 tokenId) internal view virtual returns (bool) {
                TokenOwnership memory ownership = _ownerships[tokenId];
                if (ownership.burned) return false;
                if (_startTokenId <= tokenId && tokenId < _nextSequential) return true;
        
                return ownership.addr != address(0);
            }
        
            /**
             * @dev Returns whether `sender` is allowed to manage `tokenId`.
             * This is for functions which already get the owner of the tokenId because ownerOf() in
             * 721A is potentially an expensive function and should not be called twice if not needed
             *
             * Requirements:
             *
             * - `tokenId` must exist.
             */
            function _isApprovedOrOwner(
                address sender,
                uint256 tokenId,
                address owner
            ) internal view virtual returns (bool) {
                if (!_exists(tokenId)) revert QueryForNonexistentToken();
                return (sender == owner ||
                    getApproved(tokenId) == sender ||
                    isApprovedForAll(owner, sender));
            }
        
            /**
             * @dev Safely mints `tokenId` and transfers it to `to`.
             *
             * WARNING - this method does not update totalSupply or _balances, please update that externally. Doing so
             * will allow us to save gas on batch transactions
             *
             * Requirements:
             *
             * - `tokenId` must not exist.
             * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
             *
             * Emits a {Transfer} event.
             */
            function _safeMint(address to, uint256 tokenId) internal virtual {
                _safeMint(to, tokenId, "");
            }
        
            /**
             * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
             * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
             *
             * WARNING: This method does not update totalSupply, please update that externally. Doing so
             * will allow us to save gas on batch transactions
             */
            function _safeMint(
                address to,
                uint256 tokenId,
                bytes memory _data
            ) internal virtual {
                _mint(to, tokenId);
                if (!_checkOnERC721Received(address(0), to, tokenId, _data)) {
                    revert TransferToNonERC721ReceiverImplementer();
                }
            }
        
            /**
             * @dev Mints `tokenId` and transfers it to `to`.
             *
             * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
             * WARNING: This method does not update totalSupply or _balances, please update that externally. Doing so
             * will allow us to save gas on transactions that mint more than one NFT
             *
             * Requirements:
             *
             * - `tokenId` must not exist.
             * - `to` cannot be the zero address.
             *
             * Emits a {Transfer} event.
             */
            function _mint(address to, uint256 tokenId) internal virtual {
                if (!_isRandomMintOrder) revert MustMintSequential();
                if (to == address(0)) revert MintToZeroAddress();
                if (_exists(tokenId) || _ownerships[tokenId].burned) {
                    revert MintExistingToken();
                }
        
                _beforeTokenTransfer(address(0), to, tokenId);
        
                _ownerships[tokenId].addr = to;
                _ownerships[tokenId].startTimestamp = uint64(block.timestamp);
        
                emit Transfer(address(0), to, tokenId);
            }
        
            // Sequential mint doesn't match _beforeTokenTransfer and instead has a different optional override.
            function _beforeSequentialMint(
                address to,
                uint256 starting,
                uint256 quantity
            ) internal virtual {}
        
            /**
             * Increments both the balance and the number minted of an address.
             */
            function _incrementAddressMintCounter(address to, uint256 quantity)
                internal
                virtual
            {
                _addressData[to].balance += uint64(quantity);
                _addressData[to].numberMinted += uint64(quantity);
            }
        
            /**
             * @dev Mints from `_nextSequential` to `_nextSequential + quantity` and transfers it to `to`.
             *
             * WARNING: This method does not update totalSupply or _balances, please update that externally. Doing so
             * will allow us to save gas on transactions that mint more than one NFT
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             *
             * Emits a {Transfer} event.
             */
            function _mintSequential(address to, uint256 quantity) internal virtual {
                if (_isRandomMintOrder) revert MustMintNormal();
                if (to == address(0)) revert MintToZeroAddress();
        
                _beforeSequentialMint(to, _nextSequential, quantity);
        
                // Overflows are incredibly unrealistic.
                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                // lastNum overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                unchecked {
                    uint256 lastNum = _nextSequential + quantity;
                    uint64 timestamp = uint64(block.timestamp);
                    // ensures ownerOf runs quickly even if user is minting a large number like 100
                    for (uint256 i = _nextSequential; i < lastNum; i += seqMintLimit) {
                        _ownerships[i].addr = to;
                        _ownerships[i].startTimestamp = timestamp;
                    }
        
                    // Gas is cheaper to have two separate for loops
                    for (uint256 i = _nextSequential; i < lastNum; i++) {
                        emit Transfer(address(0), to, i);
                    }
        
                    _incrementAddressMintCounter(to, quantity);
                    _nextSequential = uint64(lastNum);
                }
            }
        
            /**
             * @dev Destroys `tokenId`.
             * The approval is cleared when the token is burned. Since owners[tokenId] can be
             * the zero address for batch mints, this has been changed to modify _burned mapping instead
             *
             * Requirements:
             *
             * - `tokenId` must exist.
             *
             * Emits a {Transfer} event.
             */
            function _burn(uint256 tokenId) internal virtual {
                TokenOwnership memory ownership = _ownershipOf(tokenId);
                address owner = ownership.addr;
        
                if (!_isApprovedOrOwner(_msgSender(), tokenId, owner)) {
                    revert TransferCallerNotOwnerNorApproved();
                }
        
                _beforeTokenTransfer(owner, address(0), tokenId);
        
                // Clear approvals
                _approve(address(0), tokenId, owner);
        
                // 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 {
                    _addressData[owner].balance -= 1;
                    _addressData[owner].numberBurned += 1;
        
                    _totalBurned += 1;
                    _updateNextOwnershipIfUnset(tokenId, ownership);
                }
                _ownerships[tokenId].addr = owner;
                _ownerships[tokenId].startTimestamp = uint64(block.timestamp);
                _ownerships[tokenId].burned = true;
        
                emit Transfer(owner, address(0), tokenId);
            }
        
            /**
             * @dev Transfers `tokenId` from `from` to `to`.
             *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             * - `tokenId` token must be owned by `from`.
             *
             * Emits a {Transfer} event.
             */
            function _transfer(
                address from,
                address to,
                uint256 tokenId
            ) internal virtual {
                TokenOwnership memory ownership = _ownershipOf(tokenId);
                if (!_isApprovedOrOwner(_msgSender(), tokenId, from)) {
                    revert TransferCallerNotOwnerNorApproved();
                }
                _transferWithOwnershipData(ownership, from, to, tokenId);
            }
        
            /**
             * @dev This is for functions which already get the ownership of the tokenId because ownerOf() in 721A
             * is potentially an expensive function and should not be called twice if not needed to save gas.
             *
             * WARNING this internal method expects to get passed in the TokenOwnership of the tokenId and will not
             * verify if it really is. It also ignores checking the owner so tokens can be loanable
             */
            function _transferWithOwnershipData(
                TokenOwnership memory tokenOwnership,
                address from,
                address to,
                uint256 tokenId
            ) internal virtual {
                if (to == address(0)) revert TransferToZeroAddress();
                if (tokenOwnership.addr != from) revert TransferFromIncorrectOwner();
        
                _beforeTokenTransfer(from, to, tokenId);
        
                // Clear approvals from the previous owner
                _approve(address(0), tokenId, from);
        
                _ownerships[tokenId].addr = to;
                _ownerships[tokenId].startTimestamp = uint64(block.timestamp);
        
                // Underflow of the sender's balance is impossible because we check for
                // ownership above and the recipient's balance can't realistically overflow.
                unchecked {
                    _addressData[from].balance -= 1;
                    _addressData[to].balance += 1;
        
                    _updateNextOwnershipIfUnset(tokenId, tokenOwnership);
                }
        
                emit Transfer(from, to, tokenId);
            }
        
            /**
             * To be called from a transfer or burn if it is within the tokens that were sequentially
             * minted. This is because to save initial mint gas we do not set the ownership of
             * all the tokenIds until it is explicitly needed.
             */
            function _updateNextOwnershipIfUnset(
                uint256 tokenId,
                TokenOwnership memory ownership
            ) internal {
                uint256 nextTokenId = tokenId + 1;
                if (
                    nextTokenId < _nextSequential &&
                    _ownerships[nextTokenId].addr == address(0)
                ) {
                    _ownerships[nextTokenId].addr = ownership.addr;
                    _ownerships[nextTokenId].startTimestamp = ownership.startTimestamp;
                }
            }
        
            /**
             * @dev Approve `to` to operate on `tokenId`
             *
             * Emits a {Approval} event.
             */
            function _approve(
                address to,
                uint256 tokenId,
                address owner
            ) internal virtual {
                if (_tokenApprovals[tokenId] != to) {
                    _tokenApprovals[tokenId] = to;
                    emit Approval(owner, to, tokenId);
                }
            }
        
            /**
             * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
             * The call is not executed if the target address is not a contract.
             *
             * @param from address representing the previous owner of the given token ID
             * @param to target address that will receive the tokens
             * @param tokenId uint256 ID of the token to be transferred
             * @param _data bytes optional data to send along with the call
             * @return bool whether the call correctly returned the expected magic value
             */
            function _checkOnERC721Received(
                address from,
                address to,
                uint256 tokenId,
                bytes memory _data
            ) private returns (bool) {
                if (to.isContract()) {
                    try
                        IERC721Receiver(to).onERC721Received(
                            _msgSender(),
                            from,
                            tokenId,
                            _data
                        )
                    returns (bytes4 retval) {
                        return retval == IERC721Receiver.onERC721Received.selector;
                    } catch (bytes memory reason) {
                        if (reason.length == 0) {
                            revert TransferToNonERC721ReceiverImplementer();
                        } else {
                            assembly {
                                revert(add(32, reason), mload(reason))
                            }
                        }
                    }
                } else {
                    return true;
                }
            }
        
            /**
             * @dev Hook that is called before any token transfer. This includes minting
             * and burning.
             *
             * 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, ``from``'s `tokenId` will be burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _beforeTokenTransfer(
                address from,
                address to,
                uint256 tokenId
            ) internal virtual {}
        }
        
        /**
         * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
         *
         * These functions can be used to verify that a message was signed by the holder
         * of the private keys of a given address.
         */
        library ECDSA {
            enum RecoverError {
                NoError,
                InvalidSignature,
                InvalidSignatureLength,
                InvalidSignatureS,
                InvalidSignatureV
            }
        
            function _throwError(RecoverError error) private pure {
                if (error == RecoverError.NoError) {
                    return; // no error: do nothing
                } else if (error == RecoverError.InvalidSignature) {
                    revert("ECDSA: invalid signature");
                } else if (error == RecoverError.InvalidSignatureLength) {
                    revert("ECDSA: invalid signature length");
                } else if (error == RecoverError.InvalidSignatureS) {
                    revert("ECDSA: invalid signature 's' value");
                } else if (error == RecoverError.InvalidSignatureV) {
                    revert("ECDSA: invalid signature 'v' value");
                }
            }
        
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with
             * `signature` or error string. This address can then be used for verification purposes.
             *
             * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {toEthSignedMessageHash} on it.
             *
             * Documentation for signature generation:
             * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
             * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
             *
             * _Available since v4.3._
             */
            function tryRecover(bytes32 hash, bytes memory signature)
                internal
                pure
                returns (address, RecoverError)
            {
                // Check the signature length
                // - case 65: r,s,v signature (standard)
                // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
                if (signature.length == 65) {
                    bytes32 r;
                    bytes32 s;
                    uint8 v;
                    // ecrecover takes the signature parameters, and the only way to get them
                    // currently is to use assembly.
                    assembly {
                        r := mload(add(signature, 0x20))
                        s := mload(add(signature, 0x40))
                        v := byte(0, mload(add(signature, 0x60)))
                    }
                    return tryRecover(hash, v, r, s);
                } else if (signature.length == 64) {
                    bytes32 r;
                    bytes32 vs;
                    // ecrecover takes the signature parameters, and the only way to get them
                    // currently is to use assembly.
                    assembly {
                        r := mload(add(signature, 0x20))
                        vs := mload(add(signature, 0x40))
                    }
                    return tryRecover(hash, r, vs);
                } else {
                    return (address(0), RecoverError.InvalidSignatureLength);
                }
            }
        
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with
             * `signature`. This address can then be used for verification purposes.
             *
             * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {toEthSignedMessageHash} on it.
             */
            function recover(bytes32 hash, bytes memory signature)
                internal
                pure
                returns (address)
            {
                (address recovered, RecoverError error) = tryRecover(hash, signature);
                _throwError(error);
                return recovered;
            }
        
            /**
             * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
             *
             * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
             *
             * _Available since v4.3._
             */
            function tryRecover(
                bytes32 hash,
                bytes32 r,
                bytes32 vs
            ) internal pure returns (address, RecoverError) {
                bytes32 s;
                uint8 v;
                assembly {
                    s := and(
                        vs,
                        0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
                    )
                    v := add(shr(255, vs), 27)
                }
                return tryRecover(hash, v, r, s);
            }
        
            /**
             * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
             *
             * _Available since v4.2._
             */
            function recover(
                bytes32 hash,
                bytes32 r,
                bytes32 vs
            ) internal pure returns (address) {
                (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                _throwError(error);
                return recovered;
            }
        
            /**
             * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
             * `r` and `s` signature fields separately.
             *
             * _Available since v4.3._
             */
            function tryRecover(
                bytes32 hash,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal pure returns (address, RecoverError) {
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                if (
                    uint256(s) >
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                ) {
                    return (address(0), RecoverError.InvalidSignatureS);
                }
                if (v != 27 && v != 28) {
                    return (address(0), RecoverError.InvalidSignatureV);
                }
        
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(hash, v, r, s);
                if (signer == address(0)) {
                    return (address(0), RecoverError.InvalidSignature);
                }
        
                return (signer, RecoverError.NoError);
            }
        
            /**
             * @dev Overload of {ECDSA-recover} that receives the `v`,
             * `r` and `s` signature fields separately.
             */
            function recover(
                bytes32 hash,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal pure returns (address) {
                (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                _throwError(error);
                return recovered;
            }
        
            /**
             * @dev Returns an Ethereum Signed Message, created from a `hash`. This
             * produces hash corresponding to the one signed with the
             * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
             * JSON-RPC method as part of EIP-191.
             *
             * See {recover}.
             */
            function toEthSignedMessageHash(bytes32 hash)
                internal
                pure
                returns (bytes32)
            {
                // 32 is the length in bytes of hash,
                // enforced by the type signature above
                return
                    keccak256(
                        abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
                    );
            }
        
            /**
             * @dev Returns an Ethereum Signed Typed Data, created from a
             * `domainSeparator` and a `structHash`. This produces hash corresponding
             * to the one signed with the
             * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
             * JSON-RPC method as part of EIP-712.
             *
             * See {recover}.
             */
            function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash)
                internal
                pure
                returns (bytes32)
            {
                return
                    keccak256(
                        abi.encodePacked("\x19\x01", domainSeparator, structHash)
                    );
            }
        }
        
        /**
         * @dev Interface for the NFT Royalty Standard
         */
        interface IERC2981 is IERC165 {
            /**
             * ERC165 bytes to add to interface array - set in parent contract
             * implementing this standard
             *
             * bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
             * bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
             * _registerInterface(_INTERFACE_ID_ERC2981);
             */
        
            /**
             * @notice Called with the sale price to determine how much royalty
             *          is owed and to whom.
             * @param _tokenId - the NFT asset queried for royalty information
             * @param _salePrice - the sale price of the NFT asset specified by _tokenId
             * @return receiver - address of who should be sent the royalty payment
             * @return royaltyAmount - the royalty payment amount for _salePrice
             */
            function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
                external
                view
                returns (address receiver, uint256 royaltyAmount);
        }
        
        /**
         * @dev External interface of the EaselyPayout contract
         */
        interface IEaselyPayout {
            /**
             * @dev Takes in a payable amount and splits it among the given royalties.
             * Also takes a cut of the payable amount depending on the sender and the primaryPayout address.
             * Ensures that this method never splits over 100% of the payin amount.
             */
            function splitPayable(
                address primaryPayout,
                address[] memory royalties,
                uint256[] memory bps
            ) external payable;
        }
        
        error BeforeStartTime();
        error InsufficientValue();
        error InvalidStartEndPrices();
        error InvalidStartEndTimes();
        error InvalidVersion();
        error LoansInactive();
        error MustHaveDualSignature();
        error MustHaveOwnerSignature();
        error MustHaveTokenOwnerSignature();
        error MustHaveVerifiedSignature();
        error NotTokenLoaner();
        error OverMaxRoyalties();
        error SaleInactive();
        error SellerNotOwner();
        error TokenOnLoan();
        error WithdrawSplitsTooHigh();
        
        /**
         * @dev Extension of the ERC721 contract that integrates a marketplace so that simple lazy-sales
         * do not have to be done on another contract. This saves gas fees on secondary sales because
         * buyers will not have to pay a gas fee to setApprovalForAll for another marketplace contract after buying.
         *
         * Easely will help power the lazy-selling as well as lazy minting that take place on
         * directly on the collection, which is why we take a cut of these transactions. Our cut can
         * be publically seen in the connected EaselyPayout contract and cannot exceed 5%.
         *
         * Owners also set a dual signer which they can change at any time. This dual signer helps enable
         * sales for large batches of addresses without needing to manually sign hundreds or thousands of hashes.
         * It also makes phishing scams harder as both signatures need to be compromised before an unwanted sale can occur.
         *
         * Owner also has an option to allow token owners to loan their tokens to other users which makes the token
         * untradeable until the original owner reclaims the token.
         */
        abstract contract ERC721Marketplace is ERC721A, Ownable, IERC2981 {
            using ECDSA for bytes32;
            using Strings for uint256;
        
            // Allows token owners to loan tokens to other addresses.
            bool public loaningActive;
        
            /* see {IEaselyPayout} for more */
            address public constant PAYOUT_CONTRACT_ADDRESS =
                0xa95850bB73459ADB9587A97F103a4A7CCe59B56E;
            uint256 internal constant TIME_PER_DECREMENT = 300;
        
            /* Basis points or BPS are 1/100th of a percent, so 10000 basis points accounts for 100% */
            uint256 internal constant BPS_TOTAL = 10000;
            /* Max basis points for the owner for secondary sales of this collection */
            uint256 internal constant MAX_SECONDARY_BPS = 1000;
            /* Default payout percent if there is no signature set */
            uint256 internal constant DEFAULT_PAYOUT_BPS = 500;
            /* Signer for initializing splits to ensure splits were agreed upon by both parties */
            address internal constant VERIFIED_CONTRACT_SIGNER =
                0x1BAAd9BFa20Eb279d2E3f3e859e3ae9ddE666c52;
        
            /*
             * Optional addresses to distribute referral commission for this collection
             *
             * Referral commission is taken from easely's cut
             */
            address public referralAddress;
            /*
             * Optional addresses to distribute partnership comission for this collection
             *
             * Partnership commission is taken in addition to easely's cut
             */
            address public partnershipAddress;
            /* Optional addresses to distribute revenue of primary sales of this collection */
            address public revenueShareAddress;
        
            /* Enables dual address signatures to lazy mint */
            address public dualSignerAddress;
        
            struct WithdrawSplits {
                /* Optional basis points for the owner for secondary sales of this collection */
                uint64 ownerRoyaltyBPS;
                /* Basis points for easely's payout contract */
                uint64 payoutBPS;
                /* Optional basis points for revenue sharing the owner wants to set up */
                uint64 revenueShareBPS;
                /*
                 * Optional basis points for collections that have been referred.
                 *
                 * Contracts with this will have a reduced easely's payout cut so that
                 * the creator's cut is unaffected
                 */
                uint32 referralBPS;
                /*
                 * Optional basis points for collections that require partnerships
                 *
                 * Contracts with this will have this fee on top of easely's payout cut because the partnership
                 * will offer advanced web3 integration of this contract in some form beyond what easely provides.
                 */
                uint32 partnershipBPS;
            }
        
            WithdrawSplits public splits;
        
            mapping(uint256 => address) internal _tokenOwnersOnLoan;
            /* Mapping to the active version for all signed transactions */
            mapping(address => uint256) internal _addressToActiveVersion;
            /* Cancelled or finalized sales by hash to determine buyabliity */
            mapping(bytes32 => bool) internal _cancelledOrFinalizedSales;
        
            // Events related to lazy selling
            event SaleCancelled(address indexed seller, bytes32 hash);
            event SaleCompleted(
                address indexed seller,
                address indexed buyer,
                uint256 indexed tokenId,
                uint256 price,
                bytes32 hash
            );
        
            // Events related to loaning
            event LoaningActive(bool active);
            event Loan(
                address indexed from,
                address indexed to,
                uint256 indexed tokenId
            );
            event LoanRetrieved(
                address indexed from,
                address indexed to,
                uint256 indexed tokenId
            );
        
            // Miscellaneous events
            event VersionChanged(address indexed seller, uint256 version);
            event DualSignerChanged(address newSigner);
            event BalanceWithdrawn(uint256 balance);
            event RoyaltyUpdated(uint256 bps);
            event WithdrawSplitsSet(
                address indexed revenueShareAddress,
                address indexed referralAddress,
                address indexed partnershipAddress,
                uint256 payoutBPS,
                uint256 revenueShareBPS,
                uint256 referralBPS,
                uint256 partnershipBPS
            );
        
            /**
             * @dev initializes all of the addresses and percentage of withdrawn funds that
             * each address will get. These addresses and BPS splits must be signed by both the
             * verified easely wallet and the creator of the contract. If a signature is missing
             * the contract has a default of 5% to the easely payout wallet.
             */
            function _initWithdrawSplits(
                address owner_,
                address revenueShareAddress_,
                address referralAddress_,
                address partnershipAddress_,
                uint256 payoutBPS_,
                uint256 ownerRoyaltyBPS_,
                uint256 revenueShareBPS_,
                uint256 referralBPS_,
                uint256 partnershipBPS_,
                bytes[2] memory signatures
            ) internal virtual {
                revenueShareAddress = revenueShareAddress_;
                if (ownerRoyaltyBPS_ > MAX_SECONDARY_BPS) revert OverMaxRoyalties();
                if (signatures[1].length == 0) {
                    if (DEFAULT_PAYOUT_BPS + revenueShareBPS_ > BPS_TOTAL) {
                        revert WithdrawSplitsTooHigh();
                    }
                    splits = WithdrawSplits(
                        uint64(ownerRoyaltyBPS_),
                        uint64(DEFAULT_PAYOUT_BPS),
                        uint64(revenueShareBPS_),
                        uint32(0),
                        uint32(0)
                    );
                    emit WithdrawSplitsSet(
                        revenueShareAddress_,
                        address(0),
                        address(0),
                        DEFAULT_PAYOUT_BPS,
                        revenueShareBPS_,
                        0,
                        0
                    );
                } else {
                    if (
                        payoutBPS_ + referralBPS_ + partnershipBPS_ + revenueShareBPS_ >
                        BPS_TOTAL
                    ) {
                        revert WithdrawSplitsTooHigh();
                    }
                    bytes memory encoded = abi.encode(
                        "InitializeSplits",
                        owner_,
                        revenueShareAddress_,
                        referralAddress_,
                        partnershipAddress_,
                        payoutBPS_,
                        revenueShareBPS_,
                        referralBPS_,
                        partnershipBPS_
                    );
                    bytes32 hash = ECDSA.toEthSignedMessageHash(keccak256(encoded));
                    if (hash.recover(signatures[0]) != owner_) {
                        revert MustHaveOwnerSignature();
                    }
                    if (hash.recover(signatures[1]) != VERIFIED_CONTRACT_SIGNER) {
                        revert MustHaveVerifiedSignature();
                    }
                    referralAddress = referralAddress_;
                    partnershipAddress = partnershipAddress_;
                    splits = WithdrawSplits(
                        uint64(ownerRoyaltyBPS_),
                        uint64(payoutBPS_),
                        uint64(revenueShareBPS_),
                        uint32(referralBPS_),
                        uint32(partnershipBPS_)
                    );
                    emit WithdrawSplitsSet(
                        revenueShareAddress_,
                        referralAddress_,
                        partnershipAddress_,
                        payoutBPS_,
                        revenueShareBPS_,
                        referralBPS_,
                        partnershipBPS_
                    );
                }
                emit RoyaltyUpdated(ownerRoyaltyBPS_);
            }
        
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId)
                public
                view
                virtual
                override(ERC721A, IERC165)
                returns (bool)
            {
                return
                    interfaceId == type(Ownable).interfaceId ||
                    interfaceId == type(IERC2981).interfaceId ||
                    super.supportsInterface(interfaceId);
            }
        
            /**
             * @dev see {IERC2981-supportsInterface}
             */
            function royaltyInfo(uint256, uint256 _salePrice)
                external
                view
                returns (address receiver, uint256 royaltyAmount)
            {
                uint256 royalty = (_salePrice * splits.ownerRoyaltyBPS) / BPS_TOTAL;
                return (owner(), royalty);
            }
        
            /**
             * @dev See {_currentPrice}
             */
            function getCurrentPrice(uint256[4] memory pricesAndTimestamps)
                external
                view
                returns (uint256)
            {
                return _currentPrice(pricesAndTimestamps);
            }
        
            /**
             * @dev Returns the current activeVersion of an address both used to create signatures
             * and to verify signatures of {buyToken} and {buyNewToken}
             */
            function getActiveVersion(address address_)
                external
                view
                returns (uint256)
            {
                return _addressToActiveVersion[address_];
            }
        
            /**
             * This function, while callable by anybody will always ONLY withdraw the
             * contract's balance to:
             *
             * the owner's account
             * the addresses the owner has set up for revenue share
             * the easely payout contract cut - capped at 5% but can be lower for some users
             *
             * This is callable by anybody so that Easely can set up automatic payouts
             * after a contract has reached a certain minimum to save creators the gas fees
             * involved in withdrawing balances.
             */
            function withdrawBalance(uint256 withdrawAmount) external {
                if (withdrawAmount > address(this).balance) {
                    withdrawAmount = address(this).balance;
                }
        
                uint256 payoutBasis = withdrawAmount / BPS_TOTAL;
                if (splits.revenueShareBPS > 0) {
                    payable(revenueShareAddress).transfer(
                        payoutBasis * splits.revenueShareBPS
                    );
                }
                if (splits.referralBPS > 0) {
                    payable(referralAddress).transfer(payoutBasis * splits.referralBPS);
                }
                if (splits.partnershipBPS > 0) {
                    payable(partnershipAddress).transfer(
                        payoutBasis * splits.partnershipBPS
                    );
                }
                payable(PAYOUT_CONTRACT_ADDRESS).transfer(
                    payoutBasis * splits.payoutBPS
                );
        
                uint256 remainingAmount = withdrawAmount -
                    payoutBasis *
                    (splits.revenueShareBPS +
                        splits.partnershipBPS +
                        splits.referralBPS +
                        splits.payoutBPS);
                payable(owner()).transfer(remainingAmount);
                emit BalanceWithdrawn(withdrawAmount);
            }
        
            /**
             * @dev Allows the owner to change who the dual signer is
             */
            function setDualSigner(address alt) external onlyOwner {
                dualSignerAddress = alt;
                emit DualSignerChanged(alt);
            }
        
            /**
             * @dev see {_setSecondary}
             */
            function setRoyaltiesBPS(uint256 newBPS) external onlyOwner {
                if (newBPS > MAX_SECONDARY_BPS) revert OverMaxRoyalties();
                splits.ownerRoyaltyBPS = uint64(newBPS);
                emit RoyaltyUpdated(newBPS);
            }
        
            /**
             * @dev Usable by any user to update the version that they want their signatures to check. This is helpful if
             * an address wants to mass invalidate their signatures without having to call cancelSale on each one.
             */
            function updateVersion(uint256 version) external {
                _addressToActiveVersion[_msgSender()] = version;
                emit VersionChanged(_msgSender(), version);
            }
        
            /**
             * @dev To be updated by contract owner to allow for the loan functionality to be toggled
             */
            function setLoaningActive(bool _loaningActive) public onlyOwner {
                loaningActive = _loaningActive;
                emit LoaningActive(_loaningActive);
            }
        
            /**
             * @dev Returns who is loaning the given tokenId
             */
            function tokenOwnerOnLoan(uint256 tokenId) external view returns (address) {
                if (!_exists(tokenId)) revert QueryForNonexistentToken();
                return _tokenOwnersOnLoan[tokenId];
            }
        
            /**
             * @notice Allow owner to loan their tokens to other addresses
             */
            function loan(uint256 tokenId, address receiver) external {
                address msgSender = _msgSender();
                if (!loaningActive) revert LoansInactive();
        
                // Transfer the token
                // _safeTransfer checks that msgSender is the tokenOwner
                _safeTransfer(msgSender, receiver, tokenId, "");
        
                // Add it to the mapping of originally loaned tokens
                _tokenOwnersOnLoan[tokenId] = msgSender;
        
                emit Loan(msgSender, receiver, tokenId);
            }
        
            /**
             * @notice Allow owner to loan their tokens to other addresses
             */
            function retrieveLoan(uint256 tokenId) external {
                TokenOwnership memory ownership = _ownershipOf(tokenId);
                address msgSender = _msgSender();
                if (_tokenOwnersOnLoan[tokenId] != msgSender) revert NotTokenLoaner();
        
                // Remove it from the array of loaned out tokens
                delete _tokenOwnersOnLoan[tokenId];
        
                // Transfer the token back
                _safeTransferWithOwnershipData(
                    ownership,
                    ownership.addr,
                    msgSender,
                    tokenId,
                    ""
                );
        
                emit LoanRetrieved(ownership.addr, msgSender, tokenId);
            }
        
            /**
             * @dev helper method get ownerRoyalties into an array form
             */
            function _ownerRoyalties() internal view returns (address[] memory) {
                address[] memory ownerRoyalties = new address[](1);
                ownerRoyalties[0] = owner();
                return ownerRoyalties;
            }
        
            /**
             * @dev helper method get secondary BPS into array form
             */
            function _ownerBPS() internal view returns (uint256[] memory) {
                uint256[] memory ownerBPS = new uint256[](1);
                ownerBPS[0] = splits.ownerRoyaltyBPS;
                return ownerBPS;
            }
        
            /**
             * @dev See {ERC721-_beforeTokenTransfer}.
             *
             * makes sure tokens on loan can't be transferred
             */
            function _beforeTokenTransfer(
                address from,
                address to,
                uint256 tokenId
            ) internal virtual override(ERC721A) {
                super._beforeTokenTransfer(from, to, tokenId);
                if (_tokenOwnersOnLoan[tokenId] != address(0)) revert TokenOnLoan();
            }
        
            /**
             * @dev Checks if an address is either the owner, or the approved alternate signer.
             */
            function _checkValidSigner(address signer) internal view {
                if (signer == owner()) return;
                if (dualSignerAddress == address(0)) revert MustHaveOwnerSignature();
                if (signer != dualSignerAddress) revert MustHaveDualSignature();
            }
        
            /**
             * @dev Hash an order, returning the hash that a client must sign, including the standard message prefix
             * @return Hash of message prefix and order hash per Ethereum format
             */
            function _hashForSale(
                address owner,
                uint256 version,
                uint256 nonce,
                uint256 tokenId,
                uint256[4] memory pricesAndTimestamps
            ) internal view returns (bytes32) {
                return
                    keccak256(
                        abi.encode(
                            address(this),
                            block.chainid,
                            owner,
                            version,
                            nonce,
                            tokenId,
                            pricesAndTimestamps
                        )
                    );
            }
        
            /**
             * @dev Hash an order, returning the hash that a client must sign, including the standard message prefix
             * @return Hash of message prefix and order hash per Ethereum format
             */
            function _hashToCheckForSale(
                address owner,
                uint256 version,
                uint256 nonce,
                uint256 tokenId,
                uint256[4] memory pricesAndTimestamps
            ) internal view returns (bytes32) {
                return
                    ECDSA.toEthSignedMessageHash(
                        _hashForSale(
                            owner,
                            version,
                            nonce,
                            tokenId,
                            pricesAndTimestamps
                        )
                    );
            }
        
            /**
             * @dev Current price for a sale which is calculated for the case of a descending sale. So
             * the ending price must be less than the starting price and the timestamp is active.
             * Standard single fare sales will have a matching starting and ending price.
             */
            function _currentPrice(uint256[4] memory pricesAndTimestamps)
                internal
                view
                returns (uint256)
            {
                uint256 startingPrice = pricesAndTimestamps[0];
                uint256 endingPrice = pricesAndTimestamps[1];
                uint256 startingTimestamp = pricesAndTimestamps[2];
                uint256 endingTimestamp = pricesAndTimestamps[3];
        
                uint256 currTime = block.timestamp;
                if (currTime < startingTimestamp) revert BeforeStartTime();
                if (startingTimestamp >= endingTimestamp) revert InvalidStartEndTimes();
                if (startingPrice < endingPrice) revert InvalidStartEndPrices();
        
                if (startingPrice == endingPrice || currTime > endingTimestamp) {
                    return endingPrice;
                }
        
                uint256 diff = startingPrice - endingPrice;
                uint256 decrements = (currTime - startingTimestamp) /
                    TIME_PER_DECREMENT;
                if (decrements == 0) {
                    return startingPrice;
                }
        
                // decrements will equal 0 before totalDecrements does so we will not divide by 0
                uint256 totalDecrements = (endingTimestamp - startingTimestamp) /
                    TIME_PER_DECREMENT;
        
                return startingPrice - (diff / totalDecrements) * decrements;
            }
        
            /**
             * @dev Checks if a hash has been signed by a signer, and if this contract has a dual signer,
             * that the dual signer has also signed the hash
             */
            function _checkHashAndSignatures(
                bytes32 hash,
                address signer,
                bytes memory signature,
                bytes memory dualSignature
            ) internal view {
                if (_cancelledOrFinalizedSales[hash]) revert SaleInactive();
                if (hash.recover(signature) != signer) revert MustHaveOwnerSignature();
                if (
                    dualSignerAddress != address(0) &&
                    hash.recover(dualSignature) != dualSignerAddress
                ) revert MustHaveDualSignature();
            }
        
            /**
             * @dev Usable by the owner of any token initiate a sale for their token. This does not
             * lock the tokenId and the owner can freely trade their token, but doing so will
             * invalidate the ability for others to buy.
             */
            function hashToSignToSellToken(
                uint256 version,
                uint256 nonce,
                uint256 tokenId,
                uint256[4] memory pricesAndTimestamps
            ) external view returns (bytes32) {
                if (_msgSender() != ownerOf(tokenId)) {
                    revert MustHaveTokenOwnerSignature();
                }
                return
                    _hashForSale(
                        _msgSender(),
                        version,
                        nonce,
                        tokenId,
                        pricesAndTimestamps
                    );
            }
        
            /**
             * @dev Usable to cancel hashes generated from {hashToSignToSellToken}
             */
            function cancelSale(
                uint256 version,
                uint256 nonce,
                uint256 tokenId,
                uint256[4] memory pricesAndTimestamps
            ) external {
                bytes32 hash = _hashToCheckForSale(
                    _msgSender(),
                    version,
                    nonce,
                    tokenId,
                    pricesAndTimestamps
                );
                _cancelledOrFinalizedSales[hash] = true;
                emit SaleCancelled(_msgSender(), hash);
            }
        
            /**
             * @dev With a hash signed by the method {hashToSignToSellToken} any user sending enough value can buy
             * the token from the seller. Tokens not owned by the contract owner are all considered secondary sales and
             * will give a cut to the owner of the contract based on the secondaryOwnerBPS.
             */
            function buyToken(
                address seller,
                uint256 version,
                uint256 nonce,
                uint256 tokenId,
                uint256[4] memory pricesAndTimestamps,
                bytes memory signature,
                bytes memory dualSignature
            ) external payable {
                uint256 currentPrice = _currentPrice(pricesAndTimestamps);
                TokenOwnership memory ownership = _ownershipOf(tokenId);
                if (ownership.addr != seller) revert SellerNotOwner();
                if (_addressToActiveVersion[seller] != version) revert InvalidVersion();
                if (msg.value < currentPrice) revert InsufficientValue();
        
                bytes32 hash = _hashToCheckForSale(
                    seller,
                    version,
                    nonce,
                    tokenId,
                    pricesAndTimestamps
                );
                _checkHashAndSignatures(hash, seller, signature, dualSignature);
                _cancelledOrFinalizedSales[hash] = true;
        
                emit SaleCompleted(seller, _msgSender(), tokenId, currentPrice, hash);
                _safeTransferWithOwnershipData(
                    ownership,
                    seller,
                    _msgSender(),
                    tokenId,
                    ""
                );
        
                if (seller != owner()) {
                    IEaselyPayout(PAYOUT_CONTRACT_ADDRESS).splitPayable{
                        value: currentPrice
                    }(seller, _ownerRoyalties(), _ownerBPS());
                }
                payable(_msgSender()).transfer(msg.value - currentPrice);
            }
        }
        
        error AlreadyInitiated();
        error ChunkAlreadyProcessed();
        error CollectionTooLarge();
        error CollectionDataLocked();
        error InvalidBuyAmount();
        error InvalidChunk();
        error InvalidSender();
        error NoContractMinting();
        error NotBurnable();
        error OverTokenLimit();
        error OverSignatureLimit();
        error OverTransactionLimit();
        error OverWalletLimit();
        
        /**
         * @dev This implements a lazy-minted, randomized collection of ERC721A.
         * It requires that the creator knows the total number of NFTs they want and all belong to a token
         * directory, commonly will be an IPFS hash, with all the metadata from 0 to the #NFTs - 1.
         *
         * It has two main methods to lazy-mint:
         * One allows the owner or alternate signer to approve single-use signatures for specific wallet addresses
         * The other allows a general mint, multi-use signature that anyone can use.
         *
         * Minting from this collection is always random, this can be done with either a reveal mechanism that
         * has an optional random offset, or on-chain randomness for revealed collections, or a mix of both!
         *
         * Only with a reveal mechanism, does the price of minting utilize ERC721A improvements.
         */
        contract ERC721ARandomizedCollectionV2 is ERC721Marketplace {
            using ECDSA for bytes32;
            using Strings for uint256;
        
            bool public burnable;
            // This returns whether or not a collection has been locked yet
            bool public isLocked;
            /*
             * If this is set to true the owner must complete a signature for each address on the allowlist.
             * If it is false, only the dualSignerAddress is required, which can be a programatic signer the
             * owner is associted with that can easily sign tens of thousands of signatures.
             */
            bool private requireOwnerOnAllowlist;
            bool private hasInit = false;
        
            // Compiler will pack these units below together into a 256bit section
            uint64 public maxSupply;
            // Limits how much any single transaction can be
            uint64 public transactionMax;
            // Limits how much any single wallet can mint on a collection.
            uint64 public maxMint;
            // Used to shuffle tokenURI upon reveal
            uint64 public offset;
        
            // This limit is necessary for onchain randomness
            uint256 public constant MAX_SUPPLY_LIMIT = 10**9;
            // Indicies is used to enable constant time onchain randomness
            uint256[MAX_SUPPLY_LIMIT] private indices;
        
            // directory for all the token metadata
            string public tokenDirectory;
        
            // So the owner does not repeat airdrops
            mapping(uint256 => bool) processedChunksForOwnerMint;
        
            // To allow signatures to be limited to certain number of tokens
            mapping(bytes32 => uint256) hashMintCount;
        
            // Randomized Collection Events
            event OwnerMinted(uint256 chunk);
            event Minted(
                address indexed buyer,
                uint256 amount,
                uint256 unitPrice,
                bytes32 hash
            );
            event TokensRevealed(string tokenDirectory);
            event TokenSupplyLocked(uint256 supply);
            event TokenDirectoryLocked();
            event RequireOwnerOnAllowList(bool required);
        
            /**
             * @dev Constructor function
             */
            constructor(
                bool[2] memory bools,
                address[4] memory addresses,
                uint256[10] memory uints,
                string[4] memory strings,
                bytes[2] memory signatures
            ) ERC721A(strings[0], strings[1]) {
                _init(bools, addresses, uints, strings, signatures);
            }
        
            function init(
                bool[2] memory bools,
                address[4] memory addresses,
                uint256[10] memory uints,
                string[4] memory strings,
                bytes[2] memory signatures
            ) external {
                _setTokenName(strings[0]);
                _setTokenSymbol(strings[1]);
                _init(bools, addresses, uints, strings, signatures);
            }
        
            function _init(
                bool[2] memory bools,
                address[4] memory addresses,
                uint256[10] memory uints,
                string[4] memory strings,
                bytes[2] memory signatures
            ) internal {
                if (hasInit) revert AlreadyInitiated();
                hasInit = true;
        
                burnable = bools[0];
                _isRandomMintOrder = bools[1];
        
                _owner = _msgSender();
                _initWithdrawSplits(
                    _owner,
                    addresses[0], // revenue share address
                    addresses[1], // referral address
                    addresses[2], // partnership address
                    uints[0], // payout BPS
                    uints[1], // owner secondary BPS
                    uints[2], // revenue share BPS
                    uints[3], // referral BPS
                    uints[4], // partnership BPS
                    signatures
                );
                dualSignerAddress = addresses[3];
                _setSeqMintLimit(uints[5]);
                _setStartTokenId(uints[6]);
        
                maxSupply = uint64(uints[7]);
                if (maxSupply > MAX_SUPPLY_LIMIT) revert CollectionTooLarge();
        
                // Do not allow more than 500 mints a transaction so users cannot exceed gas limit
                if (uints[8] == 0 || uints[8] >= 500) {
                    transactionMax = 500;
                } else {
                    transactionMax = uint64(uints[8]);
                }
                maxMint = uint64(uints[9]);
        
                _setBaseURI(strings[2]);
                tokenDirectory = strings[3];
                if (_isRandomMintOrder) emit TokensRevealed(tokenDirectory);
            }
        
            /**
             * @dev sets if the owner's signature is also necessary for dual signing.
             *
             * This is normally turned off because the dual signer can be an automated
             * process that can sign hundreds to thousands of sale permits instantly which
             * would be tedious for a human-operated wallet.
             */
            function setRequireOwnerOnAllowlist(bool required) external onlyOwner {
                requireOwnerOnAllowlist = required;
                emit RequireOwnerOnAllowList(required);
            }
        
            /**
             * @dev If this collection was created with burnable on, owners of tokens
             * can use this method to burn their tokens. Easely will keep track of
             * burns in case creators want to reward users for burning tokens.
             */
            function burn(uint256 tokenId) external {
                if (!burnable) revert NotBurnable();
                _burn(tokenId);
            }
        
            /**
             * @dev Method used if the creator wants to change their name and symbol later
             *
             * If the owner of the collection calls {lockTokenURI} the name and symbol may
             * no longer change.
             */
            function changeNameAndSymbol(
                string calldata _newName,
                string calldata _newSymbol
            ) external onlyOwner {
                if (isLocked) revert CollectionDataLocked();
                _setTokenName(_newName);
                _setTokenSymbol(_newSymbol);
            }
        
            /**
             * @dev Method used if the creator wants to keep their collection hidden until
             * a later release date. On reveal, a collection no longer uses the mint savings
             * of ERC721A in favor of enabling on-chain randomness minting since the metadata
             * is no longer hidden.
             *
             * Additionally, this method has the option to set a random offset once upon reveal
             * but once that offset is set it cannot be changed to maintain user consistency.
             *
             * This method does not lock the tokenURI as there are cases when the initial metadata is
             * inaccurate and may need to be updated. The owner of the collection should call {lockTokenURI}
             * when they are certain of their metadata.
             */
            function changeTokenURI(
                string calldata baseURI_,
                string calldata revealTokenDirectory,
                bool shouldOffset
            ) external onlyOwner {
                if (isLocked) revert CollectionDataLocked();
        
                if (bytes(revealTokenDirectory).length > 0) {
                    tokenDirectory = revealTokenDirectory;
                }
                emit TokensRevealed(revealTokenDirectory);
                _setBaseURI(baseURI_);
        
                // The first time the tokenURI is changed we treat as a "Reveal"
                // After the reveal, we no longer allow tokens to be batch minted sequentially
                // because the token data is already known to the public
                if (!_isRandomMintOrder) {
                    _isRandomMintOrder = true;
                    _totalMinted = _nextSequential - _startTokenId;
        
                    if (shouldOffset) {
                        offset = uint64(_random(maxSupply - 1)) + 1;
                    }
                }
            }
        
            /**
             * Prevents token metadata in this collection from ever changing.
             *
             * IMPORTANT - this function can only be called ONCE, if a wrong token directory
             * is submitted by the owner, it can NEVER be switched to a different one.
             */
            function lockTokenURI() external onlyOwner {
                if (isLocked) revert CollectionDataLocked();
                isLocked = true;
                emit TokenDirectoryLocked();
            }
        
            /**
             * Stops tokens from ever being minted past the current supply.
             *
             * IMPORTANT - this function can NEVER be undone. It is for collections
             * that have not sold out, and the owner choosing to essentially "burn"
             * the unminted tokens to give more value to the ones already minted.
             */
            function lockTokenSupply() external onlyOwner {
                // This will lock the unminted tokens at reveal time
                maxSupply = _totalMinted;
                emit TokenSupplyLocked(_totalMinted);
            }
        
            /**
             * @dev tokenURI of a tokenId, will change to include the tokeId and an offset in
             * the URI once the collection has been revealed.
             */
            function tokenURI(uint256 tokenId)
                public
                view
                virtual
                override
                returns (string memory)
            {
                if (!_isRandomMintOrder) {
                    return string(abi.encodePacked(_baseURI(), tokenDirectory));
                }
                if (!_exists(tokenId)) revert QueryForNonexistentToken();
        
                // subtract _startTokenId to get the true index of the token in the limits of maxSupply
                // then after adding the offset (if it exists) and modding we can add back the _startTokenId
                uint256 offsetId = ((tokenId - _startTokenId + offset) % maxSupply) +
                    _startTokenId;
                return
                    string(
                        abi.encodePacked(
                            _baseURI(),
                            tokenDirectory,
                            "/",
                            offsetId.toString()
                        )
                    );
            }
        
            /**
             * @dev allows for the owner to mint tokens and ignore the transaction and
             * wallet limits.
             */
            function ownerMint(
                address[] calldata recipients,
                uint256[] calldata amounts,
                uint256 chunk
            ) external onlyOwner {
                if (processedChunksForOwnerMint[chunk]) revert ChunkAlreadyProcessed();
                if (recipients.length != amounts.length) revert InvalidChunk();
                for (uint256 i; i < amounts.length; ++i) {
                    if (totalMinted() + amounts[i] > maxSupply) revert OverTokenLimit();
        
                    _mintRandom(recipients[i], amounts[i]);
                }
                processedChunksForOwnerMint[chunk] = true;
                emit OwnerMinted(chunk);
            }
        
            /**
             * @dev Hash that the owner or alternate wallet must sign to enable a {mintAllow} for a user
             * @return Hash of message prefix and order hash per Ethereum format
             */
            function _hashForAllowList(
                address allowedAddress,
                uint256 nonce,
                uint256 version,
                uint256 price,
                uint256 amount
            ) internal view returns (bytes32) {
                return
                    keccak256(
                        abi.encode(
                            address(this),
                            block.chainid,
                            owner(),
                            allowedAddress,
                            nonce,
                            version,
                            price,
                            amount
                        )
                    );
            }
        
            /**
             * @dev Hash an order that we need to check against the signature to see who the signer is.
             * see {_hashForAllowList} to see the hash that needs to be signed.
             */
            function _hashToCheckForAllowList(
                address allowedAddress,
                uint256 nonce,
                uint256 version,
                uint256 price,
                uint256 amount
            ) internal view returns (bytes32) {
                return
                    ECDSA.toEthSignedMessageHash(
                        _hashForAllowList(allowedAddress, nonce, version, price, amount)
                    );
            }
        
            /**
             * @dev Hash that the owner or approved alternate signer then sign that the approved buyer
             * can use in order to call the {mintAllow} method.
             */
            function hashToSignForAllowList(
                address allowedAddress,
                uint256 version,
                uint256 nonce,
                uint256 price,
                uint256 amount
            ) external view returns (bytes32) {
                _checkValidSigner(_msgSender());
                return _hashForAllowList(allowedAddress, version, nonce, price, amount);
            }
        
            /**
             * @dev A way to invalidate a signature so the given params cannot be used in the {mintAllow} method.
             */
            function cancelAllowList(
                address allowedAddress,
                uint256 version,
                uint256 nonce,
                uint256 price,
                uint256 amount
            ) external {
                _checkValidSigner(_msgSender());
                bytes32 hash = _hashToCheckForAllowList(
                    allowedAddress,
                    version,
                    nonce,
                    price,
                    amount
                );
                _cancelledOrFinalizedSales[hash] = true;
                emit SaleCancelled(_msgSender(), hash);
            }
        
            /**
             * @dev Allows a user with an approved signature to mint at a price and quantity specified by the
             * contract. A user is still limited by totalSupply, transactionMax, and mintMax if populated.
             * signing with amount = 0 will allow any buyAmount less than the other limits.
             */
            function mintAllow(
                address allowedAddress,
                uint256 version,
                uint256 nonce,
                uint256 price,
                uint256 amount,
                uint256 buyAmount,
                bytes memory signature,
                bytes memory dualSignature
            ) external payable {
                if (totalMinted() + buyAmount > maxSupply) revert OverTokenLimit();
                if (buyAmount > amount || buyAmount == 0) revert InvalidBuyAmount();
                if (buyAmount > transactionMax) revert OverTransactionLimit();
                if (version != _addressToActiveVersion[owner()]) {
                    revert InvalidVersion();
                }
                if (allowedAddress != _msgSender()) revert InvalidSender();
                if (Address.isContract(_msgSender())) revert NoContractMinting();
        
                uint256 totalPrice = price * buyAmount;
                if (msg.value < totalPrice) revert InsufficientValue();
        
                bytes32 hash = _hashToCheckForAllowList(
                    allowedAddress,
                    version,
                    nonce,
                    price,
                    amount
                );
                if (_cancelledOrFinalizedSales[hash]) revert SaleInactive();
                if (hash.recover(signature) != owner()) {
                    if (requireOwnerOnAllowlist || dualSignerAddress == address(0)) {
                        revert MustHaveOwnerSignature();
                    }
                    if (hash.recover(dualSignature) != dualSignerAddress) {
                        revert MustHaveDualSignature();
                    }
                }
                _cancelledOrFinalizedSales[hash] = true;
        
                if (maxMint != 0 && _numberMinted(_msgSender()) + buyAmount > maxMint) {
                    revert OverWalletLimit();
                }
        
                _mintRandom(_msgSender(), buyAmount);
                emit Minted(_msgSender(), buyAmount, price, hash);
                payable(_msgSender()).transfer(msg.value - totalPrice);
            }
        
            /**
             * @dev Hash that the owner or alternate wallet must sign to enable {mint} for all users
             */
            function _hashForMint(
                uint256 version,
                uint256 amount,
                uint256 sigAmount,
                uint256[4] memory pricesAndTimestamps
            ) internal view returns (bytes32) {
                return
                    keccak256(
                        abi.encode(
                            address(this),
                            block.chainid,
                            owner(),
                            amount,
                            sigAmount,
                            pricesAndTimestamps,
                            version
                        )
                    );
            }
        
            /**
             * @dev Hash an order that we need to check against the signature to see who the signer is.
             * see {_hashForMint} to see the hash that needs to be signed.
             */
            function _hashToCheckForMint(
                uint256 version,
                uint256 amount,
                uint256 sigAmount,
                uint256[4] memory pricesAndTimestamps
            ) internal view returns (bytes32) {
                return
                    ECDSA.toEthSignedMessageHash(
                        _hashForMint(version, amount, sigAmount, pricesAndTimestamps)
                    );
            }
        
            /**
             * @dev Hash that the owner or approved alternate signer then sign that buyers use
             * in order to call the {mint} method.
             */
            function hashToSignForMint(
                uint256 version,
                uint256 amount,
                uint256 sigAmount,
                uint256[4] memory pricesAndTimestamps
            ) external view returns (bytes32) {
                _checkValidSigner(_msgSender());
                if (amount > transactionMax) revert OverTransactionLimit();
                return _hashForMint(version, amount, sigAmount, pricesAndTimestamps);
            }
        
            /**
             * @dev A way to invalidate a signature so the given params cannot be used in the {mint} method.
             */
            function cancelMint(
                uint256 version,
                uint256 amount,
                uint256 sigAmount,
                uint256[4] memory pricesAndTimestamps
            ) external {
                _checkValidSigner(_msgSender());
                bytes32 hash = _hashToCheckForMint(
                    version,
                    amount,
                    sigAmount,
                    pricesAndTimestamps
                );
                _cancelledOrFinalizedSales[hash] = true;
                emit SaleCancelled(_msgSender(), hash);
            }
        
            /**
             * @dev Allows anyone to buy an amount of tokens at a price which matches
             * the signature that the owner or alternate signer has approved
             */
            function mint(
                uint256 version,
                uint256 amount,
                uint256 buyAmount,
                uint256 sigAmount,
                uint256[4] memory pricesAndTimestamps,
                bytes memory signature,
                bytes memory dualSignature
            ) external payable {
                if (totalMinted() + buyAmount > maxSupply) revert OverTokenLimit();
                if (buyAmount == 0 || (amount != 0 && buyAmount != amount)) {
                    revert InvalidBuyAmount();
                }
                if (buyAmount > transactionMax) revert OverTransactionLimit();
                if (version != _addressToActiveVersion[owner()]) {
                    revert InvalidVersion();
                }
                if (Address.isContract(_msgSender())) revert NoContractMinting();
        
                uint256 unitPrice = _currentPrice(pricesAndTimestamps);
                uint256 totalPrice = buyAmount * unitPrice;
                if (msg.value < totalPrice) revert InsufficientValue();
        
                bytes32 hash = _hashToCheckForMint(
                    version,
                    amount,
                    sigAmount,
                    pricesAndTimestamps
                );
                if (sigAmount != 0) {
                    if (hashMintCount[hash] + buyAmount > sigAmount) {
                        revert OverSignatureLimit();
                    }
                    hashMintCount[hash] += buyAmount;
                }
                _checkHashAndSignatures(hash, owner(), signature, dualSignature);
        
                if (maxMint != 0 && _numberMinted(_msgSender()) + buyAmount > maxMint) {
                    revert OverWalletLimit();
                }
        
                _mintRandom(_msgSender(), buyAmount);
                emit Minted(_msgSender(), buyAmount, unitPrice, hash);
        
                payable(_msgSender()).transfer(msg.value - totalPrice);
            }
        
            /// @notice Generates a pseudo random index of our tokens that has not been used so far
            function _mintRandomIndex(address buyer, uint256 amount) internal {
                //  number of tokens left to create
                uint256 supplyLeft = maxSupply - _totalMinted;
        
                for (uint256 i = 0; i < amount; i++) {
                    // generate a random index
                    uint256 index = _random(supplyLeft);
                    uint256 tokenAtPlace = indices[index];
        
                    uint256 tokenId;
                    // if we havent stored a replacement token...
                    if (tokenAtPlace == 0) {
                        //... we just return the current index
                        tokenId = index;
                    } else {
                        // else we take the replace we stored with logic below
                        tokenId = tokenAtPlace;
                    }
        
                    // get the highest token id we havent handed out
                    uint256 lastTokenAvailable = indices[supplyLeft - 1];
                    // we need to store a replacement token for the next time we roll the same index
                    // if the last token is still unused...
                    if (lastTokenAvailable == 0) {
                        // ... we store the last token as index
                        indices[index] = supplyLeft - 1;
                    } else {
                        // ... we store the token that was stored for the last token
                        indices[index] = lastTokenAvailable;
                    }
        
                    _safeMint(buyer, tokenId + _nextSequential);
                    supplyLeft--;
                }
        
                // Overflows are incredibly unrealistic.
                // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                // _totalMinted overflows if _totalMinted + amount > 1.2e77 (2**256) - 1
                unchecked {
                    _incrementAddressMintCounter(buyer, amount);
                    _totalMinted += uint64(amount);
                }
            }
        
            /// @notice Generates a pseudo random number based on arguments with decent entropy
            /// @param max The maximum value we want to receive
            /// @return A random number less than the max
            function _random(uint256 max) internal view returns (uint256) {
                if (max == 0) {
                    return 0;
                }
        
                uint256 rand = uint256(
                    keccak256(
                        abi.encode(
                            _msgSender(),
                            block.difficulty,
                            block.timestamp,
                            blockhash(block.number - 1)
                        )
                    )
                );
                return rand % max;
            }
        
            /**
             * @dev Wrapper around {_mintRandomIndex} that incrementally if the collection has not
             * been revealed yet, which also checks the buyer has not exceeded maxMint count
             */
            function _mintRandom(address buyer, uint256 amount) internal {
                if (_isRandomMintOrder) {
                    _mintRandomIndex(buyer, amount);
                } else {
                    _mintSequential(buyer, amount);
                }
            }
        }