Skip to content

Commit

Permalink
Merge pull request #1258 from ProjectOpenSea/horsefacts/add-calldata-…
Browse files Browse the repository at this point in the history
…to-navigator-response

Add suggested action calldata to Navigator response
  • Loading branch information
0age authored Jun 15, 2023
2 parents 8dd8b59 + eaa1356 commit 9d4f697
Show file tree
Hide file tree
Showing 10 changed files with 441 additions and 236 deletions.
5 changes: 5 additions & 0 deletions contracts/helpers/navigator/SeaportNavigator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ contract SeaportNavigator is SeaportNavigatorInterface {
HelperInterface public immutable validatorHelper;
HelperInterface public immutable orderDetailsHelper;
HelperInterface public immutable fulfillmentsHelper;
HelperInterface public immutable suggestedActionHelper;
HelperInterface public immutable executionsHelper;

HelperInterface[] public helpers;
Expand All @@ -55,6 +56,7 @@ contract SeaportNavigator is SeaportNavigatorInterface {
address _validatorHelper,
address _orderDetailsHelper,
address _fulfillmentsHelper,
address _suggestedActionHelper,
address _executionsHelper
) {
requestValidator = HelperInterface(_requestValidator);
Expand All @@ -72,6 +74,9 @@ contract SeaportNavigator is SeaportNavigatorInterface {
fulfillmentsHelper = HelperInterface(_fulfillmentsHelper);
helpers.push(fulfillmentsHelper);

suggestedActionHelper = HelperInterface(_suggestedActionHelper);
helpers.push(suggestedActionHelper);

executionsHelper = HelperInterface(_executionsHelper);
helpers.push(executionsHelper);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/helpers/navigator/lib/ExecutionsHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ contract ExecutionsHelper is HelperInterface {
function prepare(
NavigatorContext memory context
) public view returns (NavigatorContext memory) {
return context.withSuggestedAction().withExecutions();
return context.withExecutions();
}
}
2 changes: 1 addition & 1 deletion contracts/helpers/navigator/lib/NavigatorContextLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ library NavigatorContextLib {
context.response = NavigatorResponse({
orders: new AdvancedOrder[](0),
criteriaResolvers: new CriteriaResolver[](0),
suggestedAction: bytes4(0),
suggestedActionName: "",
suggestedCallData: hex"",
validationErrors: new ErrorsAndWarnings[](0),
orderDetails: new OrderDetails[](0),
offerFulfillments: new FulfillmentComponent[][](0),
Expand Down
220 changes: 2 additions & 218 deletions contracts/helpers/navigator/lib/NavigatorExecutionsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,26 @@ import { UnavailableReason } from "seaport-sol/src/SpaceEnums.sol";

import { OrderDetails } from "seaport-sol/src/fulfillments/lib/Structs.sol";

import { Family, Structure, OrderStructureLib } from "./OrderStructureLib.sol";

import { NavigatorContext } from "./SeaportNavigatorTypes.sol";

library NavigatorExecutionsLib {
using ExecutionHelper for FulfillmentDetails;
using OrderStructureLib for AdvancedOrder[];

/**
* @dev Bad request error: provided orders cannot be fulfilled.
*/
error CannotFulfillProvidedCombinedOrder();

/**
* @dev Bad request error: provided orders include an invalid combination of
* native tokens and unavailable orders.
*/
error InvalidNativeTokenUnavailableCombination();

/**
* @dev Internal error: Could not select a fulfillment method for the provided
* orders.
*/
error UnknownAction();

/**
* @dev Internal error: Could not find selector for the suggested action.
*/
error UnknownSelector();

/**
* @dev Calculate executions for the provided orders and add them to the
* NavigatorResponse.
*/
function withExecutions(
NavigatorContext memory context
) internal pure returns (NavigatorContext memory) {
bytes4 _suggestedAction = context.response.suggestedAction;
bytes memory callData = context.response.suggestedCallData;
bytes4 _suggestedAction = bytes4(callData);
FulfillmentDetails memory fulfillmentDetails = FulfillmentDetails({
orders: context.response.orderDetails,
recipient: payable(context.request.recipient),
Expand Down Expand Up @@ -133,202 +115,4 @@ library NavigatorExecutionsLib {
context.response.nativeTokensReturned = nativeTokensReturned;
return context;
}

/**
* @dev Choose a suggested fulfillment method based on the structure of the
* orders and add it to the NavigatorResponse.
*/
function withSuggestedAction(
NavigatorContext memory context
) internal view returns (NavigatorContext memory) {
context.response.suggestedAction = action(context);
context.response.suggestedActionName = actionName(context);
return context;
}

/**
* @dev Add the human-readable name of the selected fulfillment method to
* the NavigatorResponse.
*/
function actionName(
NavigatorContext memory context
) internal view returns (string memory) {
bytes4 selector = action(context);
if (selector == 0xe7acab24) return "fulfillAdvancedOrder";
if (selector == 0x87201b41) return "fulfillAvailableAdvancedOrders";
if (selector == 0xed98a574) return "fulfillAvailableOrders";
if (selector == 0xfb0f3ee1) return "fulfillBasicOrder";
if (selector == 0x00000000) return "fulfillBasicOrder_efficient_6GL6yc";
if (selector == 0xb3a34c4c) return "fulfillOrder";
if (selector == 0xf2d12b12) return "matchAdvancedOrders";
if (selector == 0xa8174404) return "matchOrders";

revert UnknownSelector();
}

/**
* @dev Choose a suggested fulfillment method based on the structure of the
* orders.
*/
function action(
NavigatorContext memory context
) internal view returns (bytes4) {
Family family = context.response.orders.getFamily();

bool invalidOfferItemsLocated = mustUseMatch(context);

Structure structure = context.response.orders.getStructure(
address(context.request.seaport)
);

bool hasUnavailable = context.request.maximumFulfilled <
context.response.orders.length;
for (uint256 i = 0; i < context.response.orderDetails.length; ++i) {
if (
context.response.orderDetails[i].unavailableReason !=
UnavailableReason.AVAILABLE
) {
hasUnavailable = true;
break;
}
}

if (hasUnavailable) {
if (invalidOfferItemsLocated) {
revert InvalidNativeTokenUnavailableCombination();
}

if (structure == Structure.ADVANCED) {
return
ConsiderationInterface
.fulfillAvailableAdvancedOrders
.selector;
} else {
return ConsiderationInterface.fulfillAvailableOrders.selector;
}
}

if (family == Family.SINGLE && !invalidOfferItemsLocated) {
if (structure == Structure.BASIC) {
return
ConsiderationInterface
.fulfillBasicOrder_efficient_6GL6yc
.selector;
}

if (structure == Structure.STANDARD) {
return ConsiderationInterface.fulfillOrder.selector;
}

if (structure == Structure.ADVANCED) {
return ConsiderationInterface.fulfillAdvancedOrder.selector;
}
}

bool cannotMatch = (context
.response
.unmetConsiderationComponents
.length !=
0 ||
hasUnavailable);

if (cannotMatch && invalidOfferItemsLocated) {
revert CannotFulfillProvidedCombinedOrder();
}

if (cannotMatch) {
if (structure == Structure.ADVANCED) {
return
ConsiderationInterface
.fulfillAvailableAdvancedOrders
.selector;
} else {
return ConsiderationInterface.fulfillAvailableOrders.selector;
}
} else if (invalidOfferItemsLocated) {
if (structure == Structure.ADVANCED) {
return ConsiderationInterface.matchAdvancedOrders.selector;
} else {
return ConsiderationInterface.matchOrders.selector;
}
} else {
if (structure == Structure.ADVANCED) {
return
context.request.preferMatch
? ConsiderationInterface.matchAdvancedOrders.selector
: ConsiderationInterface
.fulfillAvailableAdvancedOrders
.selector;
} else {
return
context.request.preferMatch
? ConsiderationInterface.matchOrders.selector
: ConsiderationInterface
.fulfillAvailableOrders
.selector;
}
}
}

/**
* @dev Return whether the provided orders must be matched using matchOrders
* or matchAdvancedOrders.
*/
function mustUseMatch(
NavigatorContext memory context
) internal pure returns (bool) {
OrderDetails[] memory orders = context.response.orderDetails;

for (uint256 i = 0; i < orders.length; ++i) {
OrderDetails memory order = orders[i];

if (order.isContract) {
continue;
}

for (uint256 j = 0; j < order.offer.length; ++j) {
if (order.offer[j].itemType == ItemType.NATIVE) {
return true;
}
}
}

if (context.request.caller == context.request.recipient) {
return false;
}

for (uint256 i = 0; i < orders.length; ++i) {
OrderDetails memory order = orders[i];

for (uint256 j = 0; j < order.offer.length; ++j) {
SpentItem memory item = order.offer[j];

if (item.itemType != ItemType.ERC721) {
continue;
}

for (uint256 k = 0; k < orders.length; ++k) {
OrderDetails memory comparisonOrder = orders[k];
for (
uint256 l = 0;
l < comparisonOrder.consideration.length;
++l
) {
ReceivedItem memory considerationItem = comparisonOrder
.consideration[l];

if (
considerationItem.itemType == ItemType.ERC721 &&
considerationItem.identifier == item.identifier &&
considerationItem.token == item.token
) {
return true;
}
}
}
}
}

return false;
}
}
Loading

0 comments on commit 9d4f697

Please sign in to comment.