From c022e65d9e2f0a88007a7cd2a730edd212fbf00c Mon Sep 17 00:00:00 2001 From: Sam Jeston Date: Thu, 2 May 2024 08:57:29 +1000 Subject: [PATCH] Revert if context is empty for ImmutableSignedZoneV2 --- .../v2/ImmutableSignedZoneV2.sol | 8 ++- .../v2/ImmutableSignedZoneV2.t.sol | 65 +++++++++++++++++++ .../zones/immutable-signed-zone/v2/README.md | 3 +- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol index 3b3d1538..c0f298b3 100644 --- a/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol @@ -397,10 +397,14 @@ contract ImmutableSignedZoneV2 is uint256 startIndex = 0; uint256 contextLength = context.length; + // The ImmutableSignedZoneV2 contract enforces at least + // one of the supported substandards is present in the context. + if (contextLength == 0) { + revert InvalidExtraData("invalid context, no substandards present", zoneParameters.orderHash); + } + // Each _validateSubstandard* function returns the length of the substandard // segment (0 if the substandard was not matched). - - if (startIndex == contextLength) return; startIndex = _validateSubstandard3(context[startIndex:], zoneParameters) + startIndex; if (startIndex == contextLength) return; diff --git a/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol index bd8f6a2a..fefe742d 100644 --- a/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol @@ -556,6 +556,54 @@ contract ImmutableSignedZoneV2Test is zone.validateOrder(zoneParameters); } + function test_validateOrder_revertsIfContextIsEmpty() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); + bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); + vm.prank(OWNER); + zone.grantRole(managerRole, OWNER); + vm.prank(OWNER); + zone.addSigner(SIGNER); + + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + uint64 expiration = 100; + + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC1155, token: address(0x5), identifier: 222, amount: 10}); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC20, + token: address(0x4), + identifier: 0, + amount: 20, + recipient: payable(address(0x3)) + }); + receivedItems[0] = receivedItem; + + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + + bytes memory extraData = _buildExtraDataWithoutContext(zone, SIGNER_PRIVATE_KEY, FULFILLER, expiration, orderHash); + + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: spentItems, + consideration: receivedItems, + extraData: extraData, + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + vm.expectRevert( + abi.encodeWithSelector(InvalidExtraData.selector, "invalid context, no substandards present", zoneParameters.orderHash) + ); + zone.validateOrder(zoneParameters); + } + function test_validateOrder_returnsMagicValueOnSuccessfulValidation() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 managerRole = zone.ZONE_MANAGER_ROLE(); @@ -1435,6 +1483,23 @@ contract ImmutableSignedZoneV2Test is ); return extraData; } + + function _buildExtraDataWithoutContext( + ImmutableSignedZoneV2Harness zone, + uint256 signerPrivateKey, + address fulfiller, + uint64 expiration, + bytes32 orderHash, + ) private view returns (bytes memory) { + bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + bytes memory extraData = abi.encodePacked( + bytes1(0), + fulfiller, + expiration, + _signCompact(signerPrivateKey, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash)) + ); + return extraData; + } } // solhint-enable func-name-mixedcase diff --git a/test/trading/seaport/zones/immutable-signed-zone/v2/README.md b/test/trading/seaport/zones/immutable-signed-zone/v2/README.md index a5dc1cc8..4c7d4d07 100644 --- a/test/trading/seaport/zones/immutable-signed-zone/v2/README.md +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/README.md @@ -50,6 +50,7 @@ Operational function tests: | `test_validateOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller` | Validate order with unexpected fufiller. | No | Yes | | `test_validateOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller` | Validate order with expected *any* fufiller. | Yes | No | | `test_validateOrder_revertsIfSignerIsNotActive` | Validate order with inactive signer. | No | Yes | +| `test_validateOrder_revertsIfContextIsEmpty` | Validate order with an empty context. | No | Yes | | `test_validateOrder_returnsMagicValueOnSuccessfulValidation` | Validate order successfully. | Yes | Yes | Internal operational function tests: @@ -98,4 +99,4 @@ All of these tests are in [test/trading/seaport/ImmutableSeaportSignedZoneV2Inte | `test_fulfillAdvancedOrder_withCompleteFulfilment` | Full fulfilment. | Yes | Yes | | `test_fulfillAdvancedOrder_withPartialFill` | Partial fulfilment. | Yes | Yes | | `test_fulfillAdvancedOrder_withMultiplePartialFills` | Sequential partial fulfilments. | Yes | Yes | -| `test_fulfillAdvancedOrder_withOverfilling` | Over fulfilment. | Yes | Yes | \ No newline at end of file +| `test_fulfillAdvancedOrder_withOverfilling` | Over fulfilment. | Yes | Yes |