Skip to content

Commit

Permalink
Merge pull request #60 from ProjectOpenSea/green
Browse files Browse the repository at this point in the history
Optimize calldata and memory access for basic orders
  • Loading branch information
0age authored Apr 5, 2022
2 parents b860c15 + 1253a6b commit cc0357b
Show file tree
Hide file tree
Showing 9 changed files with 991 additions and 1,145 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Each order contains nine key components:
- The `identifierOrCriteria` represents either the ERC721 or ERC1155 token identifier or, in the case of a criteria-based item type, a merkle root composed of the valid set of token identifiers for the item. This value will always be zero for Ether and ERC20 item types, and can optionally be zero for criteria-based item types to allow for any identifier.
- The `startAmount` represents the amount of the item in question that will be required should the order be fulfilled at the moment the order becomes active.
- The `endAmount` represents the amount of the item in question that will be required should the order be fulfilled at the moment the order expires. If this value differs from the item's `startAmount`, the realized amount is calculated linearly based on the time elapsed since the order became active.
- The `consideration` contains an array of items that must be received in order to fulfill the order. It contains all of the same components as an offered item, and additionally includes a `recipient` that will receive each item.
- The `consideration` contains an array of items that must be received in order to fulfill the order. It contains all of the same components as an offered item, and additionally includes a `recipient` that will receive each item. This array may be extended by the fulfiller on order fulfillment so as to support "tipping" (e.g. relayer or referral payments).
- The `orderType` designates one of eight types for the order depending on three distinct preferences:
- `FULL` indicates that the order does not support partial fills, whereas `PARTIAL` enables filling some fraction of the order, with the important caveat that each item must be cleanly divisible by the supplied fraction (i.e. no remainder after division).
- `OPEN` indicates that the call to execute the order can be submitted by any account, whereas `RESTRICTED` requires that the order can only be executed by either the offerer or the zone of the order.
Expand Down Expand Up @@ -149,14 +149,14 @@ When matching a group of orders via `matchOrders` or `matchAdvancedOrders`, step
- As all items on orders supporting partial fills must be "cleanly divisible" when performing a partial fill, orders with multiple items should to be constructed with care. A straightforward heuristic is to start with a "unit" bundle (e.g. 1 NFT item A, 3 NFT item B, and 5 NFT item C for 2 ETH) then applying a multiple to that unit bundle (e.g. 7 of those units results in a partial order for 7 NFT item A, 21 NFT item B, and 35 NFT item C for 14 ETH).
- As Ether cannot be "taken" from an account, any order that contains Ether or other native tokens as an offer item (including "implied" mirror orders) must be supplied by the caller executing the order(s) as msg.value. This also explains why there are no `fulfillBasicERC721ForEthOrder` and `fulfillBasicERC1155ForEthOrder` functions, as Ether cannot be taken from the offerer in these cases. One important takeaway from this mechanic is that, technically, anyone can supply Ether on behalf of a given offerer (whereas the offerer themselves must supply all other items).
- As restricted orders must be fulfilled by the named zone, multiple restricted orders with differing zones cannot be composed via `matchOrders` — they must either be fulfilled independently or make use of a shared zone that routes orders in accordance with the requirements of each original zone.
- As extensions to the consideration array on fulfillment (i.e. "tipping") can be arbitrarily set by the caller, fulfillments where all matched orders have already been signed for or validated can be frontrun on submission, with the frontrunner modifying any tips. Therefore, it is important that orders fulfilled in this manner either leverage "restricted" order types that route through a zone that enforces appropriate allocation of consideration extensions, or that each offer item is fully spent and each consideration item is appropriately declared on order creation.
- As each consideration item on each order fulfilled via `matchOrders` must be met in order for the group of orders to be matchable, any failing transfer will cause the entire group to fail. Two workarounds are available in cases where failing order fulfillment should not prevent the rest of the desired orders from being successfully fulfilled:
- The orders can be broken up into distinct calls and submitted via a multicall contract or flashbots bundle. This method does not allow for cross-order fulfillment aggregation and will also require the fulfiller to sign for each order in the multicall contract case.
- The orders can be routed through a zone or wrapper contract that determines whether the offered items on each order are still spendable, and filters out unfulfillable orders when composing the final group before providing them to Consideration. This approach allows for cross-order fulfillment aggregation, with the tradeoff of either needing to supply adequate item balances and approvals to the zone or wrapper for items offered by the fulfiller, or to sign for each order as above. This method would add appreciable gas overhead for larger order groups, in many cases negating the gas savings resulting from fulfillment aggregation.

## Feature Wishlist

The following features are good candidates for investigation and potential integration:
- **Support for "tipping" on basic and/or standard order fulfillment.** While currently supported via match orders, this would make single order fulfillments more efficient and straightforward while still enabling referral / relayer fees to be paid out. This feature would allow the fulfiller to specify an optional, arbitrary array of consideration items alongside the fulfilled order, and could be accomplished via either an additional argument on existing functions or additional functions that leverage much of the existing logic and perform the additional transfers once all items specified by the fulfilled order have been transferred.
- **Native support for attempting fulfillment for a group of orders without requiring that every order fulfillment succeeds.** One potential solution would be to implement a `fulfillAvailableOrders` function that iterates over the supplied orders and attempts to transfer all offer items for each order to the fulfiller; if a particular transfer fails, all prior offer item transfers and order status updates for that order would be rolled back and the order would be excluded from the group. Then, remaining consideration items can be aggregated by item type + token + identifier + recipient and transferred from the fulfiller to the recipient.
- **General gas efficiency and code size optimizations.**

Expand Down
Loading

0 comments on commit cc0357b

Please sign in to comment.