From c82c7e2e8d50bef3e58a827cc99af37a02c96537 Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Fri, 9 Feb 2024 17:51:31 -0500 Subject: [PATCH 1/8] encode + call authorizeOrder, reuse encoding for validateOrder --- src/core/lib/BasicOrderFulfiller.sol | 76 +++++----- src/core/lib/Consideration.sol | 20 ++- src/core/lib/ConsiderationDecoder.sol | 26 ++++ src/core/lib/ConsiderationEncoder.sol | 184 +++++++++++++++++------ src/core/lib/OrderCombiner.sol | 4 +- src/core/lib/OrderFulfiller.sol | 6 +- src/core/lib/OrderValidator.sol | 16 +- src/core/lib/ZoneInteraction.sol | 94 +++++++++++- src/types/lib/ConsiderationConstants.sol | 4 +- 9 files changed, 323 insertions(+), 107 deletions(-) diff --git a/src/core/lib/BasicOrderFulfiller.sol b/src/core/lib/BasicOrderFulfiller.sol index d200a24..8c6e207 100644 --- a/src/core/lib/BasicOrderFulfiller.sol +++ b/src/core/lib/BasicOrderFulfiller.sol @@ -7,7 +7,7 @@ import { OrderType } from "seaport-types/src/lib/ConsiderationEnums.sol"; -import { BasicOrderParameters } from +import { BasicOrderParameters, OrderStatus } from "seaport-types/src/lib/ConsiderationStructs.sol"; import { OrderValidator } from "./OrderValidator.sol"; @@ -57,6 +57,7 @@ import { BasicOrder_order_startTime_ptr, BasicOrder_order_typeHash_ptr, BasicOrder_receivedItemByteMap, + BasicOrder_signature_cdPtr, BasicOrder_startTime_cdPtr, BasicOrder_totalOriginalAdditionalRecipients_cdPtr, BasicOrder_zone_cdPtr, @@ -114,6 +115,10 @@ import { UnusedItemParameters_error_selector } from "seaport-types/src/lib/ConsiderationErrorConstants.sol"; +import { + CalldataPointer +} from "seaport-types/src/helpers/PointerLibraries.sol"; + /** * @title BasicOrderFulfiller * @author 0age @@ -147,19 +152,9 @@ contract BasicOrderFulfiller is OrderValidator { * 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) { + function _validateAndFulfillBasicOrder() internal returns (bool) { // Declare enums for order type & route to extract from basicOrderType. BasicOrderRouteType route; OrderType orderType; @@ -207,6 +202,7 @@ contract BasicOrderFulfiller is OrderValidator { address additionalRecipientsToken; ItemType offeredItemType; bool offerTypeIsAdditionalRecipientsType; + uint256 callDataPointer; // Declare scope for received item type to manage stack pressure. { @@ -240,8 +236,7 @@ contract BasicOrderFulfiller is OrderValidator { } // Derive & validate order using parameters and update order status. - orderHash = _prepareBasicFulfillmentFromCalldata( - parameters, + (orderHash, callDataPointer) = _prepareBasicFulfillmentFromCalldata( orderType, receivedItemType, additionalRecipientsItemType, @@ -303,33 +298,33 @@ contract BasicOrderFulfiller is OrderValidator { if (route == BasicOrderRouteType.ERC20_TO_ERC721) { // Transfer ERC721 to caller using offerer's conduit preference. _transferERC721( - parameters.offerToken, - parameters.offerer, + CalldataPointer.wrap(BasicOrder_offerToken_cdPtr).readAddress(), + CalldataPointer.wrap(BasicOrder_offerer_cdPtr).readAddress(), msg.sender, - parameters.offerIdentifier, - parameters.offerAmount, + CalldataPointer.wrap(BasicOrder_offerIdentifier_cdPtr).readUint256(), + CalldataPointer.wrap(BasicOrder_offerAmount_cdPtr).readUint256(), conduitKey, accumulator ); } else if (route == BasicOrderRouteType.ERC20_TO_ERC1155) { // Transfer ERC1155 to caller with offerer's conduit preference. _transferERC1155( - parameters.offerToken, - parameters.offerer, + CalldataPointer.wrap(BasicOrder_offerToken_cdPtr).readAddress(), + CalldataPointer.wrap(BasicOrder_offerer_cdPtr).readAddress(), msg.sender, - parameters.offerIdentifier, - parameters.offerAmount, + CalldataPointer.wrap(BasicOrder_offerIdentifier_cdPtr).readUint256(), + CalldataPointer.wrap(BasicOrder_offerAmount_cdPtr).readUint256(), conduitKey, accumulator ); } else if (route == BasicOrderRouteType.ERC721_TO_ERC20) { // Transfer ERC721 to offerer using caller's conduit preference. _transferERC721( - parameters.considerationToken, + CalldataPointer.wrap(BasicOrder_considerationToken_cdPtr).readAddress(), msg.sender, - parameters.offerer, - parameters.considerationIdentifier, - parameters.considerationAmount, + CalldataPointer.wrap(BasicOrder_offerer_cdPtr).readAddress(), + CalldataPointer.wrap(BasicOrder_considerationIdentifier_cdPtr).readUint256(), + CalldataPointer.wrap(BasicOrder_considerationAmount_cdPtr).readUint256(), conduitKey, accumulator ); @@ -338,11 +333,11 @@ contract BasicOrderFulfiller is OrderValidator { // Transfer ERC1155 to offerer with caller's conduit preference. _transferERC1155( - parameters.considerationToken, + CalldataPointer.wrap(BasicOrder_considerationToken_cdPtr).readAddress(), msg.sender, - parameters.offerer, - parameters.considerationIdentifier, - parameters.considerationAmount, + CalldataPointer.wrap(BasicOrder_offerer_cdPtr).readAddress(), + CalldataPointer.wrap(BasicOrder_considerationIdentifier_cdPtr).readUint256(), + CalldataPointer.wrap(BasicOrder_considerationAmount_cdPtr).readUint256(), conduitKey, accumulator ); @@ -358,7 +353,7 @@ contract BasicOrderFulfiller is OrderValidator { } // Determine whether order is restricted and, if so, that it is valid. - _assertRestrictedBasicOrderValidity(orderHash, orderType, parameters); + _assertRestrictedBasicOrderValidity(orderHash, orderType, callDataPointer); // Clear the reentrancy guard. _clearReentrancyGuard(); @@ -379,7 +374,6 @@ contract BasicOrderFulfiller is OrderValidator { * same data as the order hash is derived from. Also note that this * function accesses memory directly. * - * @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. @@ -393,13 +387,12 @@ contract BasicOrderFulfiller is OrderValidator { * @return orderHash The calculated order hash. */ function _prepareBasicFulfillmentFromCalldata( - BasicOrderParameters calldata parameters, OrderType orderType, ItemType receivedItemType, ItemType additionalRecipientsItemType, address additionalRecipientsToken, ItemType offeredItemType - ) internal returns (bytes32 orderHash) { + ) internal returns (bytes32 orderHash, uint256 callDataPointer) { // Ensure this function cannot be triggered during a reentrant call. _setReentrancyGuard(false); // Native tokens rejected during execution. @@ -1026,10 +1019,21 @@ contract BasicOrderFulfiller is OrderValidator { } // Verify and update the status of the derived order. - _validateBasicOrderAndUpdateStatus(orderHash, parameters.signature); + OrderStatus storage orderStatus = _validateBasicOrder( + orderHash, + _toBytesReturnType(_decodeBytes)( + CalldataPointer.wrap(BasicOrder_signature_cdPtr) + ) + ); + + // Determine whether order is restricted and, if so, that it is valid. + callDataPointer = _assertRestrictedBasicOrderAuthorization(orderHash, orderType); + + // Update the status of the order and mark as fully filled. + _updateBasicOrderStatus(orderStatus); // Return the derived order hash. - return orderHash; + return (orderHash, callDataPointer); } /** diff --git a/src/core/lib/Consideration.sol b/src/core/lib/Consideration.sol index 79196b5..e116844 100644 --- a/src/core/lib/Consideration.sol +++ b/src/core/lib/Consideration.sol @@ -87,7 +87,7 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * 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 + * @custom: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 @@ -97,14 +97,19 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * @return fulfilled A boolean indicating whether the order has been * successfully fulfilled. */ - function fulfillBasicOrder(BasicOrderParameters calldata parameters) + function fulfillBasicOrder( + /** + * @custom:name parameters + */ + BasicOrderParameters calldata + ) external payable override returns (bool fulfilled) { // Validate and fulfill the basic order. - fulfilled = _validateAndFulfillBasicOrder(parameters); + fulfilled = _validateAndFulfillBasicOrder(); } /** @@ -125,7 +130,7 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * the zero bytes in the function selector (0x00000000) which also * results in earlier function dispatch. * - * @param parameters Additional information on the fulfilled order. Note + * @custom: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 @@ -136,10 +141,13 @@ contract Consideration is ConsiderationInterface, OrderCombiner { * successfully fulfilled. */ function fulfillBasicOrder_efficient_6GL6yc( - BasicOrderParameters calldata parameters + /** + * @custom:name parameters + */ + BasicOrderParameters calldata ) external payable override returns (bool fulfilled) { // Validate and fulfill the basic order. - fulfilled = _validateAndFulfillBasicOrder(parameters); + fulfilled = _validateAndFulfillBasicOrder(); } /** diff --git a/src/core/lib/ConsiderationDecoder.sol b/src/core/lib/ConsiderationDecoder.sol index 5fb7be4..cfb229f 100644 --- a/src/core/lib/ConsiderationDecoder.sol +++ b/src/core/lib/ConsiderationDecoder.sol @@ -1131,6 +1131,32 @@ contract ConsiderationDecoder { } } + /** + * @dev Converts a function taking a calldata pointer and returning a memory + * pointer into a function taking that calldata pointer and returning + * a bytes type. + * + * @param inFn The input function, taking an arbitrary calldata pointer and + * returning an arbitrary memory pointer. + * + * @return outFn The output function, taking an arbitrary calldata pointer + * and returning a bytes type. + */ + function _toBytesReturnType( + function(CalldataPointer) internal pure returns (MemoryPointer) inFn + ) + internal + pure + returns ( + function(CalldataPointer) internal pure returns (bytes memory) + outFn + ) + { + assembly { + outFn := inFn + } + } + /** * @dev Converts a function taking a calldata pointer and returning a memory * pointer into a function taking that calldata pointer and returning diff --git a/src/core/lib/ConsiderationEncoder.sol b/src/core/lib/ConsiderationEncoder.sol index f89c3fd..9c68196 100644 --- a/src/core/lib/ConsiderationEncoder.sol +++ b/src/core/lib/ConsiderationEncoder.sol @@ -2,8 +2,13 @@ pragma solidity ^0.8.17; import { + authorizeOrder_head_offset, + authorizeOrder_selector_offset, + authorizeOrder_selector, + authorizeOrder_zoneParameters_offset, BasicOrder_additionalRecipients_length_cdPtr, BasicOrder_common_params_size, + BasicOrder_offerer_cdPtr, BasicOrder_startTime_cdPtr, BasicOrder_startTimeThroughZoneHash_size, Common_amount_offset, @@ -39,10 +44,7 @@ import { SixtyThreeBytes, SpentItem_size_shift, SpentItem_size, - validateOrder_head_offset, - validateOrder_selector_offset, validateOrder_selector, - validateOrder_zoneParameters_offset, ZoneParameters_base_tail_offset, ZoneParameters_basicOrderFixedElements_length, ZoneParameters_consideration_head_offset, @@ -65,7 +67,9 @@ import { import { CalldataPointer, getFreeMemoryPointer, - MemoryPointer + setFreeMemoryPointer, + MemoryPointer, + OffsetOrLengthMask } from "seaport-types/src/helpers/PointerLibraries.sol"; contract ConsiderationEncoder { @@ -338,7 +342,7 @@ contract ConsiderationEncoder { /** * @dev Takes an order hash, OrderParameters struct, extraData bytes array, * and array of order hashes for each order included as part of the - * current fulfillment and encodes it as `validateOrder` calldata. + * current fulfillment and encodes it as `authorizeOrder` calldata. * Note that future, new versions of this contract may end up writing * to a memory region that might have been potentially dirtied by the * accumulator. Since the book-keeping for the accumulator does not @@ -348,39 +352,41 @@ contract ConsiderationEncoder { * * @param orderHash The order hash. * @param orderParameters The OrderParameters struct used to construct the - * encoded `validateOrder` calldata. + * encoded `authorizeOrder` calldata. * @param extraData The extraData bytes array used to construct the - * encoded `validateOrder` calldata. + * encoded `authorizeOrder` calldata. * @param orderHashes An array of bytes32 values representing the order * hashes of all orders included as part of the * current fulfillment. * - * @return dst A memory pointer referencing the encoded `validateOrder` + * @return dst A memory pointer referencing the encoded `authorizeOrder` * calldata. * @return size The size of the bytes array. */ - function _encodeValidateOrder( + function _encodeAuthorizeOrder( bytes32 orderHash, OrderParameters memory orderParameters, bytes memory extraData, - bytes32[] memory orderHashes + bytes32[] memory orderHashes, + uint256 orderIndex ) internal view returns (MemoryPointer dst, uint256 size) { - // Get free memory pointer to write calldata to. This isn't allocated as - // it is only used for a single function call. - dst = getFreeMemoryPointer(); + // Get free memory pointer to write calldata to. + MemoryPointer ptr = getFreeMemoryPointer(); - // Write validateOrder selector and get pointer to start of calldata. - dst.write(validateOrder_selector); - dst = dst.offset(validateOrder_selector_offset); + dst = ptr; + + // Write authorizeOrder selector and get pointer to start of calldata. + dst.write(authorizeOrder_selector); + dst = dst.offset(authorizeOrder_selector_offset); // Get pointer to the beginning of the encoded data. - MemoryPointer dstHead = dst.offset(validateOrder_head_offset); + MemoryPointer dstHead = dst.offset(authorizeOrder_head_offset); // Write offset to zoneParameters to start of calldata. - dstHead.write(validateOrder_zoneParameters_offset); + dstHead.write(authorizeOrder_zoneParameters_offset); // Reuse `dstHead` as pointer to zoneParameters. - dstHead = dstHead.offset(validateOrder_zoneParameters_offset); + dstHead = dstHead.offset(authorizeOrder_zoneParameters_offset); // Write orderHash and fulfiller to zoneParameters. dstHead.writeBytes32(orderHash); @@ -442,11 +448,12 @@ contract ConsiderationEncoder { // Write offset to extraData. dstHead.offset(ZoneParameters_extraData_head_offset).write(tailOffset); - // Copy extraData. - uint256 extraDataSize = - _encodeBytes(toMemoryPointer(extraData), dstHead.offset(tailOffset)); unchecked { + // Copy extraData. + uint256 extraDataSize = + _encodeBytes(toMemoryPointer(extraData), dstHead.offset(tailOffset)); + // Increment tail offset, now used to populate orderHashes array. tailOffset += extraDataSize; } @@ -455,56 +462,119 @@ contract ConsiderationEncoder { dstHead.offset(ZoneParameters_orderHashes_head_offset).write(tailOffset); // Encode the order hashes array. - uint256 orderHashesSize = _encodeOrderHashes( - toMemoryPointer(orderHashes), dstHead.offset(tailOffset) - ); + MemoryPointer orderHashesLengthLocation = dstHead.offset(tailOffset); unchecked { + uint256 orderHashesSize = _encodeOrderHashes( + toMemoryPointer(orderHashes), orderHashesLengthLocation + ); + // Increment the tail offset, now used to determine final size. tailOffset += orderHashesSize; // Derive final size including selector and ZoneParameters pointer. size = ZoneParameters_selectorAndPointer_length + tailOffset; } + + // Update the free memory pointer. + setFreeMemoryPointer(ptr.offset(size)); + + // Track the pointer, size (when performing validateOrder) and pointer + // to orderHashes length by overriding the salt value on the order. + orderParameters.salt = ( + (ptr.readMaskedUint256() << 128) & + (size << 64) & + orderHashesLengthLocation.readMaskedUint256() + ); + + // Write the shorted orderHashes array length. + orderHashesLengthLocation.write(orderIndex); + + // Modify encoding size to account for the shorter orderHashes array. + size -= (1 + orderHashes.length - orderIndex) * OneWord; } /** - * @dev Takes an order hash and BasicOrderParameters struct (from calldata) - * and encodes it as `validateOrder` calldata. + * @dev Takes an order hash, OrderParameters struct, extraData bytes array, + * and array of order hashes for each order included as part of the + * current fulfillment and encodes it as `validateOrder` calldata. + * Note that future, new versions of this contract may end up writing + * to a memory region that might have been potentially dirtied by the + * accumulator. Since the book-keeping for the accumulator does not + * update the free memory pointer, it will be necessary to ensure that + * all bytes in the memory in the range [dst, dst+size) are fully + * updated/written to in this function. * - * @param orderHash The order hash. - * @param parameters The BasicOrderParameters struct used to construct the - * encoded `validateOrder` calldata. + * @param salt The salt on the order, which has been repurposed + * to contain relevant pointers and encoding size. + * @param orderHashes An array of bytes32 values representing the order + * hashes of all orders included as part of the + * current fulfillment. * * @return dst A memory pointer referencing the encoded `validateOrder` * calldata. * @return size The size of the bytes array. */ - function _encodeValidateBasicOrder( - bytes32 orderHash, - BasicOrderParameters calldata parameters + function _encodeValidateOrder( + uint256 salt, + bytes32[] memory orderHashes ) internal view returns (MemoryPointer dst, uint256 size) { - // Get free memory pointer to write calldata to. This isn't allocated as - // it is only used for a single function call. - dst = getFreeMemoryPointer(); + dst = MemoryPointer.wrap(salt >> 128); + size = (salt >> 64) & OffsetOrLengthMask; + MemoryPointer orderHashesLengthLocation = MemoryPointer.wrap(salt & OffsetOrLengthMask); - // Write validateOrder selector and get pointer to start of calldata. + // Write validateOrder selector. dst.write(validateOrder_selector); - dst = dst.offset(validateOrder_selector_offset); + + // Encode the order hashes array. Note that this currently modifies + // order hashes that are known to be properly encoded already and could + // therefore be skipped. + _encodeOrderHashes( + toMemoryPointer(orderHashes), orderHashesLengthLocation + ); + } + + /** + * @dev Takes an order hash and BasicOrderParameters struct (from calldata) + * and encodes it as `authorizeOrder` calldata. + * + * @param orderHash The order hash. + * + * @return dst A memory pointer referencing the encoded `authorizeOrder` + * calldata. + * @return size The size of the bytes array. + */ + function _encodeAuthorizeBasicOrder( + bytes32 orderHash + ) internal view returns ( + MemoryPointer dst, + uint256 size, + uint256 memoryLocationForOrderHashes + ) { + // Get free memory pointer to write calldata to. + MemoryPointer ptr = getFreeMemoryPointer(); + + dst = ptr; + + // Write validateOrder selector and get pointer to start of calldata. + dst.write(authorizeOrder_selector); + dst = dst.offset(authorizeOrder_selector_offset); // Get pointer to the beginning of the encoded data. - MemoryPointer dstHead = dst.offset(validateOrder_head_offset); + MemoryPointer dstHead = dst.offset(authorizeOrder_head_offset); // Write offset to zoneParameters to start of calldata. - dstHead.write(validateOrder_zoneParameters_offset); + dstHead.write(authorizeOrder_zoneParameters_offset); // Reuse `dstHead` as pointer to zoneParameters. - dstHead = dstHead.offset(validateOrder_zoneParameters_offset); + dstHead = dstHead.offset(authorizeOrder_zoneParameters_offset); // Write offerer, orderHash and fulfiller to zoneParameters. dstHead.writeBytes32(orderHash); dstHead.offset(ZoneParameters_fulfiller_offset).write(msg.sender); - dstHead.offset(ZoneParameters_offerer_offset).write(parameters.offerer); + dstHead.offset(ZoneParameters_offerer_offset).write( + CalldataPointer.wrap(BasicOrder_offerer_cdPtr).readAddress() + ); // Copy startTime, endTime and zoneHash to zoneParameters. CalldataPointer.wrap(BasicOrder_startTime_cdPtr).copy( @@ -559,8 +629,10 @@ contract ConsiderationEncoder { // Write offset to orderHashes. dstHead.offset(ZoneParameters_orderHashes_head_offset).write(tailOffset); - // Write length = 1 to the orderHashes array. - dstHead.offset(tailOffset).write(1); + memoryLocationForOrderHashes = dstHead.offset(tailOffset).readMaskedUint256(); + + // Write length = 0 to the orderHashes array. + dstHead.offset(tailOffset).write(0); unchecked { // Write the single order hash to the orderHashes array. @@ -568,9 +640,33 @@ contract ConsiderationEncoder { // Final size: selector, ZoneParameters pointer, orderHashes & tail. size = ZoneParameters_basicOrderFixedElements_length + tailOffset; + + // Update the free memory pointer. + setFreeMemoryPointer(ptr.offset(size + OneWord)); } } + /** + * @dev Takes pointers to already-encoded data and modifies it so that + * it is properly formatted for a `validateOrder` call. + * + * @param dst A memory pointer referencing the + * encoded `validateOrder` calldata. + * @param memoryLocationForOrderHashes A memory pointer referencing where + * to encode orderHashes length of 1. + */ + function _encodeValidateBasicOrder( + MemoryPointer dst, + uint256 memoryLocationForOrderHashes + ) internal pure { + // Write validateOrder selector and get pointer to start of calldata. + dst.write(validateOrder_selector); + + // Write length = 1 to the orderHashes array. Note that size should now + // be one word larger than the provided size. + MemoryPointer.wrap(memoryLocationForOrderHashes).write(1); + } + /** * @dev Takes a memory pointer to an array of bytes32 values representing * the order hashes included as part of the fulfillment and a memory diff --git a/src/core/lib/OrderCombiner.sol b/src/core/lib/OrderCombiner.sol index 7c66fc5..7e3bf5b 100644 --- a/src/core/lib/OrderCombiner.sol +++ b/src/core/lib/OrderCombiner.sol @@ -486,7 +486,9 @@ contract OrderCombiner is OrderFulfiller, FulfillmentApplier { // Update order status as long as there is some fraction available. if (advancedOrder.parameters.orderType != OrderType.CONTRACT) { - // TODO: perform authorizeOrder call + _assertRestrictedAdvancedOrderAuthorization( + advancedOrder, orderHashes, orderHash, (i / OneWord) - 1 + ); if (!_updateStatus( orderHash, diff --git a/src/core/lib/OrderFulfiller.sol b/src/core/lib/OrderFulfiller.sol index 8b64878..a68e431 100644 --- a/src/core/lib/OrderFulfiller.sol +++ b/src/core/lib/OrderFulfiller.sol @@ -121,9 +121,11 @@ contract OrderFulfiller is bytes32[] memory orderHashes = new bytes32[](0); - // TODO: perform authorizeOrder call here - if (advancedOrder.parameters.orderType != OrderType.CONTRACT) { + _assertRestrictedAdvancedOrderAuthorization( + advancedOrder, orderHashes, orderHash, 0 + ); + _updateStatus(orderHash, fillNumerator, fillDenominator, true); } else { // Return the generated order based on the order params and the diff --git a/src/core/lib/OrderValidator.sol b/src/core/lib/OrderValidator.sol index 332ea15..18a6c66 100644 --- a/src/core/lib/OrderValidator.sol +++ b/src/core/lib/OrderValidator.sol @@ -76,7 +76,7 @@ contract OrderValidator is Executor, ZoneInteraction { constructor(address conduitController) Executor(conduitController) { } /** - * @dev Internal function to verify and update the status of a basic order. + * @dev Internal function to verify the status of a basic order. * Note that this function may only be safely called as part of basic * orders, as it assumes a specific calldata encoding structure that * must first be validated. @@ -85,10 +85,10 @@ contract OrderValidator is Executor, ZoneInteraction { * @param signature A signature from the offerer indicating that the order * has been approved. */ - function _validateBasicOrderAndUpdateStatus( + function _validateBasicOrder( bytes32 orderHash, - bytes calldata signature - ) internal { + bytes memory signature + ) internal view returns (OrderStatus storage orderStatus) { // Retrieve offerer directly using fixed calldata offset based on strict // basic parameter encoding. address offerer; @@ -97,7 +97,7 @@ contract OrderValidator is Executor, ZoneInteraction { } // Retrieve the order status for the given order hash. - OrderStatus storage orderStatus = _orderStatus[orderHash]; + orderStatus = _orderStatus[orderHash]; // Ensure order is fillable and is not cancelled. _verifyOrderStatus( @@ -111,14 +111,14 @@ contract OrderValidator is Executor, ZoneInteraction { if (!orderStatus.isValidated) { _verifySignature(offerer, orderHash, signature); } + } - // TODO: perform authorizeOrder call here - + function _updateBasicOrderStatus(OrderStatus storage orderStatus) internal { // Update order status as fully filled, packing struct values. orderStatus.isValidated = true; orderStatus.isCancelled = false; orderStatus.numerator = 1; - orderStatus.denominator = 1; + orderStatus.denominator = 1; } /** diff --git a/src/core/lib/ZoneInteraction.sol b/src/core/lib/ZoneInteraction.sol index c5903eb..6965e5a 100644 --- a/src/core/lib/ZoneInteraction.sol +++ b/src/core/lib/ZoneInteraction.sol @@ -16,12 +16,14 @@ import { LowLevelHelpers } from "./LowLevelHelpers.sol"; import { ConsiderationEncoder } from "./ConsiderationEncoder.sol"; -import { MemoryPointer } from "seaport-types/src/helpers/PointerLibraries.sol"; +import { CalldataPointer, MemoryPointer, OffsetOrLengthMask } from "seaport-types/src/helpers/PointerLibraries.sol"; import { + BasicOrder_zone_cdPtr, ContractOrder_orderHash_offerer_shift, MaskOverFirstFourBytes, OneWord, + OrderParameters_salt_offset, OrderParameters_zone_offset } from "seaport-types/src/lib/ConsiderationConstants.sol"; @@ -52,24 +54,71 @@ contract ZoneInteraction is * * @param orderHash The hash of the order. * @param orderType The order type. - * @param parameters The parameters of the basic order. + */ + function _assertRestrictedBasicOrderAuthorization( + bytes32 orderHash, + OrderType orderType + ) internal returns (uint256 callDataPointer) { + // Order type 2-3 require zone be caller or zone to approve. + // Note that in cases where fulfiller == zone, the restricted order + // validation will be skipped. + if (_isRestrictedAndCallerNotZone(orderType, CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress())) { + // Encode the `authorizeOrder` call in memory. + + (MemoryPointer callData, uint256 size, uint256 memoryLocationForOrderHashes) = + _encodeAuthorizeBasicOrder(orderHash); + + // Perform `authorizeOrder` call and ensure magic value was returned. + _callAndCheckStatus( + CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress(), + orderHash, + callData, + size, + InvalidRestrictedOrder_error_selector + ); + + callDataPointer = callData.readMaskedUint256(); + + unchecked { + callData.write((size + OneWord) << 128 & memoryLocationForOrderHashes); + } + } + } + + /** + * @dev Internal function to determine if an order has a restricted order + * type and, if so, to ensure that either the zone is the caller or + * that a call to `validateOrder` on the zone returns a magic value + * indicating that the order is currently valid. Note that contract + * orders are not accessible via the basic fulfillment method. + * + * @param orderHash The hash of the order. + * @param orderType The order type. + * @param callDataPtr The pointer to the call data for the basic order. + * Note that the initial value will contain the size + * and the memory location for order hashes length. */ function _assertRestrictedBasicOrderValidity( bytes32 orderHash, OrderType orderType, - BasicOrderParameters calldata parameters + uint256 callDataPtr ) internal { // Order type 2-3 require zone be caller or zone to approve. // Note that in cases where fulfiller == zone, the restricted order // validation will be skipped. - if (_isRestrictedAndCallerNotZone(orderType, parameters.zone)) { + if (_isRestrictedAndCallerNotZone(orderType, CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress())) { + MemoryPointer callData = MemoryPointer.wrap(callDataPtr); + uint256 sizeAndMemoryLocationForOrderHashes = callData.readUint256(); + + uint256 size = sizeAndMemoryLocationForOrderHashes >> 128; + uint256 memoryLocationForOrderHashes = sizeAndMemoryLocationForOrderHashes & OffsetOrLengthMask; + // Encode the `validateOrder` call in memory. - (MemoryPointer callData, uint256 size) = - _encodeValidateBasicOrder(orderHash, parameters); + _encodeValidateBasicOrder(callData, memoryLocationForOrderHashes); // Perform `validateOrder` call and ensure magic value was returned. _callAndCheckStatus( - parameters.zone, + CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress(), orderHash, callData, size, @@ -78,6 +127,33 @@ contract ZoneInteraction is } } + function _assertRestrictedAdvancedOrderAuthorization( + AdvancedOrder memory advancedOrder, + bytes32[] memory orderHashes, + bytes32 orderHash, + uint256 orderIndex + ) internal { + // Retrieve the parameters of the order in question. + OrderParameters memory parameters = advancedOrder.parameters; + + // OrderType 2-3 require zone to be caller or approve via validateOrder. + if ( + _isRestrictedAndCallerNotZone(parameters.orderType, parameters.zone) + ) { + // Encode the `validateOrder` call in memory. + (MemoryPointer callData, uint256 size) = _encodeAuthorizeOrder( + orderHash, + parameters, + advancedOrder.extraData, + orderHashes, + orderIndex + ); + + // Perform call and ensure a corresponding magic value was returned. + _callAndCheckStatus(parameters.zone, orderHash, callData, size, InvalidRestrictedOrder_error_selector); + } + } + /** * @dev Internal function to determine the post-execution validity of * restricted and contract orders. Restricted orders where the caller @@ -110,7 +186,9 @@ contract ZoneInteraction is ) { // Encode the `validateOrder` call in memory. (callData, size) = _encodeValidateOrder( - orderHash, parameters, advancedOrder.extraData, orderHashes + parameters.toMemoryPointer().offset(OrderParameters_salt_offset) + .readUint256(), + orderHashes ); // Set the target to the zone. diff --git a/src/types/lib/ConsiderationConstants.sol b/src/types/lib/ConsiderationConstants.sol index 8b62089..4b50997 100644 --- a/src/types/lib/ConsiderationConstants.sol +++ b/src/types/lib/ConsiderationConstants.sol @@ -108,7 +108,7 @@ uint256 constant OrderParameters_consideration_head_offset = 0x60; uint256 constant OrderParameters_startTime_offset = 0xa0; uint256 constant OrderParameters_endTime_offset = 0xc0; uint256 constant OrderParameters_zoneHash_offset = 0xe0; -// uint256 constant OrderParameters_salt_offset = 0x100; +uint256 constant OrderParameters_salt_offset = 0x100; uint256 constant OrderParameters_conduit_offset = 0x120; uint256 constant OrderParameters_counter_offset = 0x140; @@ -524,7 +524,7 @@ uint256 constant ZoneParameters_endTime_offset = 0x100; uint256 constant ZoneParameters_zoneHash_offset = 0x120; uint256 constant ZoneParameters_base_tail_offset = 0x140; uint256 constant ZoneParameters_selectorAndPointer_length = 0x24; -uint256 constant ZoneParameters_basicOrderFixedElements_length = 0x64; +uint256 constant ZoneParameters_basicOrderFixedElements_length = 0x44; // ConsiderationDecoder Constants uint256 constant OrderParameters_head_size = 0x0160; From 9dd74f4dca1ff3244dcb98db4edf1862c9db17d7 Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Fri, 9 Feb 2024 19:59:03 -0500 Subject: [PATCH 2/8] read the pointer --- src/core/lib/BasicOrderFulfiller.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/lib/BasicOrderFulfiller.sol b/src/core/lib/BasicOrderFulfiller.sol index 8c6e207..cea8aa9 100644 --- a/src/core/lib/BasicOrderFulfiller.sol +++ b/src/core/lib/BasicOrderFulfiller.sol @@ -1022,7 +1022,7 @@ contract BasicOrderFulfiller is OrderValidator { OrderStatus storage orderStatus = _validateBasicOrder( orderHash, _toBytesReturnType(_decodeBytes)( - CalldataPointer.wrap(BasicOrder_signature_cdPtr) + CalldataPointer.wrap(CalldataPointer.wrap(BasicOrder_signature_cdPtr).readMaskedUint256()) ) ); From 87868b8f44ad5a174e32a5cc82021056c2226eb2 Mon Sep 17 00:00:00 2001 From: James Wenzel Date: Fri, 9 Feb 2024 20:29:56 -0800 Subject: [PATCH 3/8] manually add 0x24 offset to read signature pointer --- src/core/lib/BasicOrderFulfiller.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/lib/BasicOrderFulfiller.sol b/src/core/lib/BasicOrderFulfiller.sol index cea8aa9..5e113e6 100644 --- a/src/core/lib/BasicOrderFulfiller.sol +++ b/src/core/lib/BasicOrderFulfiller.sol @@ -1022,7 +1022,10 @@ contract BasicOrderFulfiller is OrderValidator { OrderStatus storage orderStatus = _validateBasicOrder( orderHash, _toBytesReturnType(_decodeBytes)( - CalldataPointer.wrap(CalldataPointer.wrap(BasicOrder_signature_cdPtr).readMaskedUint256()) + CalldataPointer.wrap( + CalldataPointer.wrap(BasicOrder_signature_cdPtr) + .readMaskedUint256() + 0x24 + ) ) ); From 72e38e4c099cd623ee4419c028d36802608ff78a Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Sat, 10 Feb 2024 00:22:38 -0500 Subject: [PATCH 4/8] clean up encoding logic --- src/core/lib/ConsiderationEncoder.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/lib/ConsiderationEncoder.sol b/src/core/lib/ConsiderationEncoder.sol index 9c68196..a204460 100644 --- a/src/core/lib/ConsiderationEncoder.sol +++ b/src/core/lib/ConsiderationEncoder.sol @@ -482,9 +482,9 @@ contract ConsiderationEncoder { // Track the pointer, size (when performing validateOrder) and pointer // to orderHashes length by overriding the salt value on the order. orderParameters.salt = ( - (ptr.readMaskedUint256() << 128) & - (size << 64) & - orderHashesLengthLocation.readMaskedUint256() + (MemoryPointer.unwrap(ptr) << 128) | + (size << 64) | + MemoryPointer.unwrap(orderHashesLengthLocation) ); // Write the shorted orderHashes array length. From 2db4801b4e725a74127c34db694f5f4255d45712 Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Sat, 10 Feb 2024 00:39:43 -0500 Subject: [PATCH 5/8] account for padding before selector --- src/core/lib/ConsiderationEncoder.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/lib/ConsiderationEncoder.sol b/src/core/lib/ConsiderationEncoder.sol index a204460..69bcf99 100644 --- a/src/core/lib/ConsiderationEncoder.sol +++ b/src/core/lib/ConsiderationEncoder.sol @@ -45,6 +45,7 @@ import { SpentItem_size_shift, SpentItem_size, validateOrder_selector, + validateOrder_selector_offset, ZoneParameters_base_tail_offset, ZoneParameters_basicOrderFixedElements_length, ZoneParameters_consideration_head_offset, @@ -477,7 +478,7 @@ contract ConsiderationEncoder { } // Update the free memory pointer. - setFreeMemoryPointer(ptr.offset(size)); + setFreeMemoryPointer(dst.offset(size)); // Track the pointer, size (when performing validateOrder) and pointer // to orderHashes length by overriding the salt value on the order. @@ -526,6 +527,8 @@ contract ConsiderationEncoder { // Write validateOrder selector. dst.write(validateOrder_selector); + dst = dst.offset(validateOrder_selector_offset); + // Encode the order hashes array. Note that this currently modifies // order hashes that are known to be properly encoded already and could // therefore be skipped. From 054cfc2c6104ab4a7e141c53d68b37538f096e51 Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Sat, 10 Feb 2024 01:05:00 -0500 Subject: [PATCH 6/8] patch up basic order issues --- src/core/lib/ConsiderationEncoder.sol | 14 +++++++------- src/core/lib/ZoneInteraction.sol | 12 ++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/core/lib/ConsiderationEncoder.sol b/src/core/lib/ConsiderationEncoder.sol index 69bcf99..4217f26 100644 --- a/src/core/lib/ConsiderationEncoder.sol +++ b/src/core/lib/ConsiderationEncoder.sol @@ -543,21 +543,21 @@ contract ConsiderationEncoder { * * @param orderHash The order hash. * - * @return dst A memory pointer referencing the encoded `authorizeOrder` - * calldata. + * @return ptr A memory pointer referencing the encoded `authorizeOrder` + * calldata with extra padding at the start to word align. * @return size The size of the bytes array. */ function _encodeAuthorizeBasicOrder( bytes32 orderHash ) internal view returns ( - MemoryPointer dst, + MemoryPointer ptr, uint256 size, uint256 memoryLocationForOrderHashes ) { // Get free memory pointer to write calldata to. - MemoryPointer ptr = getFreeMemoryPointer(); + ptr = getFreeMemoryPointer(); - dst = ptr; + MemoryPointer dst = ptr; // Write validateOrder selector and get pointer to start of calldata. dst.write(authorizeOrder_selector); @@ -632,7 +632,7 @@ contract ConsiderationEncoder { // Write offset to orderHashes. dstHead.offset(ZoneParameters_orderHashes_head_offset).write(tailOffset); - memoryLocationForOrderHashes = dstHead.offset(tailOffset).readMaskedUint256(); + memoryLocationForOrderHashes = MemoryPointer.unwrap(dstHead.offset(tailOffset)); // Write length = 0 to the orderHashes array. dstHead.offset(tailOffset).write(0); @@ -645,7 +645,7 @@ contract ConsiderationEncoder { size = ZoneParameters_basicOrderFixedElements_length + tailOffset; // Update the free memory pointer. - setFreeMemoryPointer(ptr.offset(size + OneWord)); + setFreeMemoryPointer(dst.offset(size + OneWord)); } } diff --git a/src/core/lib/ZoneInteraction.sol b/src/core/lib/ZoneInteraction.sol index 6965e5a..99cc7cc 100644 --- a/src/core/lib/ZoneInteraction.sol +++ b/src/core/lib/ZoneInteraction.sol @@ -19,12 +19,14 @@ import { ConsiderationEncoder } from "./ConsiderationEncoder.sol"; import { CalldataPointer, MemoryPointer, OffsetOrLengthMask } from "seaport-types/src/helpers/PointerLibraries.sol"; import { + authorizeOrder_selector_offset, BasicOrder_zone_cdPtr, ContractOrder_orderHash_offerer_shift, MaskOverFirstFourBytes, OneWord, OrderParameters_salt_offset, - OrderParameters_zone_offset + OrderParameters_zone_offset, + validateOrder_selector_offset } from "seaport-types/src/lib/ConsiderationConstants.sol"; import { @@ -72,15 +74,15 @@ contract ZoneInteraction is _callAndCheckStatus( CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress(), orderHash, - callData, + callData.offset(authorizeOrder_selector_offset), size, InvalidRestrictedOrder_error_selector ); - callDataPointer = callData.readMaskedUint256(); + callDataPointer = MemoryPointer.unwrap(callData); unchecked { - callData.write((size + OneWord) << 128 & memoryLocationForOrderHashes); + callData.write((size + OneWord) << 128 | memoryLocationForOrderHashes); } } } @@ -116,6 +118,8 @@ contract ZoneInteraction is // Encode the `validateOrder` call in memory. _encodeValidateBasicOrder(callData, memoryLocationForOrderHashes); + callData = callData.offset(validateOrder_selector_offset); + // Perform `validateOrder` call and ensure magic value was returned. _callAndCheckStatus( CalldataPointer.wrap(BasicOrder_zone_cdPtr).readAddress(), From 346094d4d4beae96e5902f9e8f6014fa1b94e0ed Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Sat, 10 Feb 2024 01:28:16 -0500 Subject: [PATCH 7/8] fix another issue --- src/core/lib/OrderFulfiller.sol | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/lib/OrderFulfiller.sol b/src/core/lib/OrderFulfiller.sol index a68e431..d943403 100644 --- a/src/core/lib/OrderFulfiller.sol +++ b/src/core/lib/OrderFulfiller.sol @@ -119,11 +119,13 @@ contract OrderFulfiller is recipient ); - bytes32[] memory orderHashes = new bytes32[](0); + // Declare empty bytes32 array and populate with the order hash. + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = orderHash; if (advancedOrder.parameters.orderType != OrderType.CONTRACT) { _assertRestrictedAdvancedOrderAuthorization( - advancedOrder, orderHashes, orderHash, 0 + advancedOrders[0], orderHashes, orderHash, 0 ); _updateStatus(orderHash, fillNumerator, fillDenominator, true); @@ -193,10 +195,6 @@ contract OrderFulfiller is _transferEach(orderParameters, fulfillerConduitKey); - // Declare empty bytes32 array and populate with the order hash. - orderHashes = new bytes32[](1); - orderHashes[0] = orderHash; - // Ensure restricted orders have a valid submitter or pass a zone check. _assertRestrictedAdvancedOrderValidity( advancedOrders[0], orderHashes, orderHash From 33a991567213fe82b92126f737e3d734bf726a4c Mon Sep 17 00:00:00 2001 From: 0age <0age@protonmail.com> Date: Sat, 10 Feb 2024 01:34:44 -0500 Subject: [PATCH 8/8] assign the order hash a bit later --- src/core/lib/OrderFulfiller.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lib/OrderFulfiller.sol b/src/core/lib/OrderFulfiller.sol index d943403..e4de2b0 100644 --- a/src/core/lib/OrderFulfiller.sol +++ b/src/core/lib/OrderFulfiller.sol @@ -121,7 +121,6 @@ contract OrderFulfiller is // Declare empty bytes32 array and populate with the order hash. bytes32[] memory orderHashes = new bytes32[](1); - orderHashes[0] = orderHash; if (advancedOrder.parameters.orderType != OrderType.CONTRACT) { _assertRestrictedAdvancedOrderAuthorization( @@ -195,6 +194,8 @@ contract OrderFulfiller is _transferEach(orderParameters, fulfillerConduitKey); + orderHashes[0] = orderHash; + // Ensure restricted orders have a valid submitter or pass a zone check. _assertRestrictedAdvancedOrderValidity( advancedOrders[0], orderHashes, orderHash