More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 149,967 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Cancel | 79582422 | 742 days ago | IN | 0 ETH | 0.00009887 | ||||
Cancel | 78369260 | 745 days ago | IN | 0 ETH | 0.00013521 | ||||
Cancel | 65340578 | 785 days ago | IN | 0 ETH | 0.00008923 | ||||
Cancel | 43300837 | 871 days ago | IN | 0 ETH | 0.00004946 | ||||
Fulfill Order | 41976436 | 876 days ago | IN | 0.0005 ETH | 0.00007601 | ||||
Fulfill Order | 41975787 | 876 days ago | IN | 0.0001 ETH | 0.00008089 | ||||
Fulfill Order | 41974543 | 876 days ago | IN | 0.0001 ETH | 0.00007992 | ||||
Fulfill Order | 41974256 | 876 days ago | IN | 0.00025 ETH | 0.00007213 | ||||
Fulfill Order | 41973404 | 876 days ago | IN | 0.0001 ETH | 0.00007298 | ||||
Fulfill Order | 41972741 | 876 days ago | IN | 0.0065 ETH | 0.00006893 | ||||
Fulfill Order | 41972074 | 876 days ago | IN | 0.0035 ETH | 0.00007519 | ||||
Fulfill Order | 41971767 | 876 days ago | IN | 0.0001 ETH | 0.00007573 | ||||
Fulfill Order | 41971501 | 876 days ago | IN | 0.018498 ETH | 0.00007722 | ||||
Fulfill Order | 41971238 | 876 days ago | IN | 0.001 ETH | 0.00007283 | ||||
Fulfill Order | 41971111 | 876 days ago | IN | 0.00026 ETH | 0.00007568 | ||||
Fulfill Order | 41970927 | 876 days ago | IN | 0.00069 ETH | 0.00006954 | ||||
Fulfill Order | 41970725 | 876 days ago | IN | 0.0002 ETH | 0.00007747 | ||||
Fulfill Order | 41970651 | 876 days ago | IN | 0.001112 ETH | 0.00007081 | ||||
Fulfill Order | 41970498 | 876 days ago | IN | 0.018498 ETH | 0.00008017 | ||||
Fulfill Order | 41969610 | 876 days ago | IN | 0.0037 ETH | 0.00008036 | ||||
Fulfill Order | 41969014 | 876 days ago | IN | 0.0022 ETH | 0.00007413 | ||||
Fulfill Order | 41968948 | 876 days ago | IN | 0.0022 ETH | 0.00007525 | ||||
Fulfill Order | 41968695 | 876 days ago | IN | 0.0001 ETH | 0.00008131 | ||||
Fulfill Availabl... | 41968494 | 876 days ago | IN | 0.0035 ETH | 0.00008772 | ||||
Fulfill Order | 41968319 | 876 days ago | IN | 0.0001 ETH | 0.00008052 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
41976436 | 876 days ago | 0.0004875 ETH | ||||
41976436 | 876 days ago | 0.0000125 ETH | ||||
41976436 | 876 days ago | 0 ETH | ||||
41976436 | 876 days ago | 0 ETH | ||||
41976436 | 876 days ago | 0 ETH | ||||
41975787 | 876 days ago | 0.0000025 ETH | ||||
41975787 | 876 days ago | 0.000095 ETH | ||||
41975787 | 876 days ago | 0.0000025 ETH | ||||
41975787 | 876 days ago | 0 ETH | ||||
41975787 | 876 days ago | 0 ETH | ||||
41975787 | 876 days ago | 0 ETH | ||||
41974543 | 876 days ago | 0.000005 ETH | ||||
41974543 | 876 days ago | 0.0000925 ETH | ||||
41974543 | 876 days ago | 0.0000025 ETH | ||||
41974543 | 876 days ago | 0 ETH | ||||
41974543 | 876 days ago | 0 ETH | ||||
41974543 | 876 days ago | 0 ETH | ||||
41974256 | 876 days ago | 0.00024375 ETH | ||||
41974256 | 876 days ago | 0.00000625 ETH | ||||
41974256 | 876 days ago | 0 ETH | ||||
41974256 | 876 days ago | 0 ETH | ||||
41974256 | 876 days ago | 0 ETH | ||||
41973404 | 876 days ago | 0.0000025 ETH | ||||
41973404 | 876 days ago | 0.000095 ETH | ||||
41973404 | 876 days ago | 0.0000025 ETH |
Loading...
Loading
Contract Name:
Seaport
Compiler Version
v0.8.14+commit.80d49f37
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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.13; import { ConsiderationInterface } from "../interfaces/ConsiderationInterface.sol"; 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; 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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; 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 { Side, ItemType } from "./ConsiderationEnums.sol"; 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.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.13; import { ItemType } from "./ConsiderationEnums.sol"; 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; import { ItemType, Side } from "./ConsiderationEnums.sol"; import { OfferItem, ConsiderationItem, ReceivedItem, OrderParameters, AdvancedOrder, Execution, FulfillmentComponent } from "./ConsiderationStructs.sol"; import "./ConsiderationConstants.sol"; 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; /* * -------------------------- 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 { ConduitInterface } from "../interfaces/ConduitInterface.sol"; import { OrderType, ItemType, BasicOrderRouteType } from "./ConsiderationEnums.sol"; 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 { ItemType, Side } from "./ConsiderationEnums.sol"; import { OfferItem, ConsiderationItem, OrderParameters, AdvancedOrder, CriteriaResolver } from "./ConsiderationStructs.sol"; import "./ConsiderationConstants.sol"; 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.13; 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; 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.13; import { OrderType } from "./ConsiderationEnums.sol"; 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.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; enum ConduitItemType { NATIVE, // unused ERC20, ERC721, ERC1155 }
// 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.13; import { ZoneInterface } from "../interfaces/ZoneInterface.sol"; import { OrderType } from "./ConsiderationEnums.sol"; import { AdvancedOrder, CriteriaResolver } from "./ConsiderationStructs.sol"; import "./ConsiderationConstants.sol"; 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.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 64 or 65 bytes or if the recovered signer does not match the * supplied offerer. Note that in cases where a 64 or 65 byte signature * is supplied, only standard ECDSA signatures that recover to a * non-zero address are supported. * * @param offerer The offerer for the order. * @param orderHash The order hash. * @param signature A signature from the offerer indicating that the order * has been approved. */ function _verifySignature( address offerer, bytes32 orderHash, bytes memory signature ) internal view { // 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; import "./TokenTransferrerConstants.sol"; import { TokenTransferrerErrors } from "../interfaces/TokenTransferrerErrors.sol"; import { ConduitBatch1155Transfer } from "../conduit/lib/ConduitStructs.sol"; /** * @title TokenTransferrer * @author 0age * @custom:coauthor d1ll0n * @custom:coauthor transmissions11 * @notice TokenTransferrer is a library for performing optimized ERC20, ERC721, * ERC1155, and batch ERC1155 transfers, used by both Seaport as well as * by conduits deployed by the ConduitController. Use great caution when * considering these functions for use in other codebases, as there are * significant side effects and edge cases that need to be thoroughly * understood and carefully addressed. */ contract TokenTransferrer is TokenTransferrerErrors { /** * @dev Internal function to transfer ERC20 tokens from a given originator * to a given recipient. Sufficient approvals must be set on the * contract performing the transfer. * * @param token The ERC20 token to transfer. * @param from The originator of the transfer. * @param to The recipient of the transfer. * @param amount The amount to transfer. */ function _performERC20Transfer( address token, address from, address to, uint256 amount ) internal { // Utilize assembly to perform an optimized ERC20 token transfer. assembly { // The free memory pointer memory slot will be used when populating // call data for the transfer; read the value and restore it later. let memPointer := mload(FreeMemoryPointerSlot) // Write call data into memory, starting with function selector. mstore(ERC20_transferFrom_sig_ptr, ERC20_transferFrom_signature) mstore(ERC20_transferFrom_from_ptr, from) mstore(ERC20_transferFrom_to_ptr, to) mstore(ERC20_transferFrom_amount_ptr, amount) // Make call & copy up to 32 bytes of return data to scratch space. // Scratch space does not need to be cleared ahead of time, as the // subsequent check will ensure that either at least a full word of // return data is received (in which case it will be overwritten) or // that no data is received (in which case scratch space will be // ignored) on a successful call to the given token. let callStatus := call( gas(), token, 0, ERC20_transferFrom_sig_ptr, ERC20_transferFrom_length, 0, OneWord ) // Determine whether transfer was successful using status & result. let success := and( // Set success to whether the call reverted, if not check it // either returned exactly 1 (can't just be non-zero data), or // had no return data. or( and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize()) ), callStatus ) // Handle cases where either the transfer failed or no data was // returned. Group these, as most transfers will succeed with data. // Equivalent to `or(iszero(success), iszero(returndatasize()))` // but after it's inverted for JUMPI this expression is cheaper. if iszero(and(success, iszero(iszero(returndatasize())))) { // If the token has no code or the transfer failed: Equivalent // to `or(iszero(success), iszero(extcodesize(token)))` but // after it's inverted for JUMPI this expression is cheaper. if iszero(and(iszero(iszero(extcodesize(token))), success)) { // If the transfer failed: if iszero(success) { // If it was due to a revert: if iszero(callStatus) { // If it returned a message, bubble it up as long as // sufficient gas remains to do so: if returndatasize() { // Ensure that sufficient gas is available to // copy returndata while expanding memory where // necessary. Start by computing the word size // of returndata and allocated memory. Round up // to the nearest full word. let returnDataWords := 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.13; import { OrderParameters } from "./ConsiderationStructs.sol"; import { GettersAndDerivers } from "./GettersAndDerivers.sol"; 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"; 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.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.13; 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; import { ConduitControllerInterface } from "../interfaces/ConduitControllerInterface.sol"; 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; /** * @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 { 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.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; 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.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; /** * @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; /** * @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.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(); }
{ "optimizer": { "enabled": true, "runs": 10000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"conduitController","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadContractSignature","type":"error"},{"inputs":[],"name":"BadFraction","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BadReturnValueFromERC20OnTransfer","type":"error"},{"inputs":[{"internalType":"uint8","name":"v","type":"uint8"}],"name":"BadSignatureV","type":"error"},{"inputs":[],"name":"ConsiderationCriteriaResolverOutOfRange","type":"error"},{"inputs":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"considerationIndex","type":"uint256"},{"internalType":"uint256","name":"shortfallAmount","type":"uint256"}],"name":"ConsiderationNotMet","type":"error"},{"inputs":[],"name":"CriteriaNotEnabledForItem","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"identifiers","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"ERC1155BatchTransferGenericFailure","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EtherTransferGenericFailure","type":"error"},{"inputs":[],"name":"InexactFraction","type":"error"},{"inputs":[],"name":"InsufficientEtherSupplied","type":"error"},{"inputs":[],"name":"Invalid1155BatchTransferEncoding","type":"error"},{"inputs":[],"name":"InvalidBasicOrderParameterEncoding","type":"error"},{"inputs":[{"internalType":"address","name":"conduit","type":"address"}],"name":"InvalidCallToConduit","type":"error"},{"inputs":[],"name":"InvalidCanceller","type":"error"},{"inputs":[{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"address","name":"conduit","type":"address"}],"name":"InvalidConduit","type":"error"},{"inputs":[],"name":"InvalidERC721TransferAmount","type":"error"},{"inputs":[],"name":"InvalidFulfillmentComponentData","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"InvalidMsgValue","type":"error"},{"inputs":[],"name":"InvalidNativeOfferItem","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"InvalidRestrictedOrder","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidTime","type":"error"},{"inputs":[],"name":"MismatchedFulfillmentOfferAndConsiderationComponents","type":"error"},{"inputs":[{"internalType":"enum Side","name":"side","type":"uint8"}],"name":"MissingFulfillmentComponentOnAggregation","type":"error"},{"inputs":[],"name":"MissingItemAmount","type":"error"},{"inputs":[],"name":"MissingOriginalConsiderationItems","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NoContract","type":"error"},{"inputs":[],"name":"NoReentrantCalls","type":"error"},{"inputs":[],"name":"NoSpecifiedOrdersAvailable","type":"error"},{"inputs":[],"name":"OfferAndConsiderationRequiredOnFulfillment","type":"error"},{"inputs":[],"name":"OfferCriteriaResolverOutOfRange","type":"error"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"OrderAlreadyFilled","type":"error"},{"inputs":[],"name":"OrderCriteriaResolverOutOfRange","type":"error"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"OrderIsCancelled","type":"error"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"OrderPartiallyFilled","type":"error"},{"inputs":[],"name":"PartialFillsNotEnabledForOrder","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenTransferGenericFailure","type":"error"},{"inputs":[],"name":"UnresolvedConsiderationCriteria","type":"error"},{"inputs":[],"name":"UnresolvedOfferCriteria","type":"error"},{"inputs":[],"name":"UnusedItemParameters","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCounter","type":"uint256"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"}],"name":"CounterIncremented","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"},{"indexed":true,"internalType":"address","name":"zone","type":"address"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"},{"indexed":true,"internalType":"address","name":"zone","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct SpentItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"indexed":false,"internalType":"struct ReceivedItem[]","name":"consideration","type":"tuple[]"}],"name":"OrderFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"},{"indexed":true,"internalType":"address","name":"zone","type":"address"}],"name":"OrderValidated","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"counter","type":"uint256"}],"internalType":"struct OrderComponents[]","name":"orders","type":"tuple[]"}],"name":"cancel","outputs":[{"internalType":"bool","name":"cancelled","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct AdvancedOrder","name":"advancedOrder","type":"tuple"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes32[]","name":"criteriaProof","type":"bytes32[]"}],"internalType":"struct CriteriaResolver[]","name":"criteriaResolvers","type":"tuple[]"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"}],"name":"fulfillAdvancedOrder","outputs":[{"internalType":"bool","name":"fulfilled","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct AdvancedOrder[]","name":"advancedOrders","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes32[]","name":"criteriaProof","type":"bytes32[]"}],"internalType":"struct CriteriaResolver[]","name":"criteriaResolvers","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[][]","name":"offerFulfillments","type":"tuple[][]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[][]","name":"considerationFulfillments","type":"tuple[][]"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"maximumFulfilled","type":"uint256"}],"name":"fulfillAvailableAdvancedOrders","outputs":[{"internalType":"bool[]","name":"availableOrders","type":"bool[]"},{"components":[{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ReceivedItem","name":"item","type":"tuple"},{"internalType":"address","name":"offerer","type":"address"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"}],"internalType":"struct Execution[]","name":"executions","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[][]","name":"offerFulfillments","type":"tuple[][]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[][]","name":"considerationFulfillments","type":"tuple[][]"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"},{"internalType":"uint256","name":"maximumFulfilled","type":"uint256"}],"name":"fulfillAvailableOrders","outputs":[{"internalType":"bool[]","name":"availableOrders","type":"bool[]"},{"components":[{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ReceivedItem","name":"item","type":"tuple"},{"internalType":"address","name":"offerer","type":"address"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"}],"internalType":"struct Execution[]","name":"executions","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"considerationToken","type":"address"},{"internalType":"uint256","name":"considerationIdentifier","type":"uint256"},{"internalType":"uint256","name":"considerationAmount","type":"uint256"},{"internalType":"address payable","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"internalType":"address","name":"offerToken","type":"address"},{"internalType":"uint256","name":"offerIdentifier","type":"uint256"},{"internalType":"uint256","name":"offerAmount","type":"uint256"},{"internalType":"enum BasicOrderType","name":"basicOrderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"offererConduitKey","type":"bytes32"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalAdditionalRecipients","type":"uint256"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct AdditionalRecipient[]","name":"additionalRecipients","type":"tuple[]"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct BasicOrderParameters","name":"parameters","type":"tuple"}],"name":"fulfillBasicOrder","outputs":[{"internalType":"bool","name":"fulfilled","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"order","type":"tuple"},{"internalType":"bytes32","name":"fulfillerConduitKey","type":"bytes32"}],"name":"fulfillOrder","outputs":[{"internalType":"bool","name":"fulfilled","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"offerer","type":"address"}],"name":"getCounter","outputs":[{"internalType":"uint256","name":"counter","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"counter","type":"uint256"}],"internalType":"struct OrderComponents","name":"order","type":"tuple"}],"name":"getOrderHash","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"getOrderStatus","outputs":[{"internalType":"bool","name":"isValidated","type":"bool"},{"internalType":"bool","name":"isCancelled","type":"bool"},{"internalType":"uint256","name":"totalFilled","type":"uint256"},{"internalType":"uint256","name":"totalSize","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incrementCounter","outputs":[{"internalType":"uint256","name":"newCounter","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"information","outputs":[{"internalType":"string","name":"version","type":"string"},{"internalType":"bytes32","name":"domainSeparator","type":"bytes32"},{"internalType":"address","name":"conduitController","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"uint120","name":"numerator","type":"uint120"},{"internalType":"uint120","name":"denominator","type":"uint120"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"internalType":"struct AdvancedOrder[]","name":"advancedOrders","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"bytes32[]","name":"criteriaProof","type":"bytes32[]"}],"internalType":"struct CriteriaResolver[]","name":"criteriaResolvers","type":"tuple[]"},{"components":[{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[]","name":"offerComponents","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[]","name":"considerationComponents","type":"tuple[]"}],"internalType":"struct Fulfillment[]","name":"fulfillments","type":"tuple[]"}],"name":"matchAdvancedOrders","outputs":[{"components":[{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ReceivedItem","name":"item","type":"tuple"},{"internalType":"address","name":"offerer","type":"address"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"}],"internalType":"struct Execution[]","name":"executions","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order[]","name":"orders","type":"tuple[]"},{"components":[{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[]","name":"offerComponents","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"orderIndex","type":"uint256"},{"internalType":"uint256","name":"itemIndex","type":"uint256"}],"internalType":"struct FulfillmentComponent[]","name":"considerationComponents","type":"tuple[]"}],"internalType":"struct Fulfillment[]","name":"fulfillments","type":"tuple[]"}],"name":"matchOrders","outputs":[{"components":[{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifier","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ReceivedItem","name":"item","type":"tuple"},{"internalType":"address","name":"offerer","type":"address"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"}],"internalType":"struct Execution[]","name":"executions","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"contractName","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"offerer","type":"address"},{"internalType":"address","name":"zone","type":"address"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"}],"internalType":"struct OfferItem[]","name":"offer","type":"tuple[]"},{"components":[{"internalType":"enum ItemType","name":"itemType","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"identifierOrCriteria","type":"uint256"},{"internalType":"uint256","name":"startAmount","type":"uint256"},{"internalType":"uint256","name":"endAmount","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct ConsiderationItem[]","name":"consideration","type":"tuple[]"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"bytes32","name":"zoneHash","type":"bytes32"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes32","name":"conduitKey","type":"bytes32"},{"internalType":"uint256","name":"totalOriginalConsiderationItems","type":"uint256"}],"internalType":"struct OrderParameters","name":"parameters","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order[]","name":"orders","type":"tuple[]"}],"name":"validate","outputs":[{"internalType":"bool","name":"validated","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101c06040523480156200001257600080fd5b50604051620066f3380380620066f38339810160408190526200003591620005c6565b808080808080808080806200004962000132565b610120526101005260e05260c081815260a0838152608085815246610140819052604080516020818101979097528082019890985260608801969096529086015230858201528351808603909101815293019091528151910120610160526001600160a01b03811661018081905260408051630a96ad3960e01b81528151630a96ad39926004808401939192918290030181865afa158015620000f0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001169190620005f8565b506101a052505060016000555062000688975050505050505050565b600080808080806200015e60408051808201909152600781526614d9585c1bdc9d60ca1b602082015290565b80516020918201206040805180820182526003815262312e3160e81b90840152519097507f722c0e0c80487266e8c6a45e3a1a803aab23378a9c32e6ebe029d4fad7bfc96596506000916200026391016909ecccccae492e8cada560b31b81526e1d5a5b9d0e081a5d195b551e5c194b608a1b600a8201526d1859191c995cdcc81d1bdad95b8b60921b60198201527f75696e74323536206964656e7469666965724f7243726974657269612c00000060278201527f75696e74323536207374617274416d6f756e742c0000000000000000000000006044820152701d5a5b9d0c8d4d88195b99105b5bdd5b9d607a1b6058820152602960f81b6069820152606a0190565b60408051601f1981840301815282825271086dedce6d2c8cae4c2e8d2dedc92e8cada560731b60208401526e1d5a5b9d0e081a5d195b551e5c194b608a1b60328401526d1859191c995cdcc81d1bdad95b8b60921b60418401527f75696e74323536206964656e7469666965724f7243726974657269612c000000604f8401527f75696e74323536207374617274416d6f756e742c000000000000000000000000606c840152711d5a5b9d0c8d4d88195b99105b5bdd5b9d0b60721b6080840152701859191c995cdcc81c9958da5c1a595b9d607a1b6092840152602960f81b60a384018190528251808503608401815260a485019093526f09ee4c8cae486dedae0dedccadce8e6560831b60c48501526f1859191c995cdcc81bd999995c995c8b60821b60d48501526c1859191c995cdcc81e9bdb994b609a1b60e48501527113d999995c925d195b56d7481bd999995c8b60721b60f18501527f436f6e73696465726174696f6e4974656d5b5d20636f6e73696465726174696f610103850152611b8b60f21b6101238501526f1d5a5b9d0e081bdc99195c951e5c194b60821b610125850152711d5a5b9d0c8d4d881cdd185c9d151a5b594b60721b6101358501526f1d5a5b9d0c8d4d88195b99151a5b594b60821b61014785015270189e5d195ccccc881e9bdb9952185cda0b607a1b6101578501526c1d5a5b9d0c8d4d881cd85b1d0b609a1b6101688501527f6279746573333220636f6e647569744b65792c000000000000000000000000006101758501526e3ab4b73a191a9b1031b7bab73a32b960891b6101888501526101978401529250906000906101980160408051601f19818403018152908290526c08a92a06e626488dedac2d2dc5609b1b60208301526b1cdd1c9a5b99c81b985b594b60a21b602d8301526e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b60398301526f1d5a5b9d0c8d4d8818da185a5b92590b60821b60488301527f6164647265737320766572696679696e67436f6e7472616374000000000000006058830152602960f81b6071830152915060720160408051601f19818403018152908290528051602091820120855186830120855186840120919a5098509650620005a391839185918791016200065b565b604051602081830303815290604052805190602001209350505050909192939495565b600060208284031215620005d957600080fd5b81516001600160a01b0381168114620005f157600080fd5b9392505050565b600080604083850312156200060c57600080fd5b505080516020909101519092909150565b6000815160005b8181101562000640576020818501810151868301520162000624565b8181111562000650576000828601525b509290920192915050565b60006200067f620006786200067184886200061d565b866200061d565b846200061d565b95945050505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a051615fd3620007206000396000613d5c015260008181610d660152613d1a01526000612736015260006126660152600081816108c6015261294401526000818161085501526127a20152600081816107ee01526128bd01526000612694015260006126e2015260006126ba0152615fd36000f3fe6080604052600436106100e85760003560e01c8063a81744041161008a578063f07ec37311610059578063f07ec373146102a6578063f47b7740146102c6578063fb0f3ee1146102ea578063fd9f1e10146102fd57600080fd5b8063a81744041461025a578063b3a34c4c1461026d578063e7acab2414610280578063ed98a5741461029357600080fd5b80635b34b966116100c65780635b34b966146101c657806379df72bd146101e957806387201b4114610209578063881477321461022a57600080fd5b806306fdde03146100ed57806346423aa71461011857806355944a42146101a6575b600080fd5b3480156100f957600080fd5b5061010261031d565b60405161010f91906149c3565b60405180910390f35b34801561012457600080fd5b506101846101333660046149d6565b60009081526002602052604090205460ff808216926101008304909116916effffffffffffffffffffffffffffff620100008204811692710100000000000000000000000000000000009092041690565b604080519415158552921515602085015291830152606082015260800161010f565b6101b96101b4366004614fd0565b61032c565b60405161010f919061514f565b3480156101d257600080fd5b506101db61034f565b60405190815260200161010f565b3480156101f557600080fd5b506101db610204366004615162565b610359565b61021c61021736600461519e565b6104f0565b60405161010f92919061527f565b34801561023657600080fd5b5061024a6102453660046152d8565b610536565b604051901515815260200161010f565b6101b961026836600461531a565b610549565b61024a61027b366004615386565b6105c7565b61024a61028e3660046153d0565b610639565b61021c6102a1366004615460565b61066d565b3480156102b257600080fd5b506101db6102c1366004615509565b6106f7565b3480156102d257600080fd5b506102db610715565b60405161010f93929190615526565b61024a6102f8366004615558565b61072d565b34801561030957600080fd5b5061024a6103183660046152d8565b610738565b6060610327610744565b905090565b60606103438661033c8688615594565b858561075c565b90505b95945050505050565b6000610327610779565b6040805161016081019091526000906104ea908061037a6020860186615509565b6001600160a01b0316815260200184602001602081019061039b9190615509565b6001600160a01b031681526020016103b660408601866156c9565b808060200260200160405190810160405280939291908181526020016000905b82821015610402576103f360a08302860136819003810190615730565b815260200190600101906103d6565b505050918352505060200161041a606086018661574c565b808060200260200160405190810160405280939291908181526020016000905b828210156104665761045760c083028601368190038101906157b3565b8152602001906001019061043a565b505050918352505060200161048160a08601608087016157cf565b600381111561049257610492615065565b81526020018460a0013581526020018460c0013581526020018460e001358152602001846101000135815260200184610120013581526020018480606001906104db919061574c565b909152506101408401356107d6565b92915050565b6060806105238c6105018c8e615594565b8b8b8b8b8b6001600160a01b038c161561051b578b61051d565b335b8b61091c565b915091509a509a98505050505050505050565b6000610542838361095e565b9392505050565b60606105bc6105588686610ae1565b60408051600080825260208201909252906105b4565b6105a16040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b81526020019060019003908161056e5790505b50858561075c565b90505b949350505050565b60006105426105d584610b9d565b6040805160008082526020820190925290610631565b61061e6040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b8152602001906001900390816105eb5790505b508433610c58565b6000610343610647876157ea565b6106518688615594565b856001600160a01b038616156106675785610c58565b33610c58565b6060806106e661067d8b8b610ae1565b60408051600080825260208201909252906106d9565b6106c66040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b8152602001906001900390816106935790505b508a8a8a8a8a338b61091c565b915091509850989650505050505050565b6001600160a01b0381166000908152600160205260408120546104ea565b6060600080610722610d45565b925092509250909192565b60006104ea82610dc0565b600061054283836110b2565b6060602080526707536561706f727460475260606020f35b606061076e85856001885160006112ee565b6105bc858484611665565b60006107836117e4565b503360008181526001602081815260409283902080549092019182905591518181529092917f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f910160405180910390a290565b610140820151604080519084015180516000939284927f000000000000000000000000000000000000000000000000000000000000000092602090910190845b81811015610843578251601f1901805186825260c082208652905260209384019390920191600101610816565b506020810260405120945050505060007f00000000000000000000000000000000000000000000000000000000000000009150604051602060608901510160005b868110156108b1578151601f1901805186825260e082208552905260209283019290910190600101610884565b505060408051602087029020601f198a0180517f00000000000000000000000000000000000000000000000000000000000000008252928b01805197815260608c018051938152610140909c019a8b5261018082209390915295909552939097525050925250919050565b60608061092d8b8b600086886112ee565b61094c8b61093b8a8c615845565b610945898b615845565b8888611822565b909c909b509950505050505050505050565b60006109686117e4565b6000808084815b81811015610ad3573688888381811061098a5761098a615918565b905060200281019061099c9190615947565b9050366109a98280615985565b90506109b86020820182615509565b94506109cb6109c6826159b9565b611a6f565b600081815260026020526040812098509096506109ed90879089906001611aaa565b50865460ff16610ac957610a438587610a0960208601866159c5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611bd892505050565b86547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178755610a7c6040820160208301615509565b6001600160a01b0316856001600160a01b03167ffde361574a066b44b3b5fe98a87108b7565e327327954c4faeea56a4e6491a0a88604051610ac091815260200190565b60405180910390a35b505060010161096f565b506001979650505050505050565b6060818067ffffffffffffffff811115610afd57610afd6149ef565b604051908082528060200260200182016040528015610b3657816020015b610b236148c5565b815260200190600190039081610b1b5790505b50915060005b81811015610b9557610b70858583818110610b5957610b59615918565b9050602002810190610b6b9190615947565b610b9d565b838281518110610b8257610b82615918565b6020908102919091010152600101610b3c565b505092915050565b610ba56148c5565b6040805160a0810190915280610bbb8480615985565b610bc4906159b9565b815260200160016effffffffffffffffffffffffffffff16815260200160016effffffffffffffffffffffffffffff168152602001838060200190610c0991906159c5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509385525050604080516020818101909252928352909201525092915050565b6000610c62611c48565b60606000806000610c768989600187611c57565b604080516001808252818301909252939650919450925060009190816020015b610c9e6148c5565b815260200190600190039081610c965790505090508981600081518110610cc757610cc7615918565b6020026020010181905250610cdc818a612001565b600081600081518110610cf157610cf1615918565b6020026020010151600001519050610d0c8185858c8c6123fd565b610d2a85826000015183602001518b856040015186606001516125fd565b610d346001600055565b5060019a9950505050505050505050565b6060600080610d52612662565b6040805160038082528183019092529193507f0000000000000000000000000000000000000000000000000000000000000000925060208201818036833750507f312e3100000000000000000000000000000000000000000000000000000000006020830152509391925090565b600061012435600281901c90600316600182113415811480610e15576040517fa61be9f00000000000000000000000000000000000000000000000000000000081523460048201526024015b60405180910390fd5b5060008060008060038711915060a082026024013593506002871460028811600289030201905060018101820260028615028801039250610e5a898783888888612758565b506101c46020820201356000856005811115610e7857610e78615065565b03610f345760208901803590610e8e908b615509565b6001600160a01b03161715610ecf576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f0483610ee360c08c0160a08d01615509565b610ef360808d0160608e01615509565b338d60c001358e60e0013587612a8e565b610f2f60408a0135610f1c60808c0160608d01615509565b610f2a6102008d018d615a2a565b612b74565b611099565b6040805160208082528183019092526000916020820181803683370190505090506002886005811115610f6957610f69615065565b03610fa857610fa3610f8160c08c0160a08d01615509565b610f9160808d0160608e01615509565b338d60c001358e60e001358787612c61565b611073565b6003886005811115610fbc57610fbc615065565b03610ff657610fa3610fd460c08c0160a08d01615509565b610fe460808d0160608e01615509565b338d60c001358e60e001358787612cc7565b600488600581111561100a5761100a615065565b0361104157610fa361101f60208c018c615509565b3361103060808e0160608f01615509565b8d602001358e604001358787612c61565b61107361105160208c018c615509565b3361106260808e0160608f01615509565b8d602001358e604001358787612cc7565b61108e61108660808c0160608d01615509565b8b8584612cfd565b61109781612e37565b505b6110a36001600055565b50600198975050505050505050565b60006110bc6117e4565b6000808084815b81811015610ad357368888838181106110de576110de615918565b90506020028101906110f09190615985565b90506110ff6020820182615509565b94506111116040820160208301615509565b9350336001600160a01b038616148015906111355750336001600160a01b03851614155b1561116c576040517f80ec737400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061125b604051806101600160405280886001600160a01b03168152602001876001600160a01b031681526020018480604001906111ab91906156c9565b808060200260200160405190810160405280939291908181526020016000905b828210156111f7576111e860a08302860136819003810190615730565b815260200190600101906111cb565b505050918352505060200161120f606086018661574c565b808060200260200160405190810160405280939291908181526020016000905b828210156104665761124c60c083028601368190038101906157b3565b8152602001906001019061122f565b6000818152600260205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661010017815590519098509091506001600160a01b0380871691908816907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d906112dc9085815260200190565b60405180910390a350506001016110c3565b6112f6611c48565b845160008167ffffffffffffffff811115611313576113136149ef565b60405190808252806020026020018201604052801561133c578160200160208202803683370190505b506000808252909150601d6045823560e01c061160011b905b8381101561158357600089828151811061137157611371615918565b602002602001015190508660000361139657600060209091015260018101835261157b565b60008060006113a7848d8d8a611c57565b925092509250600185018752816000036113ce57505060006020909201919091525061157b565b828786815181106113e1576113e1615918565b6020908102919091010152835160a081015160c082015160409092015180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909d019c91929160005b818110156114c757600083828151811061144757611447615918565b602002602001015190508051158c179b50600061146989898460800151612e60565b90508160800151826060015103611486576060820181905261149b565b61149589898460600151612e60565b60608301525b6080820181905260608201516114b5908289896000612ebb565b6060909201919091525060010161142b565b50875160600151805160005b8181101561156f5760008382815181106114ef576114ef615918565b60200260200101519050600061150a8b8b8460800151612e60565b90508160800151826060015103611527576060820181905261153c565b6115368b8b8460600151612e60565b60608301525b60808201819052606082015161155690828b8b6001612ebb565b60608301525060a08101516080909101526001016114d3565b50505050505050505050505b600101611355565b50806003036115be576040517f12d3f5a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115c88888612001565b60005b8381101561165a576000801b8382815181106115e9576115e9615918565b6020026020010151031561165257600089828151811061160b5761160b615918565b602002602001015160000151905061165084838151811061162e5761162e615918565b60200260200101518260000151836020015189856040015186606001516125fd565b505b6001016115cb565b505050505050505050565b6060818067ffffffffffffffff811115611681576116816149ef565b60405190808252806020026020018201604052801561170d57816020015b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e08301829052825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161169f5790505b5091506000805b828110156117c2573686868381811061172f5761172f615918565b90506020028101906117419190615947565b90506000611765896117538480615a2a565b6117606020870187615a2a565b612f10565b905080602001516001600160a01b03168160000151608001516001600160a01b031603611797578360010193506117b8565b8086858503815181106117ac576117ac615918565b60200260200101819052505b5050600101611714565b5080156117d0578083510383525b506117db8583613297565b50509392505050565b600160005414611820576040517f7fa8a98700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b8351835160609182916118358183615a92565b67ffffffffffffffff81111561184d5761184d6149ef565b6040519080825280602002602001820160405280156118d957816020015b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e08301829052825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161186b5790505b5092506000805b838110156119735760008a82815181106118fc576118fc615918565b6020026020010151905060006119168d6000848d8d6134ec565b905080602001516001600160a01b03168160000151608001516001600160a01b03160361194857836001019350611969565b80878585038151811061195d5761195d615918565b60200260200101819052505b50506001016118e0565b5060005b82811015611a0d57600089828151811061199357611993615918565b6020026020010151905060006119ae8d6001848d60006134ec565b905080602001516001600160a01b03168160000151608001516001600160a01b0316036119e057836001019350611a03565b80878588860103815181106119f7576119f7615918565b60200260200101819052505b5050600101611977565b508015611a1b578084510384525b508251600003611a57576040517fd5da9a1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a618984613297565b935050509550959350505050565b6000611a858260600151518361014001516135e2565b81516001600160a01b03166000908152600160205260409020546104ea9083906107d6565b8254600090610100900460ff1615611aff578115611af7576040517f1a51557400000000000000000000000000000000000000000000000000000000815260048101869052602401610e0c565b5060006105bf565b83546201000090046effffffffffffffffffffffffffffff168015611bcc578315611b59576040517fee9e0e6300000000000000000000000000000000000000000000000000000000815260048101879052602401610e0c565b84547101000000000000000000000000000000000090046effffffffffffffffffffffffffffff168110611bcc578215611bc2576040517f10fda3e100000000000000000000000000000000000000000000000000000000815260048101879052602401610e0c565b60009150506105bf565b50600195945050505050565b336001600160a01b03841603611bed57505050565b6000611c35611bfa612662565b7f1901000000000000000000000000000000000000000000000000000000000000600090815260029190915260228581526042822091905290565b9050611c4284828461361c565b50505050565b611c506117e4565b6002600055565b60008060008087600001519050611c778160a001518260c0015188613835565b611c8b575060009250829150819050611ff7565b602088015160408901516effffffffffffffffffffffffffffff918216911680821180611cb6575081155b15611ced576040517f5a052b3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8082108015611d0157506080830151600116155b15611d38576040517fa11b63ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d4183611a6f565b9550611d638a8a89898760e00151886080015189600001518a60200151613894565b600086815260026020526040812090611d8090889083908c611aaa565b611d95575060009450849350611ff792505050565b805460ff16611db157611db18460000151888d60600151611bd8565b80546effffffffffffffffffffffffffffff620100008204811691710100000000000000000000000000000000009004168015611f795783600103611dfb57809450809350611e27565b838114611e2757611e0c8483615aaa565b9150611e188186615aaa565b9450611e248185615aaa565b93505b83611e328684615a92565b1115611e3e5781840394505b611e488583615a92565b91506effffffffffffffffffffffffffffff84116effffffffffffffffffffffffffffff83111715611f0257611e93565b60005b8215611e8d57908290069190611e7c565b50919050565b611ea6611ea08584611e79565b86611e79565b80150194859004949384900493909104906effffffffffffffffffffffffffffff8083119085111715611f02577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b82546effffffffffffffffffffffffffffff858116710100000000000000000000000000000000000270ffffffffffffffffffffffffffffffffff91851662010000027fffffffffffffffffffffffffffffff00000000000000000000000000000000009093169290921760011716178355611fec565b82546effffffffffffffffffffffffffffff858116710100000000000000000000000000000000000270ffffffffffffffffffffffffffffffffff91881662010000027fffffffffffffffffffffffffffffff000000000000000000000000000000000090931692909217600117161783555b509295509093505050505b9450945094915050565b8051825160005b828110156122b457600084828151811061202457612024615918565b60200260200101519050600081600001519050838110612070576040517f869586c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86818151811061208257612082615918565b6020026020010151602001516effffffffffffffffffffffffffffff166000036120ad5750506122ac565b60008782815181106120c1576120c1615918565b602090810291909101015151604084015190915060008080866020015160018111156120ef576120ef615065565b036121a257604084015180518410612133576040517fbfb3f8ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081858151811061214757612147615918565b60209081029190910101518051604082015190955093509050600484146003038181600581111561217a5761217a615065565b9081600581111561218d5761218d615065565b9052505060608801516040909101525061224c565b6060840151805184106121e1576040517f6088d7de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008185815181106121f5576121f5615918565b60209081029190910101518051604082015190955093509050600484146003038181600581111561222857612228615065565b9081600581111561223b5761223b615065565b905250506060880151604090910152505b6122568260031090565b61228c576040517f94eb6af600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156122a5576122a586606001518288608001516139a1565b5050505050505b600101612008565b5060005b818110156123f65760008582815181106122d4576122d4615918565b6020026020010151905080602001516effffffffffffffffffffffffffffff1660000361230157506123ee565b805160608101515160005b8181101561237e5761233f8360600151828151811061232d5761232d615918565b60200260200101516000015160031090565b15612376576040517fff75a34000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60010161230c565b505060408101515160005b818110156123e9576123aa8360400151828151811061232d5761232d615918565b156123e1576040517fa6cfc67300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101612389565b505050505b6001016122b8565b5050505050565b60a085015160c0860151604080516020808252818301909252600091602082018180368337505050604089015151909150613a1d9060005b818110156125045760008b60400151828151811061245557612455615918565b602002602001015190506000600581111561247257612472615065565b8151600581111561248557612485615065565b036124bc576040517f12d3f5a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006124d6826060015183608001518e8e8c8c6000613b61565b606083015250608081018890528b516101208d01516124fb9183918863ffffffff8916565b50600101612435565b5050506060880151513490613a1d9060005b818110156125e15760008c60600151828151811061253657612536615918565b60200260200101519050600061255a826060015183608001518f8f8d8d6001613b61565b6060830181905260a08301516080840152905060008251600581111561258257612582615065565b036125c757858111156125c1576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80860395505b6125d782338d8a8963ffffffff16565b5050600101612516565b5050506125ed82612e37565b801561165a5761165a3382613bab565b60608290506060829050856001600160a01b0316876001600160a01b03167f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f318a8886866040516126509493929190615b21565b60405180910390a35050505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461273357610327604080517f000000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b612760611c48565b6127768661012001358761014001356001613835565b5061277f613c18565b61279d612790610200880188615a2a565b9050876101e001356135e2565b6000807f00000000000000000000000000000000000000000000000000000000000000009050806080528560a0526060602460c037604060646101203760e06080206101605261026435602081026102a0016001610264350181526020810190508781526080602460208301376101608760a0528660c052600060e05261020435925060005b8381101561286e578060400261028401602081610100376040816101203760208301925060e0608020835260a084019350898452886020850152604081606086013750600101612823565b60206001850102610160206060526102643593505b838110156128b4578060400261028401915060a0830192508883528760208401526040826060850137600101612883565b505050505060007f00000000000000000000000000000000000000000000000000000000000000009050806080528260a052606060c460c03760206101046101203760c0608020600052602060002060e052602061026435026102000160018152836020820152606060c4604083013750506084356001600160a01b0381166000908152600160205260408120547f000000000000000000000000000000000000000000000000000000000000000060808190529091506040608460a03760605161010052886101205260a061014461014037816101e0526101806080209350505050602061026435026101800181815233602082015260806040820152610120606082015260a061026435026101e00160a4356084357f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f318385a350506000606052612a2981886101600135888a6060016020810190612a149190615509565b612a2460a08d0160808e01615509565b613c7a565b612a8581612a3d60808a0160608b01615509565b612a4b6102208b018b6159c5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613cca92505050565b50505050505050565b8015612b0357600060405190507f4ce34aa200000000000000000000000000000000000000000000000000000000815260206004820152600160248201528760448201528660648201528560848201528460a48201528360c48201528260e4820152612afd8282610104613d14565b50612a85565b6002876005811115612b1757612b17615065565b03612b675781600114612b56576040517fefcc00b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6286868686613e81565b612a85565b612a858686868686613f90565b348160005b81811015612c005736858583818110612b9457612b94615918565b60400291909101915050803584811115612bda576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bf3612bed6040840160208501615509565b82613bab565b9093039250600101612b79565b5081861115612c3b576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c458587613bab565b85821115612c5957612c5933878403613bab565b505050505050565b612c6b81836140c2565b81612cb65782600114612caa576040517fefcc00b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6287878787613e81565b612a85828260028a8a8a8a8a6140e1565b612cd08361417a565b612cda81836140c2565b81612cec57612b628787878787613f90565b612a85828260038a8a8a8a8a6140e1565b60008060008060008615612d3457889450339350612d2160c0890160a08a01615509565b9250505060e086013560c0870135612d56565b339450889350612d476020890189615509565b92505050604086013560208701355b8015612d8e576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50602086026101e403356000612da86102008a018a615a2a565b9050905060005b81811015612e1c5736612dc66102008c018c615a2a565b83818110612dd657612dd6615918565b6040029190910191505080358a15612df557612df28187615bbc565b95505b612e12878a612e0a6040860160208701615509565b84898f6141b4565b5050600101612daf565b50612e2b84878786868c6141b4565b50505050505050505050565b6040815114612e435750565b6000612e50826020015190565b9050612e5c81836141ef565b5050565b6000828403612e70575080610542565b8284830915612ea3577fc63cf0890000000000000000000000000000000000000000000000000000000060005260046000fd5b6000612eaf8584615aaa565b93909304949350505050565b6000848614612f0657838303428590038082036000612eda838a615aaa565b612ee4838c615aaa565b612eee9190615a92565b90508584878303040181151502945050505050610346565b5092949350505050565b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e0830182905282526020820181905291810191909152831580612f5b575081155b15612f92576040517f98e9db6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e083018290528252602082018190529181019190915261302f878585808060200260200160405190810160405280939291908181526020016000905b828210156130245761301560408302860136819003810190615bd3565b81526020019060010190612ff8565b505050505083614213565b80516040805160208089028201810190925287815261308e918a91908a908a90819060009085015b828210156130835761307460408302860136819003810190615bd3565b81526020019060010190613057565b505050505085614401565b805160058111156130a1576130a1615065565b83515160058111156130b5576130b5615065565b1415806130e0575080602001516001600160a01b03168360000151602001516001600160a01b031614155b806130f75750806040015183600001516040015114155b1561312e576040517f09cfb45500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826000015160600151816060015111156131dc5760008585600081811061315757613157615918565b90506040020180360381019061316d9190615bd3565b90508360000151606001518260600151038982600001518151811061319457613194615918565b602002602001015160000151606001518260200151815181106131b9576131b9615918565b602090810291909101015160609081019190915284518101519083015250613277565b6000878760008181106131f1576131f1615918565b9050604002018036038101906132079190615bd3565b90508160600151846000015160600151038982600001518151811061322e5761322e615918565b6020026020010151600001516040015182602001518151811061325357613253615918565b60200260200101516060018181525050816060015184600001516060018181525050505b60809081015183516001600160a01b039091169101525095945050505050565b81516060908067ffffffffffffffff8111156132b5576132b56149ef565b6040519080825280602002602001820160405280156132de578160200160208202803683370190505b50915060005b818110156133e757600085828151811061330057613300615918565b6020026020010151905080602001516effffffffffffffffffffffffffffff1660000361332d57506133df565b600184838151811061334157613341615918565b91151560209283029190910190910152805160600151805160005b818110156133da57600083828151811061337857613378615918565b6020026020010151606001519050806000146133d1576040517fa5f54208000000000000000000000000000000000000000000000000000000008152600481018790526024810183905260448101829052606401610e0c565b5060010161335c565b505050505b6001016132e4565b5060408051602080825281830190925234916000919060208201818036833701905050855190915060005b818110156134be57600087828151811061342e5761342e615918565b6020908102919091010151805190915060008151600581111561345357613453615065565b036134a0578581606001511115613496576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151860395505b6134b4818360200151846040015188613a1d565b5050600101613412565b506134c882612e37565b82156134d8576134d83384613bab565b6134e26001600055565b5050505092915050565b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e0830182905282526020820181905291810191909152835160000361356957846040517f375c24c1000000000000000000000000000000000000000000000000000000008152600401610e0c9190615bff565b600085600181111561357d5761357d615065565b036135a45780516001600160a01b03831660809091015261359f868583614401565b6135bd565b6135af868583614213565b336020820152604081018390525b8051606001516000036103465760006020820181905281516080015295945050505050565b80821015612e5c576040517f466aa61600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060005260008251602084038051826041036000600182116136a3576040880151606089015160001a9650821561368157601b8160ff1c0196507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660408a01525b8689528985526020600060808760015afa508385528589526040890152506000515b89148915151695508590506137fa57604082526044860380516040880380517f1626ba7e0000000000000000000000000000000000000000000000000000000084528a82526020600060648901868f5afa985088156137f0577f1626ba7e00000000000000000000000000000000000000000000000000000000600051146137f0578b3b15613756577f4f7fb80d0000000000000000000000000000000000000000000000000000000060005260046000fd5b600187604103111561378c577f8baa579f0000000000000000000000000000000000000000000000000000000060005260046000fd5b640101000000881a6137c6577f1f003d0a000000000000000000000000000000000000000000000000000000006000528760045260246000fd5b7f815e1d640000000000000000000000000000000000000000000000000000000060005260046000fd5b8486529190925290525b5050505080611c425761380b6145ab565b7f4f7fb80d0000000000000000000000000000000000000000000000000000000060005260046000fd5b6000428411806138455750428311155b1561388a578115613882576040517f6f7eac2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506000610542565b5060019392505050565b60018360038111156138a8576138a8615065565b1180156138be5750336001600160a01b03821614155b80156138d35750336001600160a01b03831614155b15613997576080880151511580156138ea57508651155b15613900576138fb818684876145f3565b613997565b600061398b82633313157060e01b88338d8c8e604051602401613927959493929190615dbf565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614658565b905061165a818761466d565b5050505050505050565b600083600052602060002060208301835160051b81015b808210156139e057815180841160051b938452602093841852604060002092909101906139b8565b50508314905080611c42576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084516005811115613a3257613a32615065565b03613a9957604084015160208501516001600160a01b03161715613a82576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613a9484608001518560600151613bab565b611c42565b600184516005811115613aae57613aae615065565b03613b0957604084015115613aef576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613a948460200151848660800151876060015186866141b4565b600284516005811115613b1e57613b1e615065565b03613b4257613a948460200151848660800151876040015188606001518787612c61565b611c428460200151848660800151876040015188606001518787612cc7565b6000868803613b7c57613b75868689612e60565b9050613ba0565b613b9d613b8a87878b612e60565b613b9588888b612e60565b868686612ebb565b90505b979650505050505050565b613bb48161417a565b600080600080600085875af1905080613c1357613bcf6145ab565b6040517f470c7c1d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101839052604401610e0c565b505050565b6018610124351061024435610260610264356040020114600435602014610224356102401416161680613c77576040517f39f3e3fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6001836003811115613c8e57613c8e615065565b118015613ca45750336001600160a01b03821614155b8015613cb95750336001600160a01b03831614155b156123f6576123f6818684876145f3565b6000838152600260205260409020613ce58482600180611aaa565b50805460ff16613cfa57613cfa838584611bd8565b710100000000000000000000000000000100019055505050565b604080517f000000000000000000000000000000000000000000000000000000000000000074ff000000000000000000000000000000000000000017600090815260208690527f000000000000000000000000000000000000000000000000000000000000000083526055600b20919092526001600160a01b03169050600080600080526020600085876000875af19150600051905081613df457613db76145ab565b6040517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e0c565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f4ce34aa20000000000000000000000000000000000000000000000000000000014612c59576040517f1cf99b26000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0384166024820152604401610e0c565b833b613eb5577f5f15d672000000000000000000000000000000000000000000000000000000006000528360045260246000fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006000528360045282602452816044526000806064600080895af180613f81573d15613f42576020601f3d0104602083048160030281831115613f2957818303600302610200838002858002030401015b5a602082011015613f3e573d6000803e3d6000fd5b5050505b7ff486bc870000000000000000000000000000000000000000000000000000000060005285600452846024528360445282606452600160845260a46000fd5b50604052505060006060525050565b843b613fc4577f5f15d672000000000000000000000000000000000000000000000000000000006000528460045260246000fd5b60405160805160a05160c0517ff242432a000000000000000000000000000000000000000000000000000000006000528760045286602452856044528460645260a0608452600060a45260008060c46000808d5af1806140a6573d15614068576020601f3d010460208604816003028183111561404f57818303600302610200838002858002030401015b5a602082011015614064573d6000803e3d6000fd5b5050505b7ff486bc8700000000000000000000000000000000000000000000000000000000600052896004528860245287604452866064528560845260a46000fd5b5060809290925260a05260c05260405250506000606052505050565b60006140cf836020015190565b9050818114613c1357613c1383612e37565b600060208851036141355750604080885260208089018a90527f4ce34aa200000000000000000000000000000000000000000000000000000000918901919091526044880152600160648801819052614144565b50606487018051600101908190525b603c60c082028901038781528660208201528560408201528460608201528360808201528260a082015250505050505050505050565b80600003613c77576040517f91b3e51400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141bd8361417a565b6141c781836140c2565b816141dd576141d886868686614712565b612c59565b612c598282600189898960008a6140e1565b6064810151604082019060c00260440161420a848383613d14565b50506020905250565b614271565b7f7fda72790000000000000000000000000000000000000000000000000000000060005260046000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602082018051518451811061428857614288614218565b60208102602086010151606081510151602084510151815181106142ae576142ae614218565b602081026020830101516000806020860151156142d5575050606081018051600090915280155b885183518152602084015160208201526040840151604082015260a084015160808201526060812060208c51028c015b808b10156143ac5760208b019a508a515199508d518a1061432857614328614218565b60208a0260208f010151985060208901511561430557606089510151975060208b51015196508751871061435e5761435e614218565b602087026020890101519550606086018051860181511587821060011b1786179550809650506000815250606086208214608084015160a088015114166143a7576143a7614218565b614305565b505060600182905280156143f457600181036143ec577f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b6143f4614242565b5050505050505050505050565b602082018051518451811061441857614418614218565b60208102602086010151805160408101516020855101518151811061443f5761443f614218565b60208102602083010151600080602087015115614466575050606081018051600090915280155b8951835181526020840151602082015260408401516040820152865160208c015261012087015160408c015260608120905060208c51028c015b808b10156145545760208b019a508a515199508d518a106144c3576144c3614218565b60208a0260208f01015198506020890151156144a057885197506040880151965060208b5101519550865186106144fc576144fc614218565b602086026020880101519450606085018051850181511586821060011b178517945080955050600081525060608520821460408d01516101208a01511460208e01518a5114161661454f5761454f614218565b6144a0565b50508160608b510152801561459d5760018103614595577f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b61459d614242565b505050505050505050505050565b3d15611820576020601f3d010460206040510481600302818311156145de57818303600302610200838002858002030401015b5a602082011015613c13573d6000803e3d6000fd5b604051602481018490523360448201526001600160a01b03831660648201526084810182905260009061464c9086907f0e1d31dc000000000000000000000000000000000000000000000000000000009060a401613927565b90506123f6818561466d565b6000806000835160208501865afa9392505050565b816146af5761467a6145ab565b6040517ffb5014fc00000000000000000000000000000000000000000000000000000000815260048101829052602401610e0c565b6146d87f0e1d31dc0000000000000000000000000000000000000000000000000000000061487f565b15612e5c576040517ffb5014fc00000000000000000000000000000000000000000000000000000000815260048101829052602401610e0c565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000600052836004528260245281604452602060006064600080895af1803d15601f3d116001600051141617163d1515811661486f5780873b15151661486f57806148415781614807573d156147c8576020601f3d01046020840481600302818311156147af57818303600302610200838002858002030401015b5a6020820110156147c4573d6000803e3d6000fd5b5050505b7ff486bc870000000000000000000000000000000000000000000000000000000060005286600452856024528460445260006064528360845260a46000fd5b7f98891923000000000000000000000000000000000000000000000000000000006000528660045285602452846044528360645260846000fd5b7f5f15d672000000000000000000000000000000000000000000000000000000006000528660045260246000fd5b5050604052505060006060525050565b60008060203d036148955760206000803e506000515b7fffffffff0000000000000000000000000000000000000000000000000000000090811692169190911415919050565b6040518060a001604052806148d86148f9565b81526000602082018190526040820152606080820181905260809091015290565b60405180610160016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081526020016000600381111561494657614946615065565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b6000815180845260005b8181101561499c57602081850181015186830182015201614980565b818111156149ae576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006105426020830184614976565b6000602082840312156149e857600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715614a4157614a416149ef565b60405290565b604051610160810167ffffffffffffffff81118282101715614a4157614a416149ef565b604051601f8201601f1916810167ffffffffffffffff81118282101715614a9457614a946149ef565b604052919050565b600067ffffffffffffffff821115614ab657614ab66149ef565b5060051b60200190565b6001600160a01b0381168114613c7757600080fd5b8035614ae081614ac0565b919050565b803560068110614ae057600080fd5b600060a08284031215614b0657600080fd5b614b0e614a1e565b9050614b1982614ae5565b81526020820135614b2981614ac0565b8060208301525060408201356040820152606082013560608201526080820135608082015292915050565b600082601f830112614b6557600080fd5b81356020614b7a614b7583614a9c565b614a6b565b82815260a09283028501820192828201919087851115614b9957600080fd5b8387015b85811015614bbc57614baf8982614af4565b8452928401928101614b9d565b5090979650505050505050565b600060c08284031215614bdb57600080fd5b60405160c0810181811067ffffffffffffffff82111715614bfe57614bfe6149ef565b604052905080614c0d83614ae5565b81526020830135614c1d81614ac0565b8060208301525060408301356040820152606083013560608201526080830135608082015260a0830135614c5081614ac0565b60a0919091015292915050565b600082601f830112614c6e57600080fd5b81356020614c7e614b7583614a9c565b82815260c09283028501820192828201919087851115614c9d57600080fd5b8387015b85811015614bbc57614cb38982614bc9565b8452928401928101614ca1565b803560048110614ae057600080fd5b60006101608284031215614ce257600080fd5b614cea614a47565b9050614cf582614ad5565b8152614d0360208301614ad5565b6020820152604082013567ffffffffffffffff80821115614d2357600080fd5b614d2f85838601614b54565b60408401526060840135915080821115614d4857600080fd5b50614d5584828501614c5d565b606083015250614d6760808301614cc0565b608082015260a082013560a082015260c082013560c082015260e082013560e082015261010080830135818301525061012080830135818301525061014080830135818301525092915050565b80356effffffffffffffffffffffffffffff81168114614ae057600080fd5b600082601f830112614de457600080fd5b813567ffffffffffffffff811115614dfe57614dfe6149ef565b614e116020601f19601f84011601614a6b565b818152846020838601011115614e2657600080fd5b816020850160208301376000918101602001919091529392505050565b600060a08284031215614e5557600080fd5b614e5d614a1e565b9050813567ffffffffffffffff80821115614e7757600080fd5b614e8385838601614ccf565b8352614e9160208501614db4565b6020840152614ea260408501614db4565b60408401526060840135915080821115614ebb57600080fd5b614ec785838601614dd3565b60608401526080840135915080821115614ee057600080fd5b50614eed84828501614dd3565b60808301525092915050565b600082601f830112614f0a57600080fd5b81356020614f1a614b7583614a9c565b82815260059290921b84018101918181019086841115614f3957600080fd5b8286015b84811015614f7957803567ffffffffffffffff811115614f5d5760008081fd5b614f6b8986838b0101614e43565b845250918301918301614f3d565b509695505050505050565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b8501011115614fc957600080fd5b9250929050565b600080600080600060608688031215614fe857600080fd5b853567ffffffffffffffff8082111561500057600080fd5b61500c89838a01614ef9565b9650602088013591508082111561502257600080fd5b61502e89838a01614f84565b9096509450604088013591508082111561504757600080fd5b5061505488828901614f84565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600681106150a4576150a4615065565b9052565b6150b3828251615094565b60208101516001600160a01b038082166020850152604083015160408501526060830151606085015280608084015116608085015250505050565b600081518084526020808501945080840160005b838110156151445781516151178882516150a8565b808401516001600160a01b031660a08901526040015160c088015260e09096019590820190600101615102565b509495945050505050565b60208152600061054260208301846150ee565b60006020828403121561517457600080fd5b813567ffffffffffffffff81111561518b57600080fd5b8201610160818503121561054257600080fd5b60008060008060008060008060008060e08b8d0312156151bd57600080fd5b8a3567ffffffffffffffff808211156151d557600080fd5b6151e18e838f01614ef9565b9b5060208d01359150808211156151f757600080fd5b6152038e838f01614f84565b909b50995060408d013591508082111561521c57600080fd5b6152288e838f01614f84565b909950975060608d013591508082111561524157600080fd5b5061524e8d828e01614f84565b90965094505060808b0135925061526760a08c01614ad5565b915060c08b013590509295989b9194979a5092959850565b604080825283519082018190526000906020906060840190828701845b828110156152ba57815115158452928401929084019060010161529c565b505050838103828501526152ce81866150ee565b9695505050505050565b600080602083850312156152eb57600080fd5b823567ffffffffffffffff81111561530257600080fd5b61530e85828601614f84565b90969095509350505050565b6000806000806040858703121561533057600080fd5b843567ffffffffffffffff8082111561534857600080fd5b61535488838901614f84565b9096509450602087013591508082111561536d57600080fd5b5061537a87828801614f84565b95989497509550505050565b6000806040838503121561539957600080fd5b823567ffffffffffffffff8111156153b057600080fd5b8301604081860312156153c257600080fd5b946020939093013593505050565b6000806000806000608086880312156153e857600080fd5b853567ffffffffffffffff8082111561540057600080fd5b9087019060a0828a03121561541457600080fd5b9095506020870135908082111561542a57600080fd5b5061543788828901614f84565b90955093505060408601359150606086013561545281614ac0565b809150509295509295909350565b60008060008060008060008060a0898b03121561547c57600080fd5b883567ffffffffffffffff8082111561549457600080fd5b6154a08c838d01614f84565b909a50985060208b01359150808211156154b957600080fd5b6154c58c838d01614f84565b909850965060408b01359150808211156154de57600080fd5b506154eb8b828c01614f84565b999c989b509699959896976060870135966080013595509350505050565b60006020828403121561551b57600080fd5b813561054281614ac0565b6060815260006155396060830186614976565b90508360208301526001600160a01b0383166040830152949350505050565b60006020828403121561556a57600080fd5b813567ffffffffffffffff81111561558157600080fd5b8201610240818503121561054257600080fd5b60006155a2614b7584614a9c565b83815260208082019190600586811b8601368111156155c057600080fd5b865b818110156156bc57803567ffffffffffffffff808211156155e35760008081fd5b818a01915060a082360312156155f95760008081fd5b615601614a1e565b8235815286830135600281106156175760008081fd5b818801526040838101359082015260608084013590820152608080840135838111156156435760008081fd5b939093019236601f85011261565a57600092508283fd5b8335925061566a614b7584614a9c565b83815292871b840188019288810190368511156156875760008081fd5b948901945b848610156156a55785358252948901949089019061568c565b9183019190915250885250509483019483016155c2565b5092979650505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156fe57600080fd5b83018035915067ffffffffffffffff82111561571957600080fd5b602001915060a081023603821315614fc957600080fd5b600060a0828403121561574257600080fd5b6105428383614af4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261578157600080fd5b83018035915067ffffffffffffffff82111561579c57600080fd5b602001915060c081023603821315614fc957600080fd5b600060c082840312156157c557600080fd5b6105428383614bc9565b6000602082840312156157e157600080fd5b61054282614cc0565b60006104ea3683614e43565b60006040828403121561580857600080fd5b6040516040810181811067ffffffffffffffff8211171561582b5761582b6149ef565b604052823581526020928301359281019290925250919050565b6000615853614b7584614a9c565b80848252602080830192508560051b85013681111561587157600080fd5b855b8181101561590c57803567ffffffffffffffff8111156158935760008081fd5b870136601f8201126158a55760008081fd5b80356158b3614b7582614a9c565b81815260069190911b820185019085810190368311156158d35760008081fd5b928601925b828410156158fc576158ea36856157f6565b825286820191506040840193506158d8565b8852505050938201938201615873565b50919695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261597b57600080fd5b9190910192915050565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea183360301811261597b57600080fd5b60006104ea3683614ccf565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126159fa57600080fd5b83018035915067ffffffffffffffff821115615a1557600080fd5b602001915036819003821315614fc957600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a5f57600080fd5b83018035915067ffffffffffffffff821115615a7a57600080fd5b6020019150600681901b3603821315614fc957600080fd5b60008219821115615aa557615aa5614242565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615ae257615ae2614242565b500290565b600081518084526020808501945080840160005b8381101561514457615b0e8783516150a8565b60a0969096019590820190600101615afb565b6000608080830187845260206001600160a01b03808916828701526040848188015283895180865260a089019150848b01955060005b81811015615b98578651615b6c848251615094565b808701518616848801528481015185850152606090810151908401529585019591870191600101615b57565b50508781036060890152615bac818a615ae7565b9c9b505050505050505050505050565b600082821015615bce57615bce614242565b500390565b600060408284031215615be557600080fd5b61054283836157f6565b600281106150a4576150a4615065565b602081016104ea8284615bef565b600081518084526020808501945080840160005b83811015615144578151615c36888251615094565b808401516001600160a01b03168885015260408082015190890152606080820151908901526080908101519088015260a09096019590820190600101615c21565b600081518084526020808501945080840160005b83811015615144578151615ca0888251615094565b808401516001600160a01b0390811689860152604080830151908a0152606080830151908a0152608080830151908a015260a091820151169088015260c09096019590820190600101615c8b565b600481106150a4576150a4615065565b600081518084526020808501945080840160005b8381101561514457815187529582019590820190600101615d12565b600081518084526020808501808196508360051b8101915082860160005b85811015615db2578284038952815160a08151865286820151615d7188880182615bef565b506040828101519087015260608083015190870152608091820151918601819052615d9e81870183615cfe565b9a87019a9550505090840190600101615d4c565b5091979650505050505050565b8581526001600160a01b038516602082015260a060408201526000610140855160a080850152615dfa82850182516001600160a01b03169052565b6020810151610160615e16818701836001600160a01b03169052565b604083015191508061018087015250615e336102a0860182615c0d565b905060608201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0868303016101a0870152615e6f8282615c77565b9150506080820151615e856101c0870182615cee565b5060a08201516101e086015260c082015161020086015260e082015161022086015261010080830151610240870152610120808401516102608801528484015161028088015260208a01519450615eef60c08801866effffffffffffffffffffffffffffff169052565b60408a01516effffffffffffffffffffffffffffff1660e088015260608a01518784037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60908101848a01529095509350615f498386614976565b945060808a015192508387860301818801525050615f678382614976565b925050508281036060840152615f7d8186615cfe565b90508281036080840152615f918185615d2e565b9897505050505050505056fea26469706673582212200b15a2c17ba420934ea8c476fda179746456d13a631f9f2d52f6019d946c1cd264736f6c634300080e0033000000000000000000000000b4b53a25d16376105f7db6f3c6f39ad8e64c41ec
Deployed Bytecode
0x6080604052600436106100e85760003560e01c8063a81744041161008a578063f07ec37311610059578063f07ec373146102a6578063f47b7740146102c6578063fb0f3ee1146102ea578063fd9f1e10146102fd57600080fd5b8063a81744041461025a578063b3a34c4c1461026d578063e7acab2414610280578063ed98a5741461029357600080fd5b80635b34b966116100c65780635b34b966146101c657806379df72bd146101e957806387201b4114610209578063881477321461022a57600080fd5b806306fdde03146100ed57806346423aa71461011857806355944a42146101a6575b600080fd5b3480156100f957600080fd5b5061010261031d565b60405161010f91906149c3565b60405180910390f35b34801561012457600080fd5b506101846101333660046149d6565b60009081526002602052604090205460ff808216926101008304909116916effffffffffffffffffffffffffffff620100008204811692710100000000000000000000000000000000009092041690565b604080519415158552921515602085015291830152606082015260800161010f565b6101b96101b4366004614fd0565b61032c565b60405161010f919061514f565b3480156101d257600080fd5b506101db61034f565b60405190815260200161010f565b3480156101f557600080fd5b506101db610204366004615162565b610359565b61021c61021736600461519e565b6104f0565b60405161010f92919061527f565b34801561023657600080fd5b5061024a6102453660046152d8565b610536565b604051901515815260200161010f565b6101b961026836600461531a565b610549565b61024a61027b366004615386565b6105c7565b61024a61028e3660046153d0565b610639565b61021c6102a1366004615460565b61066d565b3480156102b257600080fd5b506101db6102c1366004615509565b6106f7565b3480156102d257600080fd5b506102db610715565b60405161010f93929190615526565b61024a6102f8366004615558565b61072d565b34801561030957600080fd5b5061024a6103183660046152d8565b610738565b6060610327610744565b905090565b60606103438661033c8688615594565b858561075c565b90505b95945050505050565b6000610327610779565b6040805161016081019091526000906104ea908061037a6020860186615509565b6001600160a01b0316815260200184602001602081019061039b9190615509565b6001600160a01b031681526020016103b660408601866156c9565b808060200260200160405190810160405280939291908181526020016000905b82821015610402576103f360a08302860136819003810190615730565b815260200190600101906103d6565b505050918352505060200161041a606086018661574c565b808060200260200160405190810160405280939291908181526020016000905b828210156104665761045760c083028601368190038101906157b3565b8152602001906001019061043a565b505050918352505060200161048160a08601608087016157cf565b600381111561049257610492615065565b81526020018460a0013581526020018460c0013581526020018460e001358152602001846101000135815260200184610120013581526020018480606001906104db919061574c565b909152506101408401356107d6565b92915050565b6060806105238c6105018c8e615594565b8b8b8b8b8b6001600160a01b038c161561051b578b61051d565b335b8b61091c565b915091509a509a98505050505050505050565b6000610542838361095e565b9392505050565b60606105bc6105588686610ae1565b60408051600080825260208201909252906105b4565b6105a16040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b81526020019060019003908161056e5790505b50858561075c565b90505b949350505050565b60006105426105d584610b9d565b6040805160008082526020820190925290610631565b61061e6040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b8152602001906001900390816105eb5790505b508433610c58565b6000610343610647876157ea565b6106518688615594565b856001600160a01b038616156106675785610c58565b33610c58565b6060806106e661067d8b8b610ae1565b60408051600080825260208201909252906106d9565b6106c66040805160a081019091526000808252602082019081526020016000815260200160008152602001606081525090565b8152602001906001900390816106935790505b508a8a8a8a8a338b61091c565b915091509850989650505050505050565b6001600160a01b0381166000908152600160205260408120546104ea565b6060600080610722610d45565b925092509250909192565b60006104ea82610dc0565b600061054283836110b2565b6060602080526707536561706f727460475260606020f35b606061076e85856001885160006112ee565b6105bc858484611665565b60006107836117e4565b503360008181526001602081815260409283902080549092019182905591518181529092917f721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f910160405180910390a290565b610140820151604080519084015180516000939284927fa66999307ad1bb4fde44d13a5d710bd7718e0c87c1eef68a571629fbf5b93d0292602090910190845b81811015610843578251601f1901805186825260c082208652905260209384019390920191600101610816565b506020810260405120945050505060007f42d81c6929ffdc4eb27a0808e40e82516ad42296c166065de7f812492304ff6e9150604051602060608901510160005b868110156108b1578151601f1901805186825260e082208552905260209283019290910190600101610884565b505060408051602087029020601f198a0180517ffa445660b7e21515a59617fcd68910b487aa5808b8abda3d78bc85df364b2c2f8252928b01805197815260608c018051938152610140909c019a8b5261018082209390915295909552939097525050925250919050565b60608061092d8b8b600086886112ee565b61094c8b61093b8a8c615845565b610945898b615845565b8888611822565b909c909b509950505050505050505050565b60006109686117e4565b6000808084815b81811015610ad3573688888381811061098a5761098a615918565b905060200281019061099c9190615947565b9050366109a98280615985565b90506109b86020820182615509565b94506109cb6109c6826159b9565b611a6f565b600081815260026020526040812098509096506109ed90879089906001611aaa565b50865460ff16610ac957610a438587610a0960208601866159c5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611bd892505050565b86547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001178755610a7c6040820160208301615509565b6001600160a01b0316856001600160a01b03167ffde361574a066b44b3b5fe98a87108b7565e327327954c4faeea56a4e6491a0a88604051610ac091815260200190565b60405180910390a35b505060010161096f565b506001979650505050505050565b6060818067ffffffffffffffff811115610afd57610afd6149ef565b604051908082528060200260200182016040528015610b3657816020015b610b236148c5565b815260200190600190039081610b1b5790505b50915060005b81811015610b9557610b70858583818110610b5957610b59615918565b9050602002810190610b6b9190615947565b610b9d565b838281518110610b8257610b82615918565b6020908102919091010152600101610b3c565b505092915050565b610ba56148c5565b6040805160a0810190915280610bbb8480615985565b610bc4906159b9565b815260200160016effffffffffffffffffffffffffffff16815260200160016effffffffffffffffffffffffffffff168152602001838060200190610c0991906159c5565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509385525050604080516020818101909252928352909201525092915050565b6000610c62611c48565b60606000806000610c768989600187611c57565b604080516001808252818301909252939650919450925060009190816020015b610c9e6148c5565b815260200190600190039081610c965790505090508981600081518110610cc757610cc7615918565b6020026020010181905250610cdc818a612001565b600081600081518110610cf157610cf1615918565b6020026020010151600001519050610d0c8185858c8c6123fd565b610d2a85826000015183602001518b856040015186606001516125fd565b610d346001600055565b5060019a9950505050505050505050565b6060600080610d52612662565b6040805160038082528183019092529193507f000000000000000000000000b4b53a25d16376105f7db6f3c6f39ad8e64c41ec925060208201818036833750507f312e3100000000000000000000000000000000000000000000000000000000006020830152509391925090565b600061012435600281901c90600316600182113415811480610e15576040517fa61be9f00000000000000000000000000000000000000000000000000000000081523460048201526024015b60405180910390fd5b5060008060008060038711915060a082026024013593506002871460028811600289030201905060018101820260028615028801039250610e5a898783888888612758565b506101c46020820201356000856005811115610e7857610e78615065565b03610f345760208901803590610e8e908b615509565b6001600160a01b03161715610ecf576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f0483610ee360c08c0160a08d01615509565b610ef360808d0160608e01615509565b338d60c001358e60e0013587612a8e565b610f2f60408a0135610f1c60808c0160608d01615509565b610f2a6102008d018d615a2a565b612b74565b611099565b6040805160208082528183019092526000916020820181803683370190505090506002886005811115610f6957610f69615065565b03610fa857610fa3610f8160c08c0160a08d01615509565b610f9160808d0160608e01615509565b338d60c001358e60e001358787612c61565b611073565b6003886005811115610fbc57610fbc615065565b03610ff657610fa3610fd460c08c0160a08d01615509565b610fe460808d0160608e01615509565b338d60c001358e60e001358787612cc7565b600488600581111561100a5761100a615065565b0361104157610fa361101f60208c018c615509565b3361103060808e0160608f01615509565b8d602001358e604001358787612c61565b61107361105160208c018c615509565b3361106260808e0160608f01615509565b8d602001358e604001358787612cc7565b61108e61108660808c0160608d01615509565b8b8584612cfd565b61109781612e37565b505b6110a36001600055565b50600198975050505050505050565b60006110bc6117e4565b6000808084815b81811015610ad357368888838181106110de576110de615918565b90506020028101906110f09190615985565b90506110ff6020820182615509565b94506111116040820160208301615509565b9350336001600160a01b038616148015906111355750336001600160a01b03851614155b1561116c576040517f80ec737400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061125b604051806101600160405280886001600160a01b03168152602001876001600160a01b031681526020018480604001906111ab91906156c9565b808060200260200160405190810160405280939291908181526020016000905b828210156111f7576111e860a08302860136819003810190615730565b815260200190600101906111cb565b505050918352505060200161120f606086018661574c565b808060200260200160405190810160405280939291908181526020016000905b828210156104665761124c60c083028601368190038101906157b3565b8152602001906001019061122f565b6000818152600260205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661010017815590519098509091506001600160a01b0380871691908816907f6bacc01dbe442496068f7d234edd811f1a5f833243e0aec824f86ab861f3c90d906112dc9085815260200190565b60405180910390a350506001016110c3565b6112f6611c48565b845160008167ffffffffffffffff811115611313576113136149ef565b60405190808252806020026020018201604052801561133c578160200160208202803683370190505b506000808252909150601d6045823560e01c061160011b905b8381101561158357600089828151811061137157611371615918565b602002602001015190508660000361139657600060209091015260018101835261157b565b60008060006113a7848d8d8a611c57565b925092509250600185018752816000036113ce57505060006020909201919091525061157b565b828786815181106113e1576113e1615918565b6020908102919091010152835160a081015160c082015160409092015180517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909d019c91929160005b818110156114c757600083828151811061144757611447615918565b602002602001015190508051158c179b50600061146989898460800151612e60565b90508160800151826060015103611486576060820181905261149b565b61149589898460600151612e60565b60608301525b6080820181905260608201516114b5908289896000612ebb565b6060909201919091525060010161142b565b50875160600151805160005b8181101561156f5760008382815181106114ef576114ef615918565b60200260200101519050600061150a8b8b8460800151612e60565b90508160800151826060015103611527576060820181905261153c565b6115368b8b8460600151612e60565b60608301525b60808201819052606082015161155690828b8b6001612ebb565b60608301525060a08101516080909101526001016114d3565b50505050505050505050505b600101611355565b50806003036115be576040517f12d3f5a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115c88888612001565b60005b8381101561165a576000801b8382815181106115e9576115e9615918565b6020026020010151031561165257600089828151811061160b5761160b615918565b602002602001015160000151905061165084838151811061162e5761162e615918565b60200260200101518260000151836020015189856040015186606001516125fd565b505b6001016115cb565b505050505050505050565b6060818067ffffffffffffffff811115611681576116816149ef565b60405190808252806020026020018201604052801561170d57816020015b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e08301829052825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161169f5790505b5091506000805b828110156117c2573686868381811061172f5761172f615918565b90506020028101906117419190615947565b90506000611765896117538480615a2a565b6117606020870187615a2a565b612f10565b905080602001516001600160a01b03168160000151608001516001600160a01b031603611797578360010193506117b8565b8086858503815181106117ac576117ac615918565b60200260200101819052505b5050600101611714565b5080156117d0578083510383525b506117db8583613297565b50509392505050565b600160005414611820576040517f7fa8a98700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b8351835160609182916118358183615a92565b67ffffffffffffffff81111561184d5761184d6149ef565b6040519080825280602002602001820160405280156118d957816020015b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e08301829052825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161186b5790505b5092506000805b838110156119735760008a82815181106118fc576118fc615918565b6020026020010151905060006119168d6000848d8d6134ec565b905080602001516001600160a01b03168160000151608001516001600160a01b03160361194857836001019350611969565b80878585038151811061195d5761195d615918565b60200260200101819052505b50506001016118e0565b5060005b82811015611a0d57600089828151811061199357611993615918565b6020026020010151905060006119ae8d6001848d60006134ec565b905080602001516001600160a01b03168160000151608001516001600160a01b0316036119e057836001019350611a03565b80878588860103815181106119f7576119f7615918565b60200260200101819052505b5050600101611977565b508015611a1b578084510384525b508251600003611a57576040517fd5da9a1b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a618984613297565b935050509550959350505050565b6000611a858260600151518361014001516135e2565b81516001600160a01b03166000908152600160205260409020546104ea9083906107d6565b8254600090610100900460ff1615611aff578115611af7576040517f1a51557400000000000000000000000000000000000000000000000000000000815260048101869052602401610e0c565b5060006105bf565b83546201000090046effffffffffffffffffffffffffffff168015611bcc578315611b59576040517fee9e0e6300000000000000000000000000000000000000000000000000000000815260048101879052602401610e0c565b84547101000000000000000000000000000000000090046effffffffffffffffffffffffffffff168110611bcc578215611bc2576040517f10fda3e100000000000000000000000000000000000000000000000000000000815260048101879052602401610e0c565b60009150506105bf565b50600195945050505050565b336001600160a01b03841603611bed57505050565b6000611c35611bfa612662565b7f1901000000000000000000000000000000000000000000000000000000000000600090815260029190915260228581526042822091905290565b9050611c4284828461361c565b50505050565b611c506117e4565b6002600055565b60008060008087600001519050611c778160a001518260c0015188613835565b611c8b575060009250829150819050611ff7565b602088015160408901516effffffffffffffffffffffffffffff918216911680821180611cb6575081155b15611ced576040517f5a052b3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8082108015611d0157506080830151600116155b15611d38576040517fa11b63ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d4183611a6f565b9550611d638a8a89898760e00151886080015189600001518a60200151613894565b600086815260026020526040812090611d8090889083908c611aaa565b611d95575060009450849350611ff792505050565b805460ff16611db157611db18460000151888d60600151611bd8565b80546effffffffffffffffffffffffffffff620100008204811691710100000000000000000000000000000000009004168015611f795783600103611dfb57809450809350611e27565b838114611e2757611e0c8483615aaa565b9150611e188186615aaa565b9450611e248185615aaa565b93505b83611e328684615a92565b1115611e3e5781840394505b611e488583615a92565b91506effffffffffffffffffffffffffffff84116effffffffffffffffffffffffffffff83111715611f0257611e93565b60005b8215611e8d57908290069190611e7c565b50919050565b611ea6611ea08584611e79565b86611e79565b80150194859004949384900493909104906effffffffffffffffffffffffffffff8083119085111715611f02577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b82546effffffffffffffffffffffffffffff858116710100000000000000000000000000000000000270ffffffffffffffffffffffffffffffffff91851662010000027fffffffffffffffffffffffffffffff00000000000000000000000000000000009093169290921760011716178355611fec565b82546effffffffffffffffffffffffffffff858116710100000000000000000000000000000000000270ffffffffffffffffffffffffffffffffff91881662010000027fffffffffffffffffffffffffffffff000000000000000000000000000000000090931692909217600117161783555b509295509093505050505b9450945094915050565b8051825160005b828110156122b457600084828151811061202457612024615918565b60200260200101519050600081600001519050838110612070576040517f869586c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86818151811061208257612082615918565b6020026020010151602001516effffffffffffffffffffffffffffff166000036120ad5750506122ac565b60008782815181106120c1576120c1615918565b602090810291909101015151604084015190915060008080866020015160018111156120ef576120ef615065565b036121a257604084015180518410612133576040517fbfb3f8ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081858151811061214757612147615918565b60209081029190910101518051604082015190955093509050600484146003038181600581111561217a5761217a615065565b9081600581111561218d5761218d615065565b9052505060608801516040909101525061224c565b6060840151805184106121e1576040517f6088d7de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008185815181106121f5576121f5615918565b60209081029190910101518051604082015190955093509050600484146003038181600581111561222857612228615065565b9081600581111561223b5761223b615065565b905250506060880151604090910152505b6122568260031090565b61228c576040517f94eb6af600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80156122a5576122a586606001518288608001516139a1565b5050505050505b600101612008565b5060005b818110156123f65760008582815181106122d4576122d4615918565b6020026020010151905080602001516effffffffffffffffffffffffffffff1660000361230157506123ee565b805160608101515160005b8181101561237e5761233f8360600151828151811061232d5761232d615918565b60200260200101516000015160031090565b15612376576040517fff75a34000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60010161230c565b505060408101515160005b818110156123e9576123aa8360400151828151811061232d5761232d615918565b156123e1576040517fa6cfc67300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101612389565b505050505b6001016122b8565b5050505050565b60a085015160c0860151604080516020808252818301909252600091602082018180368337505050604089015151909150613a1d9060005b818110156125045760008b60400151828151811061245557612455615918565b602002602001015190506000600581111561247257612472615065565b8151600581111561248557612485615065565b036124bc576040517f12d3f5a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006124d6826060015183608001518e8e8c8c6000613b61565b606083015250608081018890528b516101208d01516124fb9183918863ffffffff8916565b50600101612435565b5050506060880151513490613a1d9060005b818110156125e15760008c60600151828151811061253657612536615918565b60200260200101519050600061255a826060015183608001518f8f8d8d6001613b61565b6060830181905260a08301516080840152905060008251600581111561258257612582615065565b036125c757858111156125c1576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80860395505b6125d782338d8a8963ffffffff16565b5050600101612516565b5050506125ed82612e37565b801561165a5761165a3382613bab565b60608290506060829050856001600160a01b0316876001600160a01b03167f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f318a8886866040516126509493929190615b21565b60405180910390a35050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000a4b1461461273357610327604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f32b5c112df393a49218d7552f96b2eeb829dfb4272f4f24eef510a586b85feef918101919091527f722c0e0c80487266e8c6a45e3a1a803aab23378a9c32e6ebe029d4fad7bfc96560608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b507faeaa18c6cc025b04c1f633a43805680a2b342d79f2d314efc3f2af8866bfd87b90565b612760611c48565b6127768661012001358761014001356001613835565b5061277f613c18565b61279d612790610200880188615a2a565b9050876101e001356135e2565b6000807f42d81c6929ffdc4eb27a0808e40e82516ad42296c166065de7f812492304ff6e9050806080528560a0526060602460c037604060646101203760e06080206101605261026435602081026102a0016001610264350181526020810190508781526080602460208301376101608760a0528660c052600060e05261020435925060005b8381101561286e578060400261028401602081610100376040816101203760208301925060e0608020835260a084019350898452886020850152604081606086013750600101612823565b60206001850102610160206060526102643593505b838110156128b4578060400261028401915060a0830192508883528760208401526040826060850137600101612883565b505050505060007fa66999307ad1bb4fde44d13a5d710bd7718e0c87c1eef68a571629fbf5b93d029050806080528260a052606060c460c03760206101046101203760c0608020600052602060002060e052602061026435026102000160018152836020820152606060c4604083013750506084356001600160a01b0381166000908152600160205260408120547ffa445660b7e21515a59617fcd68910b487aa5808b8abda3d78bc85df364b2c2f60808190529091506040608460a03760605161010052886101205260a061014461014037816101e0526101806080209350505050602061026435026101800181815233602082015260806040820152610120606082015260a061026435026101e00160a4356084357f9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f318385a350506000606052612a2981886101600135888a6060016020810190612a149190615509565b612a2460a08d0160808e01615509565b613c7a565b612a8581612a3d60808a0160608b01615509565b612a4b6102208b018b6159c5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613cca92505050565b50505050505050565b8015612b0357600060405190507f4ce34aa200000000000000000000000000000000000000000000000000000000815260206004820152600160248201528760448201528660648201528560848201528460a48201528360c48201528260e4820152612afd8282610104613d14565b50612a85565b6002876005811115612b1757612b17615065565b03612b675781600114612b56576040517fefcc00b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6286868686613e81565b612a85565b612a858686868686613f90565b348160005b81811015612c005736858583818110612b9457612b94615918565b60400291909101915050803584811115612bda576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bf3612bed6040840160208501615509565b82613bab565b9093039250600101612b79565b5081861115612c3b576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c458587613bab565b85821115612c5957612c5933878403613bab565b505050505050565b612c6b81836140c2565b81612cb65782600114612caa576040517fefcc00b100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6287878787613e81565b612a85828260028a8a8a8a8a6140e1565b612cd08361417a565b612cda81836140c2565b81612cec57612b628787878787613f90565b612a85828260038a8a8a8a8a6140e1565b60008060008060008615612d3457889450339350612d2160c0890160a08a01615509565b9250505060e086013560c0870135612d56565b339450889350612d476020890189615509565b92505050604086013560208701355b8015612d8e576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50602086026101e403356000612da86102008a018a615a2a565b9050905060005b81811015612e1c5736612dc66102008c018c615a2a565b83818110612dd657612dd6615918565b6040029190910191505080358a15612df557612df28187615bbc565b95505b612e12878a612e0a6040860160208701615509565b84898f6141b4565b5050600101612daf565b50612e2b84878786868c6141b4565b50505050505050505050565b6040815114612e435750565b6000612e50826020015190565b9050612e5c81836141ef565b5050565b6000828403612e70575080610542565b8284830915612ea3577fc63cf0890000000000000000000000000000000000000000000000000000000060005260046000fd5b6000612eaf8584615aaa565b93909304949350505050565b6000848614612f0657838303428590038082036000612eda838a615aaa565b612ee4838c615aaa565b612eee9190615a92565b90508584878303040181151502945050505050610346565b5092949350505050565b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e0830182905282526020820181905291810191909152831580612f5b575081155b15612f92576040517f98e9db6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e083018290528252602082018190529181019190915261302f878585808060200260200160405190810160405280939291908181526020016000905b828210156130245761301560408302860136819003810190615bd3565b81526020019060010190612ff8565b505050505083614213565b80516040805160208089028201810190925287815261308e918a91908a908a90819060009085015b828210156130835761307460408302860136819003810190615bd3565b81526020019060010190613057565b505050505085614401565b805160058111156130a1576130a1615065565b83515160058111156130b5576130b5615065565b1415806130e0575080602001516001600160a01b03168360000151602001516001600160a01b031614155b806130f75750806040015183600001516040015114155b1561312e576040517f09cfb45500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826000015160600151816060015111156131dc5760008585600081811061315757613157615918565b90506040020180360381019061316d9190615bd3565b90508360000151606001518260600151038982600001518151811061319457613194615918565b602002602001015160000151606001518260200151815181106131b9576131b9615918565b602090810291909101015160609081019190915284518101519083015250613277565b6000878760008181106131f1576131f1615918565b9050604002018036038101906132079190615bd3565b90508160600151846000015160600151038982600001518151811061322e5761322e615918565b6020026020010151600001516040015182602001518151811061325357613253615918565b60200260200101516060018181525050816060015184600001516060018181525050505b60809081015183516001600160a01b039091169101525095945050505050565b81516060908067ffffffffffffffff8111156132b5576132b56149ef565b6040519080825280602002602001820160405280156132de578160200160208202803683370190505b50915060005b818110156133e757600085828151811061330057613300615918565b6020026020010151905080602001516effffffffffffffffffffffffffffff1660000361332d57506133df565b600184838151811061334157613341615918565b91151560209283029190910190910152805160600151805160005b818110156133da57600083828151811061337857613378615918565b6020026020010151606001519050806000146133d1576040517fa5f54208000000000000000000000000000000000000000000000000000000008152600481018790526024810183905260448101829052606401610e0c565b5060010161335c565b505050505b6001016132e4565b5060408051602080825281830190925234916000919060208201818036833701905050855190915060005b818110156134be57600087828151811061342e5761342e615918565b6020908102919091010151805190915060008151600581111561345357613453615065565b036134a0578581606001511115613496576040517f1a783b8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151860395505b6134b4818360200151846040015188613a1d565b5050600101613412565b506134c882612e37565b82156134d8576134d83384613bab565b6134e26001600055565b5050505092915050565b60408051610100810182526000606082018181526080830182905260a0830182905260c0830182905260e0830182905282526020820181905291810191909152835160000361356957846040517f375c24c1000000000000000000000000000000000000000000000000000000008152600401610e0c9190615bff565b600085600181111561357d5761357d615065565b036135a45780516001600160a01b03831660809091015261359f868583614401565b6135bd565b6135af868583614213565b336020820152604081018390525b8051606001516000036103465760006020820181905281516080015295945050505050565b80821015612e5c576040517f466aa61600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060005260008251602084038051826041036000600182116136a3576040880151606089015160001a9650821561368157601b8160ff1c0196507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660408a01525b8689528985526020600060808760015afa508385528589526040890152506000515b89148915151695508590506137fa57604082526044860380516040880380517f1626ba7e0000000000000000000000000000000000000000000000000000000084528a82526020600060648901868f5afa985088156137f0577f1626ba7e00000000000000000000000000000000000000000000000000000000600051146137f0578b3b15613756577f4f7fb80d0000000000000000000000000000000000000000000000000000000060005260046000fd5b600187604103111561378c577f8baa579f0000000000000000000000000000000000000000000000000000000060005260046000fd5b640101000000881a6137c6577f1f003d0a000000000000000000000000000000000000000000000000000000006000528760045260246000fd5b7f815e1d640000000000000000000000000000000000000000000000000000000060005260046000fd5b8486529190925290525b5050505080611c425761380b6145ab565b7f4f7fb80d0000000000000000000000000000000000000000000000000000000060005260046000fd5b6000428411806138455750428311155b1561388a578115613882576040517f6f7eac2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506000610542565b5060019392505050565b60018360038111156138a8576138a8615065565b1180156138be5750336001600160a01b03821614155b80156138d35750336001600160a01b03831614155b15613997576080880151511580156138ea57508651155b15613900576138fb818684876145f3565b613997565b600061398b82633313157060e01b88338d8c8e604051602401613927959493929190615dbf565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614658565b905061165a818761466d565b5050505050505050565b600083600052602060002060208301835160051b81015b808210156139e057815180841160051b938452602093841852604060002092909101906139b8565b50508314905080611c42576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084516005811115613a3257613a32615065565b03613a9957604084015160208501516001600160a01b03161715613a82576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613a9484608001518560600151613bab565b611c42565b600184516005811115613aae57613aae615065565b03613b0957604084015115613aef576040517f6ab37ce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613a948460200151848660800151876060015186866141b4565b600284516005811115613b1e57613b1e615065565b03613b4257613a948460200151848660800151876040015188606001518787612c61565b611c428460200151848660800151876040015188606001518787612cc7565b6000868803613b7c57613b75868689612e60565b9050613ba0565b613b9d613b8a87878b612e60565b613b9588888b612e60565b868686612ebb565b90505b979650505050505050565b613bb48161417a565b600080600080600085875af1905080613c1357613bcf6145ab565b6040517f470c7c1d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101839052604401610e0c565b505050565b6018610124351061024435610260610264356040020114600435602014610224356102401416161680613c77576040517f39f3e3fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b6001836003811115613c8e57613c8e615065565b118015613ca45750336001600160a01b03821614155b8015613cb95750336001600160a01b03831614155b156123f6576123f6818684876145f3565b6000838152600260205260409020613ce58482600180611aaa565b50805460ff16613cfa57613cfa838584611bd8565b710100000000000000000000000000000100019055505050565b604080517f000000000000000000000000b4b53a25d16376105f7db6f3c6f39ad8e64c41ec74ff000000000000000000000000000000000000000017600090815260208690527feaac5702377b8965e2d398fbf4db18a8dcb71133ee9ff60ab0d41c36d3c4470383526055600b20919092526001600160a01b03169050600080600080526020600085876000875af19150600051905081613df457613db76145ab565b6040517fd13d53d40000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610e0c565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f4ce34aa20000000000000000000000000000000000000000000000000000000014612c59576040517f1cf99b26000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0384166024820152604401610e0c565b833b613eb5577f5f15d672000000000000000000000000000000000000000000000000000000006000528360045260246000fd5b6040517f23b872dd000000000000000000000000000000000000000000000000000000006000528360045282602452816044526000806064600080895af180613f81573d15613f42576020601f3d0104602083048160030281831115613f2957818303600302610200838002858002030401015b5a602082011015613f3e573d6000803e3d6000fd5b5050505b7ff486bc870000000000000000000000000000000000000000000000000000000060005285600452846024528360445282606452600160845260a46000fd5b50604052505060006060525050565b843b613fc4577f5f15d672000000000000000000000000000000000000000000000000000000006000528460045260246000fd5b60405160805160a05160c0517ff242432a000000000000000000000000000000000000000000000000000000006000528760045286602452856044528460645260a0608452600060a45260008060c46000808d5af1806140a6573d15614068576020601f3d010460208604816003028183111561404f57818303600302610200838002858002030401015b5a602082011015614064573d6000803e3d6000fd5b5050505b7ff486bc8700000000000000000000000000000000000000000000000000000000600052896004528860245287604452866064528560845260a46000fd5b5060809290925260a05260c05260405250506000606052505050565b60006140cf836020015190565b9050818114613c1357613c1383612e37565b600060208851036141355750604080885260208089018a90527f4ce34aa200000000000000000000000000000000000000000000000000000000918901919091526044880152600160648801819052614144565b50606487018051600101908190525b603c60c082028901038781528660208201528560408201528460608201528360808201528260a082015250505050505050505050565b80600003613c77576040517f91b3e51400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6141bd8361417a565b6141c781836140c2565b816141dd576141d886868686614712565b612c59565b612c598282600189898960008a6140e1565b6064810151604082019060c00260440161420a848383613d14565b50506020905250565b614271565b7f7fda72790000000000000000000000000000000000000000000000000000000060005260046000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602082018051518451811061428857614288614218565b60208102602086010151606081510151602084510151815181106142ae576142ae614218565b602081026020830101516000806020860151156142d5575050606081018051600090915280155b885183518152602084015160208201526040840151604082015260a084015160808201526060812060208c51028c015b808b10156143ac5760208b019a508a515199508d518a1061432857614328614218565b60208a0260208f010151985060208901511561430557606089510151975060208b51015196508751871061435e5761435e614218565b602087026020890101519550606086018051860181511587821060011b1786179550809650506000815250606086208214608084015160a088015114166143a7576143a7614218565b614305565b505060600182905280156143f457600181036143ec577f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b6143f4614242565b5050505050505050505050565b602082018051518451811061441857614418614218565b60208102602086010151805160408101516020855101518151811061443f5761443f614218565b60208102602083010151600080602087015115614466575050606081018051600090915280155b8951835181526020840151602082015260408401516040820152865160208c015261012087015160408c015260608120905060208c51028c015b808b10156145545760208b019a508a515199508d518a106144c3576144c3614218565b60208a0260208f01015198506020890151156144a057885197506040880151965060208b5101519550865186106144fc576144fc614218565b602086026020880101519450606085018051850181511586821060011b178517945080955050600081525060608520821460408d01516101208a01511460208e01518a5114161661454f5761454f614218565b6144a0565b50508160608b510152801561459d5760018103614595577f91b3e5140000000000000000000000000000000000000000000000000000000060005260046000fd5b61459d614242565b505050505050505050505050565b3d15611820576020601f3d010460206040510481600302818311156145de57818303600302610200838002858002030401015b5a602082011015613c13573d6000803e3d6000fd5b604051602481018490523360448201526001600160a01b03831660648201526084810182905260009061464c9086907f0e1d31dc000000000000000000000000000000000000000000000000000000009060a401613927565b90506123f6818561466d565b6000806000835160208501865afa9392505050565b816146af5761467a6145ab565b6040517ffb5014fc00000000000000000000000000000000000000000000000000000000815260048101829052602401610e0c565b6146d87f0e1d31dc0000000000000000000000000000000000000000000000000000000061487f565b15612e5c576040517ffb5014fc00000000000000000000000000000000000000000000000000000000815260048101829052602401610e0c565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000600052836004528260245281604452602060006064600080895af1803d15601f3d116001600051141617163d1515811661486f5780873b15151661486f57806148415781614807573d156147c8576020601f3d01046020840481600302818311156147af57818303600302610200838002858002030401015b5a6020820110156147c4573d6000803e3d6000fd5b5050505b7ff486bc870000000000000000000000000000000000000000000000000000000060005286600452856024528460445260006064528360845260a46000fd5b7f98891923000000000000000000000000000000000000000000000000000000006000528660045285602452846044528360645260846000fd5b7f5f15d672000000000000000000000000000000000000000000000000000000006000528660045260246000fd5b5050604052505060006060525050565b60008060203d036148955760206000803e506000515b7fffffffff0000000000000000000000000000000000000000000000000000000090811692169190911415919050565b6040518060a001604052806148d86148f9565b81526000602082018190526040820152606080820181905260809091015290565b60405180610160016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081526020016000600381111561494657614946615065565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b6000815180845260005b8181101561499c57602081850181015186830182015201614980565b818111156149ae576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006105426020830184614976565b6000602082840312156149e857600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715614a4157614a416149ef565b60405290565b604051610160810167ffffffffffffffff81118282101715614a4157614a416149ef565b604051601f8201601f1916810167ffffffffffffffff81118282101715614a9457614a946149ef565b604052919050565b600067ffffffffffffffff821115614ab657614ab66149ef565b5060051b60200190565b6001600160a01b0381168114613c7757600080fd5b8035614ae081614ac0565b919050565b803560068110614ae057600080fd5b600060a08284031215614b0657600080fd5b614b0e614a1e565b9050614b1982614ae5565b81526020820135614b2981614ac0565b8060208301525060408201356040820152606082013560608201526080820135608082015292915050565b600082601f830112614b6557600080fd5b81356020614b7a614b7583614a9c565b614a6b565b82815260a09283028501820192828201919087851115614b9957600080fd5b8387015b85811015614bbc57614baf8982614af4565b8452928401928101614b9d565b5090979650505050505050565b600060c08284031215614bdb57600080fd5b60405160c0810181811067ffffffffffffffff82111715614bfe57614bfe6149ef565b604052905080614c0d83614ae5565b81526020830135614c1d81614ac0565b8060208301525060408301356040820152606083013560608201526080830135608082015260a0830135614c5081614ac0565b60a0919091015292915050565b600082601f830112614c6e57600080fd5b81356020614c7e614b7583614a9c565b82815260c09283028501820192828201919087851115614c9d57600080fd5b8387015b85811015614bbc57614cb38982614bc9565b8452928401928101614ca1565b803560048110614ae057600080fd5b60006101608284031215614ce257600080fd5b614cea614a47565b9050614cf582614ad5565b8152614d0360208301614ad5565b6020820152604082013567ffffffffffffffff80821115614d2357600080fd5b614d2f85838601614b54565b60408401526060840135915080821115614d4857600080fd5b50614d5584828501614c5d565b606083015250614d6760808301614cc0565b608082015260a082013560a082015260c082013560c082015260e082013560e082015261010080830135818301525061012080830135818301525061014080830135818301525092915050565b80356effffffffffffffffffffffffffffff81168114614ae057600080fd5b600082601f830112614de457600080fd5b813567ffffffffffffffff811115614dfe57614dfe6149ef565b614e116020601f19601f84011601614a6b565b818152846020838601011115614e2657600080fd5b816020850160208301376000918101602001919091529392505050565b600060a08284031215614e5557600080fd5b614e5d614a1e565b9050813567ffffffffffffffff80821115614e7757600080fd5b614e8385838601614ccf565b8352614e9160208501614db4565b6020840152614ea260408501614db4565b60408401526060840135915080821115614ebb57600080fd5b614ec785838601614dd3565b60608401526080840135915080821115614ee057600080fd5b50614eed84828501614dd3565b60808301525092915050565b600082601f830112614f0a57600080fd5b81356020614f1a614b7583614a9c565b82815260059290921b84018101918181019086841115614f3957600080fd5b8286015b84811015614f7957803567ffffffffffffffff811115614f5d5760008081fd5b614f6b8986838b0101614e43565b845250918301918301614f3d565b509695505050505050565b60008083601f840112614f9657600080fd5b50813567ffffffffffffffff811115614fae57600080fd5b6020830191508360208260051b8501011115614fc957600080fd5b9250929050565b600080600080600060608688031215614fe857600080fd5b853567ffffffffffffffff8082111561500057600080fd5b61500c89838a01614ef9565b9650602088013591508082111561502257600080fd5b61502e89838a01614f84565b9096509450604088013591508082111561504757600080fd5b5061505488828901614f84565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600681106150a4576150a4615065565b9052565b6150b3828251615094565b60208101516001600160a01b038082166020850152604083015160408501526060830151606085015280608084015116608085015250505050565b600081518084526020808501945080840160005b838110156151445781516151178882516150a8565b808401516001600160a01b031660a08901526040015160c088015260e09096019590820190600101615102565b509495945050505050565b60208152600061054260208301846150ee565b60006020828403121561517457600080fd5b813567ffffffffffffffff81111561518b57600080fd5b8201610160818503121561054257600080fd5b60008060008060008060008060008060e08b8d0312156151bd57600080fd5b8a3567ffffffffffffffff808211156151d557600080fd5b6151e18e838f01614ef9565b9b5060208d01359150808211156151f757600080fd5b6152038e838f01614f84565b909b50995060408d013591508082111561521c57600080fd5b6152288e838f01614f84565b909950975060608d013591508082111561524157600080fd5b5061524e8d828e01614f84565b90965094505060808b0135925061526760a08c01614ad5565b915060c08b013590509295989b9194979a5092959850565b604080825283519082018190526000906020906060840190828701845b828110156152ba57815115158452928401929084019060010161529c565b505050838103828501526152ce81866150ee565b9695505050505050565b600080602083850312156152eb57600080fd5b823567ffffffffffffffff81111561530257600080fd5b61530e85828601614f84565b90969095509350505050565b6000806000806040858703121561533057600080fd5b843567ffffffffffffffff8082111561534857600080fd5b61535488838901614f84565b9096509450602087013591508082111561536d57600080fd5b5061537a87828801614f84565b95989497509550505050565b6000806040838503121561539957600080fd5b823567ffffffffffffffff8111156153b057600080fd5b8301604081860312156153c257600080fd5b946020939093013593505050565b6000806000806000608086880312156153e857600080fd5b853567ffffffffffffffff8082111561540057600080fd5b9087019060a0828a03121561541457600080fd5b9095506020870135908082111561542a57600080fd5b5061543788828901614f84565b90955093505060408601359150606086013561545281614ac0565b809150509295509295909350565b60008060008060008060008060a0898b03121561547c57600080fd5b883567ffffffffffffffff8082111561549457600080fd5b6154a08c838d01614f84565b909a50985060208b01359150808211156154b957600080fd5b6154c58c838d01614f84565b909850965060408b01359150808211156154de57600080fd5b506154eb8b828c01614f84565b999c989b509699959896976060870135966080013595509350505050565b60006020828403121561551b57600080fd5b813561054281614ac0565b6060815260006155396060830186614976565b90508360208301526001600160a01b0383166040830152949350505050565b60006020828403121561556a57600080fd5b813567ffffffffffffffff81111561558157600080fd5b8201610240818503121561054257600080fd5b60006155a2614b7584614a9c565b83815260208082019190600586811b8601368111156155c057600080fd5b865b818110156156bc57803567ffffffffffffffff808211156155e35760008081fd5b818a01915060a082360312156155f95760008081fd5b615601614a1e565b8235815286830135600281106156175760008081fd5b818801526040838101359082015260608084013590820152608080840135838111156156435760008081fd5b939093019236601f85011261565a57600092508283fd5b8335925061566a614b7584614a9c565b83815292871b840188019288810190368511156156875760008081fd5b948901945b848610156156a55785358252948901949089019061568c565b9183019190915250885250509483019483016155c2565b5092979650505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156fe57600080fd5b83018035915067ffffffffffffffff82111561571957600080fd5b602001915060a081023603821315614fc957600080fd5b600060a0828403121561574257600080fd5b6105428383614af4565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261578157600080fd5b83018035915067ffffffffffffffff82111561579c57600080fd5b602001915060c081023603821315614fc957600080fd5b600060c082840312156157c557600080fd5b6105428383614bc9565b6000602082840312156157e157600080fd5b61054282614cc0565b60006104ea3683614e43565b60006040828403121561580857600080fd5b6040516040810181811067ffffffffffffffff8211171561582b5761582b6149ef565b604052823581526020928301359281019290925250919050565b6000615853614b7584614a9c565b80848252602080830192508560051b85013681111561587157600080fd5b855b8181101561590c57803567ffffffffffffffff8111156158935760008081fd5b870136601f8201126158a55760008081fd5b80356158b3614b7582614a9c565b81815260069190911b820185019085810190368311156158d35760008081fd5b928601925b828410156158fc576158ea36856157f6565b825286820191506040840193506158d8565b8852505050938201938201615873565b50919695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261597b57600080fd5b9190910192915050565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea183360301811261597b57600080fd5b60006104ea3683614ccf565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126159fa57600080fd5b83018035915067ffffffffffffffff821115615a1557600080fd5b602001915036819003821315614fc957600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a5f57600080fd5b83018035915067ffffffffffffffff821115615a7a57600080fd5b6020019150600681901b3603821315614fc957600080fd5b60008219821115615aa557615aa5614242565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615ae257615ae2614242565b500290565b600081518084526020808501945080840160005b8381101561514457615b0e8783516150a8565b60a0969096019590820190600101615afb565b6000608080830187845260206001600160a01b03808916828701526040848188015283895180865260a089019150848b01955060005b81811015615b98578651615b6c848251615094565b808701518616848801528481015185850152606090810151908401529585019591870191600101615b57565b50508781036060890152615bac818a615ae7565b9c9b505050505050505050505050565b600082821015615bce57615bce614242565b500390565b600060408284031215615be557600080fd5b61054283836157f6565b600281106150a4576150a4615065565b602081016104ea8284615bef565b600081518084526020808501945080840160005b83811015615144578151615c36888251615094565b808401516001600160a01b03168885015260408082015190890152606080820151908901526080908101519088015260a09096019590820190600101615c21565b600081518084526020808501945080840160005b83811015615144578151615ca0888251615094565b808401516001600160a01b0390811689860152604080830151908a0152606080830151908a0152608080830151908a015260a091820151169088015260c09096019590820190600101615c8b565b600481106150a4576150a4615065565b600081518084526020808501945080840160005b8381101561514457815187529582019590820190600101615d12565b600081518084526020808501808196508360051b8101915082860160005b85811015615db2578284038952815160a08151865286820151615d7188880182615bef565b506040828101519087015260608083015190870152608091820151918601819052615d9e81870183615cfe565b9a87019a9550505090840190600101615d4c565b5091979650505050505050565b8581526001600160a01b038516602082015260a060408201526000610140855160a080850152615dfa82850182516001600160a01b03169052565b6020810151610160615e16818701836001600160a01b03169052565b604083015191508061018087015250615e336102a0860182615c0d565b905060608201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0868303016101a0870152615e6f8282615c77565b9150506080820151615e856101c0870182615cee565b5060a08201516101e086015260c082015161020086015260e082015161022086015261010080830151610240870152610120808401516102608801528484015161028088015260208a01519450615eef60c08801866effffffffffffffffffffffffffffff169052565b60408a01516effffffffffffffffffffffffffffff1660e088015260608a01518784037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60908101848a01529095509350615f498386614976565b945060808a015192508387860301818801525050615f678382614976565b925050508281036060840152615f7d8186615cfe565b90508281036080840152615f918185615d2e565b9897505050505050505056fea26469706673582212200b15a2c17ba420934ea8c476fda179746456d13a631f9f2d52f6019d946c1cd264736f6c634300080e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b4b53a25d16376105f7db6f3c6f39ad8e64c41ec
-----Decoded View---------------
Arg [0] : conduitController (address): 0xB4b53A25d16376105f7db6F3c6f39Ad8e64C41EC
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b4b53a25d16376105f7db6f3c6f39ad8e64c41ec
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.