Skip to content

Commit

Permalink
Merge pull request #60 from ProjectOpenSea/skip-execution-filtering
Browse files Browse the repository at this point in the history
skip execution filtering but do not transfer when amount is zero
  • Loading branch information
0age authored Mar 6, 2024
2 parents 602e1e4 + 63b4fff commit 5fb250d
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 659 deletions.
49 changes: 20 additions & 29 deletions reference/lib/ReferenceFulfillmentApplier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ contract ReferenceFulfillmentApplier is

// Skip aggregating offer items if no consideration items are available.
if (considerationItem.amount == 0) {
// Set the offerer and recipient to null address and item type to a
// non-native item type if execution amount is zero. This will cause
// the execution item to be skipped.
execution.offerer = address(0);
execution.item.recipient = payable(0);
execution.item.itemType = ItemType.ERC20;
return execution;
}

Expand Down Expand Up @@ -141,11 +135,14 @@ contract ReferenceFulfillmentApplier is
}

// Reuse execution struct with consideration amount and recipient.
// Only set the recipient if the item amount is non-zero.
execution.item.amount = considerationItem.amount;
execution.item.recipient = considerationItem.recipient;
if (execution.item.amount != 0) {
execution.item.recipient = considerationItem.recipient;
}

// Return the final execution that will be triggered for relevant items.
return execution; // Execution(considerationItem, offerer, conduitKey);
return execution; // Execution(execution, offerer, conduitKey);
}

/**
Expand Down Expand Up @@ -204,21 +201,6 @@ contract ReferenceFulfillmentApplier is
);
}

if (execution.item.amount == 0) {
return
Execution(
ReceivedItem(
ItemType.ERC20,
address(0),
0,
0,
payable(address(0))
),
address(0),
bytes32(0)
);
}

return execution;
}

Expand Down Expand Up @@ -395,12 +377,21 @@ contract ReferenceFulfillmentApplier is
)
);

// Return execution for aggregated items provided by the fulfiller.
execution = Execution(
receiveConsiderationItem,
msg.sender,
fulfillerConduitKey
);
if (receiveConsiderationItem.amount != 0) {
// Return execution for aggregated items provided by the fulfiller.
execution = Execution(
receiveConsiderationItem,
msg.sender,
fulfillerConduitKey
);
} else {
// Return an empty execution if the received item amount is zero.
execution = Execution(
receiveConsiderationItem,
address(0),
bytes32(0)
);
}
}

/**
Expand Down
128 changes: 28 additions & 100 deletions reference/lib/ReferenceOrderCombiner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,13 @@ contract ReferenceOrderCombiner is
}

// Apply criteria resolvers to each order as applicable.
_applyCriteriaResolvers(advancedOrders, ordersToExecute, criteriaResolvers);
_applyCriteriaResolvers(
advancedOrders,
ordersToExecute,
criteriaResolvers
);

bool someOrderAvailable;

// Iterate over each order to check authorization status (for restricted
// orders), generate orders (for contract orders), and emit events (for
Expand Down Expand Up @@ -500,6 +506,13 @@ contract ReferenceOrderCombiner is
spentItems,
receivedItems
);

someOrderAvailable = true;
}

// Revert if no orders are available.
if (!someOrderAvailable) {
revert NoSpecifiedOrdersAvailable();
}
}

Expand Down Expand Up @@ -599,89 +612,32 @@ contract ReferenceOrderCombiner is
totalOfferFulfillments + totalConsiderationFulfillments
);

// Track number of filtered executions.
uint256 totalFilteredExecutions = 0;

// Iterate over each offer fulfillment.
for (uint256 i = 0; i < totalOfferFulfillments; ++i) {
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
// Derive aggregated execution corresponding with fulfillment and
// assign the execution to the executions array.
executions[i] = _aggregateAvailable(
ordersToExecute,
Side.OFFER,
offerFulfillments[i],
fulfillerConduitKey,
recipient
);

// If offerer and recipient on the execution are the same and the
// execution item has a non-native item type...
if (
execution.item.recipient == execution.offerer
&& execution.item.itemType != ItemType.NATIVE
) {
// 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) {
// Derive aggregated execution corresponding with fulfillment.
Execution memory execution = _aggregateAvailable(
// Derive aggregated execution corresponding with fulfillment and
// assign the execution to the executions array.
executions[i + totalOfferFulfillments] = _aggregateAvailable(
ordersToExecute,
Side.CONSIDERATION,
considerationFulfillments[i],
fulfillerConduitKey,
address(0) // unused
);

// If offerer and recipient on the execution are the same and the
// execution item has a non-native item type...
if (
execution.item.recipient == execution.offerer
&& execution.item.itemType != ItemType.NATIVE
) {
// 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) {
/**
* The following is highly inefficient, but written this way
* to show in the most simplest form what the optimized
* contract is performing inside its assembly.
*/

// Get the total execution length.
uint256 executionLength = (
totalOfferFulfillments + totalConsiderationFulfillments
) - totalFilteredExecutions;

// Create an array of executions that will be executed.
Execution[] memory filteredExecutions =
new Execution[](executionLength);

// Create new array from the existing Executions
for (uint256 i = 0; i < executionLength; ++i) {
filteredExecutions[i] = executions[i];
}

// Set the executions array to the newly created array.
executions = filteredExecutions;
}
// Revert if no orders are available.
if (executions.length == 0) {
revert NoSpecifiedOrdersAvailable();
}
// Perform final checks and compress executions into standard and batch.
availableOrders = _performFinalChecksAndExecuteOrders(
advancedOrders,
Expand Down Expand Up @@ -741,6 +697,11 @@ contract ReferenceOrderCombiner is
Execution memory execution = executions[i];
ReceivedItem memory item = execution.item;

// Skip transfers if the execution amount is zero.
if (item.amount == 0) {
continue;
}

// If execution transfers native tokens, reduce value available.
if (item.itemType == ItemType.NATIVE) {
// Ensure that sufficient native tokens are still available.
Expand Down Expand Up @@ -1034,52 +995,19 @@ contract ReferenceOrderCombiner is
// Allocate executions by fulfillment and apply them to each execution.
executions = new Execution[](totalFulfillments);

// 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(
// Derive the execution corresponding with the fulfillment and
// assign the execution to the executions array.
executions[i] = _applyFulfillment(
ordersToExecute,
fulfillment.offerComponents,
fulfillment.considerationComponents,
i
);

// If offerer and recipient on the execution are the same and the
// execution item has a non-native item type...
if (
execution.item.recipient == execution.offerer
&& execution.item.itemType != ItemType.NATIVE
) {
// 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) {
uint256 executionLength = (
totalFulfillments - totalFilteredExecutions
);

Execution[] memory filteredExecutions = (
new Execution[](executionLength)
);

// Create new array from executions.
for (uint256 i = 0; i < executionLength; ++i) {
filteredExecutions[i] = executions[i];
}

executions = filteredExecutions;
}

// Perform final checks and execute orders.
Expand Down
Loading

0 comments on commit 5fb250d

Please sign in to comment.