From d8a25256e8ec696c82dd989a80cd9c7f86ca7437 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 25 Mar 2024 18:30:34 +1100 Subject: [PATCH 01/79] Added skeleton and substandard 3 --- .../seaport/zones/ImmutableSignedZoneV2.sol | 538 ++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol new file mode 100644 index 00000000..c5c9604f --- /dev/null +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -0,0 +1,538 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; +import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; +import {SIP7EventsAndErrors} from "./interfaces/SIP7EventsAndErrors.sol"; +import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; +import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + +/** + * @title ImmutableSignedZoneV2 + * @author Immutable + * @notice ImmutableSignedZoneV2 is a zone implementation based on the + * SIP-7 standard https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md + * Implementing substandard 3, 4 and 6. + */ +contract ImmutableSignedZoneV2 is + ERC165, + SIP7EventsAndErrors, + SIP6EventsAndErrors, + ZoneInterface, + SIP5Interface, + SIP7Interface, + Ownable +{ + /// @dev The EIP-712 digest parameters. + bytes32 internal immutable _VERSION_HASH = keccak256(bytes("2.0")); + bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = + keccak256( + abi.encodePacked( + "EIP712Domain(", + "string name,", + "string version,", + "uint256 chainId,", + "address verifyingContract", + ")" + ) + ); + + bytes32 internal immutable _SIGNED_ORDER_TYPEHASH = + keccak256( + abi.encodePacked( + "SignedOrder(", + "address fulfiller,", + "uint64 expiration,", + "bytes32 orderHash,", + "bytes context", + ")" + ) + ); + + uint256 internal immutable _CHAIN_ID = block.chainid; + bytes32 internal immutable _DOMAIN_SEPARATOR; + uint8 internal immutable _ACCEPTED_SIP6_VERSION = 0; + + /// @dev The name for this zone returned in getSeaportMetadata(). + // solhint-disable-next-line var-name-mixedcase + string private _ZONE_NAME; + + // slither-disable-start immutable-states + // solhint-disable-next-line var-name-mixedcase + bytes32 internal _NAME_HASH; + // slither-disable-end immutable-states + + /// @dev The allowed signers. + // solhint-disable-next-line named-parameters-mapping + mapping(address => SignerInfo) private _signers; + + /// @dev The API endpoint where orders for this zone can be signed. + /// Request and response payloads are defined in SIP-7. + string private _sip7APIEndpoint; + + /// @dev The documentationURI; + string private _documentationURI; + + /** + * @notice Constructor to deploy the contract. + * + * @param zoneName The name for the zone returned in + * getSeaportMetadata(). + * @param apiEndpoint The API endpoint where orders for this zone can be + * signed. + * Request and response payloads are defined in SIP-7. + * @param owner The address of the owner of this contract. Specified in the + * constructor to be CREATE2 / CREATE3 compatible. + */ + constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) { + // Set the zone name. + _ZONE_NAME = zoneName; + // set name hash + _NAME_HASH = keccak256(bytes(zoneName)); + + // Set the API endpoint. + _sip7APIEndpoint = apiEndpoint; + _documentationURI = documentationURI; + + // Derive and set the domain separator. + _DOMAIN_SEPARATOR = _deriveDomainSeparator(); + + // Emit an event to signal a SIP-5 contract has been deployed. + emit SeaportCompatibleContractDeployed(); + + // Transfer ownership to the address specified in the constructor + _transferOwnership(owner); + } + + /** + * @notice Add a new signer to the zone. + * + * @param signer The new signer address to add. + */ + function addSigner(address signer) external override onlyOwner { + // Do not allow the zero address to be added as a signer. + if (signer == address(0)) { + revert SignerCannotBeZeroAddress(); + } + + // Revert if the signer is already added. + if (_signers[signer].active) { + revert SignerAlreadyActive(signer); + } + + // Revert if the signer was previously authorized. + // Specified in SIP-7 to prevent compromised signer from being + // Cycled back into use. + if (_signers[signer].previouslyActive) { + revert SignerCannotBeReauthorized(signer); + } + + // Set the signer info. + _signers[signer] = SignerInfo(true, true); + + // Emit an event that the signer was added. + emit SignerAdded(signer); + } + + /** + * @notice Remove an active signer from the zone. + * + * @param signer The signer address to remove. + */ + function removeSigner(address signer) external override onlyOwner { + // Revert if the signer is not active. + if (!_signers[signer].active) { + revert SignerNotActive(signer); + } + + // Set the signer's active status to false. + _signers[signer].active = false; + + // Emit an event that the signer was removed. + emit SignerRemoved(signer); + } + + /** + * @notice Check if a given order including extraData is currently valid. + * + * @dev This function is called by Seaport whenever any extraData is + * provided by the caller. + * + * @return validOrderMagicValue A magic value indicating if the order is + * currently valid. + */ + function validateOrder( + ZoneParameters calldata zoneParameters + ) external view override returns (bytes4 validOrderMagicValue) { + // Put the extraData and orderHash on the stack for cheaper access. + bytes calldata extraData = zoneParameters.extraData; + bytes32 orderHash = zoneParameters.orderHash; + + // Revert with an error if the extraData is empty. + if (extraData.length == 0) { + revert InvalidExtraData("extraData is empty", orderHash); + } + + // We expect the extraData to conform with SIP-6 as well as SIP-7 + // Therefore all SIP-7 related data is offset by one byte + // SIP-7 specifically requires SIP-6 as a prerequisite. + + // Revert with an error if the extraData does not have valid length. + if (extraData.length < 93) { + revert InvalidExtraData("extraData length must be at least 93 bytes", orderHash); + } + + // Revert if SIP6 version is not accepted (0) + if (uint8(extraData[0]) != _ACCEPTED_SIP6_VERSION) { + revert UnsupportedExtraDataVersion(uint8(extraData[0])); + } + + // extraData bytes 1-21: expected fulfiller + // (zero address means not restricted) + address expectedFulfiller = address(bytes20(extraData[1:21])); + + // extraData bytes 21-29: expiration timestamp (uint64) + uint64 expiration = uint64(bytes8(extraData[21:29])); + + // extraData bytes 29-93: signature + // (strictly requires 64 byte compact sig, ERC2098) + bytes calldata signature = extraData[29:93]; + + // extraData bytes 93-end: context (optional, variable length) + bytes calldata context = extraData[93:]; + + // Revert if expired. + // solhint-disable-next-line not-rely-on-time + if (block.timestamp > expiration) { + // solhint-disable-next-line not-rely-on-time + revert SignatureExpired(block.timestamp, expiration, orderHash); + } + + // Put fulfiller on the stack for more efficient access. + address actualFulfiller = zoneParameters.fulfiller; + + // Revert unless + // Expected fulfiller is 0 address (any fulfiller) or + // Expected fulfiller is the same as actual fulfiller + if (expectedFulfiller != address(0) && expectedFulfiller != actualFulfiller) { + revert InvalidFulfiller(expectedFulfiller, actualFulfiller, orderHash); + } + + // validate supported substandards + _validateSubstandards(context, zoneParameters); + + // Derive the signedOrder hash + bytes32 signedOrderHash = _deriveSignedOrderHash(expectedFulfiller, expiration, orderHash, context); + + // Derive the EIP-712 digest using the domain separator and signedOrder + // hash through openzepplin helper + bytes32 digest = ECDSA.toTypedDataHash(_domainSeparator(), signedOrderHash); + + // Recover the signer address from the digest and signature. + // Pass in R and VS from compact signature (ERC2098) + address recoveredSigner = ECDSA.recover(digest, bytes32(signature[0:32]), bytes32(signature[32:64])); + + // Revert if the signer is not active + // !This also reverts if the digest constructed on serverside is incorrect + if (!_signers[recoveredSigner].active) { + revert SignerNotActive(recoveredSigner); + } + + // All validation completes and passes with no reverts, return valid + validOrderMagicValue = ZoneInterface.validateOrder.selector; + } + + /** + * @dev Internal view function to get the EIP-712 domain separator. If the + * chainId matches the chainId set on deployment, the cached domain + * separator will be returned; otherwise, it will be derived from + * scratch. + * + * @return The domain separator. + */ + function _domainSeparator() internal view returns (bytes32) { + return block.chainid == _CHAIN_ID ? _DOMAIN_SEPARATOR : _deriveDomainSeparator(); + } + + /** + * @dev Returns Seaport metadata for this contract, returning the + * contract name and supported schemas. + * + * @return name The contract name + * @return schemas The supported SIPs + */ + function getSeaportMetadata() + external + view + override(SIP5Interface, ZoneInterface) + returns (string memory name, Schema[] memory schemas) + { + name = _ZONE_NAME; + + // supported SIP (7) + schemas = new Schema[](1); + schemas[0].id = 7; + + schemas[0].metadata = abi.encode(_domainSeparator(), _sip7APIEndpoint, _getSupportedSubstandards(), _documentationURI); + } + + /** + * @dev Internal view function to derive the EIP-712 domain separator. + * + * @return domainSeparator The derived domain separator. + */ + function _deriveDomainSeparator() internal view returns (bytes32 domainSeparator) { + return keccak256(abi.encode(_EIP_712_DOMAIN_TYPEHASH, _NAME_HASH, _VERSION_HASH, block.chainid, address(this))); + } + + /** + * @notice Update the API endpoint returned by this zone. + * + * @param newApiEndpoint The new API endpoint. + */ + function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyOwner { + // Update to the new API endpoint. + _sip7APIEndpoint = newApiEndpoint; + } + + /** + * @notice Returns signing information about the zone. + * + * @return domainSeparator The domain separator used for signing. + */ + function sip7Information() + external + view + override + returns ( + bytes32 domainSeparator, + string memory apiEndpoint, + uint256[] memory substandards, + string memory documentationURI + ) + { + domainSeparator = _domainSeparator(); + apiEndpoint = _sip7APIEndpoint; + + substandards = _getSupportedSubstandards(); + + documentationURI = _documentationURI; + } + + /** + * @dev validate substandards 3, 4 and 6 based on context + * + * @param context bytes payload of context + * @param zoneParameters zone parameters + */ + function _validateSubstandards( + bytes calldata context, + ZoneParameters calldata zoneParameters + ) internal pure { + + uint256 startIndex = 0; + + if (startIndex > context.length) { return; } + startIndex = _validateSubstandard3(context[startIndex:], zoneParameters) + startIndex; + + if (startIndex > context.length) { return; } + startIndex = _validateSubstandard4(context[startIndex:], zoneParameters) + startIndex; + + if (startIndex > context.length) { return; } + startIndex = _validateSubstandard6(context[startIndex:], zoneParameters) + startIndex; + + if (startIndex != context.length) { + revert InvalidExtraData("invalid context, unexpected context length", zoneParameters.orderHash); + } + + // substandard 4 - validate order hashes actual match expected + + // byte 33 to end are orderHashes array for substandard 4 + bytes calldata orderHashesBytes = context[32:]; + // context must be a multiple of 32 bytes + if (orderHashesBytes.length % 32 != 0) { + revert InvalidExtraData( + "invalid context, order hashes bytes not an array of bytes32 hashes", + zoneParameters.orderHash + ); + } + + // compute expected order hashes array based on context bytes + bytes32[] memory expectedOrderHashes = new bytes32[](orderHashesBytes.length / 32); + for (uint256 i = 0; i < orderHashesBytes.length / 32; i++) { + expectedOrderHashes[i] = bytes32(orderHashesBytes[i * 32:i * 32 + 32]); + } + + // revert if order hashes in context and payload do not match + // every expected order hash need to exist in fulfilling order hashes + if (!_everyElementExists(expectedOrderHashes, zoneParameters.orderHashes)) { + revert SubstandardViolation(4, "invalid order hashes", zoneParameters.orderHash); + } + } + + /** + * @dev Validates substandard 3 + * + * @param context bytes payload of context, 0 indexed to start of substandard segment + * @param zoneParameters zone parameters + * @return Length of substandard segment + */ + function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + if (uint8(context[0]) != 3) { + return 0; + } + + if (context.length < 33) { + // TODO: Does size of error message have a gas impact? + revert InvalidExtraData("invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", zoneParameters.orderHash); + } + + if (_deriveConsiderationHash(zoneParameters.consideration) != bytes32(context[1:33])) { + revert SubstandardViolation(3, "invalid consideration hash", zoneParameters.orderHash); + } + + return 33; + } + + /** + * @dev Validates substandard 4 + * + * @param context bytes payload of context, 0 indexed to start of substandard segment + * @param zoneParameters zone parameters + * @return Length of substandard segment + */ + function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + if (uint8(context[0]) != 4) { + return 0; + } + } + + /** + * @dev Validates substandard 6 + * + * @param context bytes payload of context, 0 indexed to start of substandard segment + * @param zoneParameters zone parameters + * @return Length of substandard segment + */ + function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + if (uint8(context[0]) != 6) { + return 0; + } + } + + /** + * @dev get the supported substandards of the contract + * + * @return substandards array of substandards supported + * + */ + function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { + // support substandards 3, 4 and 6 + substandards = new uint256[](3); + substandards[0] = 3; + substandards[1] = 4; + substandards[2] = 6; + } + + /** + * @dev Derive the signedOrder hash from the orderHash and expiration. + * + * @param fulfiller The expected fulfiller address. + * @param expiration The signature expiration timestamp. + * @param orderHash The order hash. + * @param context The optional variable-length context. + * + * @return signedOrderHash The signedOrder hash. + * + */ + function _deriveSignedOrderHash( + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes calldata context + ) internal view returns (bytes32 signedOrderHash) { + // Derive the signed order hash. + signedOrderHash = keccak256( + abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context)) + ); + } + + /** + * @dev Derive the consideration hash based on received item array + * + * @param receivedItems actual received item array + */ + function _deriveConsiderationHash(ReceivedItem[] calldata receivedItems) internal pure returns (bytes32) { + uint256 numberOfItems = receivedItems.length; + bytes memory considerationHash; + + for (uint256 i; i < numberOfItems; i++) { + considerationHash = abi.encodePacked( + considerationHash, + receivedItems[i].itemType, + receivedItems[i].token, + receivedItems[i].identifier, + receivedItems[i].amount, + receivedItems[i].recipient + ); + } + + return keccak256(considerationHash); + } + + /** + * @dev helper function to check if every element of array1 exists in array2 + * optimised for performance checking arrays sized 0-15 + * + * @param array1 subset array + * @param array2 superset array + */ + function _everyElementExists(bytes32[] memory array1, bytes32[] calldata array2) internal pure returns (bool) { + // cache the length in memory for loop optimisation + uint256 array1Size = array1.length; + uint256 array2Size = array2.length; + + // we can assume all items (order hashes) are unique + // therefore if subset is bigger than superset, revert + if (array1Size > array2Size) { + return false; + } + + // Iterate through each element and compare them + for (uint256 i = 0; i < array1Size; ) { + bool found = false; + bytes32 item = array1[i]; + for (uint256 j = 0; j < array2Size; ) { + if (item == array2[j]) { + // if item from array1 is in array2, break + found = true; + break; + } + unchecked { + j++; + } + } + if (!found) { + // if any item from array1 is not found in array2, return false + return false; + } + unchecked { + i++; + } + } + + // All elements from array1 exist in array2 + return true; + } + + // TODO: confirm this works for ERC165 + function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface) returns (bool) { + return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); + } +} From 0c28886b9a78efe0a60d01a8b4d69e3c054c4c94 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 13:04:09 +1100 Subject: [PATCH 02/79] Fix TS build --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 2620723b..d3c1893e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,5 +10,5 @@ "skipLibCheck": true }, "include": ["./index.ts", "./test/**/*.ts"], - "files": ["hardhat.config.ts"] + "files": ["./hardhat.config.ts"] } From 7b6ace6c1d4bd475d9a5ff23bd997d5bda020711 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 14:45:22 +1100 Subject: [PATCH 03/79] Add substandard 4 and 6 validation --- .../seaport/zones/ImmutableSignedZoneV2.sol | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index c5c9604f..5dc2cc0a 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -335,7 +335,6 @@ contract ImmutableSignedZoneV2 is bytes calldata context, ZoneParameters calldata zoneParameters ) internal pure { - uint256 startIndex = 0; if (startIndex > context.length) { return; } @@ -350,30 +349,6 @@ contract ImmutableSignedZoneV2 is if (startIndex != context.length) { revert InvalidExtraData("invalid context, unexpected context length", zoneParameters.orderHash); } - - // substandard 4 - validate order hashes actual match expected - - // byte 33 to end are orderHashes array for substandard 4 - bytes calldata orderHashesBytes = context[32:]; - // context must be a multiple of 32 bytes - if (orderHashesBytes.length % 32 != 0) { - revert InvalidExtraData( - "invalid context, order hashes bytes not an array of bytes32 hashes", - zoneParameters.orderHash - ); - } - - // compute expected order hashes array based on context bytes - bytes32[] memory expectedOrderHashes = new bytes32[](orderHashesBytes.length / 32); - for (uint256 i = 0; i < orderHashesBytes.length / 32; i++) { - expectedOrderHashes[i] = bytes32(orderHashesBytes[i * 32:i * 32 + 32]); - } - - // revert if order hashes in context and payload do not match - // every expected order hash need to exist in fulfilling order hashes - if (!_everyElementExists(expectedOrderHashes, zoneParameters.orderHashes)) { - revert SubstandardViolation(4, "invalid order hashes", zoneParameters.orderHash); - } } /** @@ -393,7 +368,7 @@ contract ImmutableSignedZoneV2 is revert InvalidExtraData("invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", zoneParameters.orderHash); } - if (_deriveConsiderationHash(zoneParameters.consideration) != bytes32(context[1:33])) { + if (_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1) != bytes32(context[1:33])) { revert SubstandardViolation(3, "invalid consideration hash", zoneParameters.orderHash); } @@ -411,6 +386,23 @@ contract ImmutableSignedZoneV2 is if (uint8(context[0]) != 4) { return 0; } + + // substandard ID + array offset + array length + if (context.length < 65) { + revert InvalidExtraData("invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", zoneParameters.orderHash); + } + + uint256 expectedOrderHashesSize = uint256(bytes32(context[33:65])); + uint256 substandardIndexEnd = 65 + (expectedOrderHashesSize * 32); + bytes32[] memory expectedOrderHashes = abi.decode(context[1:substandardIndexEnd + 1], (bytes32[])); + + // revert if order hashes in context and payload do not match + // every expected order hash need to exist in fulfilling order hashes + if (!_everyElementExists(expectedOrderHashes, zoneParameters.orderHashes)) { + revert SubstandardViolation(4, "invalid order hashes", zoneParameters.orderHash); + } + + return substandardIndexEnd + 1; } /** @@ -424,6 +416,19 @@ contract ImmutableSignedZoneV2 is if (uint8(context[0]) != 6) { return 0; } + + if (context.length < 65) { + revert InvalidExtraData("invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", zoneParameters.orderHash); + } + + uint256 originalFirstOfferItemAmount = uint256(bytes32(context[1:33])); + bytes32 expectedReceivedItemsHash = bytes32(context[33:65]); + + if (_deriveReceivedItemsHash(zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount) != expectedReceivedItemsHash) { + revert SubstandardViolation(6, "invalid consideration hash", zoneParameters.orderHash); + } + + return 65; } /** @@ -464,26 +469,28 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Derive the consideration hash based on received item array + * @dev Derive the received items hash based on received item array * * @param receivedItems actual received item array + * @param scalingFactorNumerator scaling factor numerator + * @param scalingFactorDenominator scaling factor denominator */ - function _deriveConsiderationHash(ReceivedItem[] calldata receivedItems) internal pure returns (bytes32) { + function _deriveReceivedItemsHash(ReceivedItem[] calldata receivedItems, uint256 scalingFactorNumerator, uint256 scalingFactorDenominator) internal pure returns (bytes32) { uint256 numberOfItems = receivedItems.length; - bytes memory considerationHash; + bytes memory receivedItemsHash; for (uint256 i; i < numberOfItems; i++) { - considerationHash = abi.encodePacked( - considerationHash, + receivedItemsHash = abi.encodePacked( + receivedItemsHash, receivedItems[i].itemType, receivedItems[i].token, receivedItems[i].identifier, - receivedItems[i].amount, + receivedItems[i].amount * scalingFactorNumerator / scalingFactorDenominator, receivedItems[i].recipient ); } - return keccak256(considerationHash); + return keccak256(receivedItemsHash); } /** From 4aa138d12d94da6dff428622d024894925f7ff0e Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 14:53:58 +1100 Subject: [PATCH 04/79] Safe maths --- .../seaport/zones/ImmutableSignedZoneV2.sol | 3 +- package.json | 1 + yarn.lock | 43 +++++++++++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 5dc2cc0a..576ac167 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -12,6 +12,7 @@ import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; /** * @title ImmutableSignedZoneV2 @@ -485,7 +486,7 @@ contract ImmutableSignedZoneV2 is receivedItems[i].itemType, receivedItems[i].token, receivedItems[i].identifier, - receivedItems[i].amount * scalingFactorNumerator / scalingFactorDenominator, + Math.mulDiv(receivedItems[i].amount, scalingFactorNumerator, scalingFactorDenominator), receivedItems[i].recipient ); } diff --git a/package.json b/package.json index c47bebdb..e43424e8 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@openzeppelin/contracts-upgradeable": "^4.9.3", "@rari-capital/solmate": "^6.4.0", "eslint-plugin-mocha": "^10.2.0", + "openzeppelin-contracts-5.0.2": "npm:@openzeppelin/contracts@^5.0.2", "openzeppelin-contracts-upgradeable-4.9.3": "npm:@openzeppelin/contracts-upgradeable@^4.9.3", "seaport": "https://github.com/immutable/seaport.git#1.5.0+im.1.3", "solidity-bits": "^0.4.0", diff --git a/yarn.lock b/yarn.lock index 632dc7eb..c16f2a84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1071,7 +1071,7 @@ find-up "^4.1.0" fs-extra "^8.1.0" -"@openzeppelin/contracts-upgradeable@^4.9.3", "openzeppelin-contracts-upgradeable-4.9.3@npm:@openzeppelin/contracts-upgradeable@^4.9.3": +"@openzeppelin/contracts-upgradeable@^4.9.3": version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== @@ -6335,6 +6335,16 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +"openzeppelin-contracts-5.0.2@npm:@openzeppelin/contracts@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210" + integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA== + +"openzeppelin-contracts-upgradeable-4.9.3@npm:@openzeppelin/contracts-upgradeable@^4.9.3": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" + integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7639,7 +7649,7 @@ string-format@^2.0.0: resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7665,6 +7675,15 @@ string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -7715,7 +7734,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7736,6 +7755,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -8918,7 +8944,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -8935,6 +8961,15 @@ wrap-ansi@^2.0.0: string-width "^1.0.1" strip-ansi "^3.0.1" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From fadfb6eaefbad346cad1e27d2e8c53ff3db44d7e Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 15:25:52 +1100 Subject: [PATCH 05/79] Update array comparison --- .../seaport/zones/ImmutableSignedZoneV2.sol | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 576ac167..ec3c6bd7 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -397,9 +397,8 @@ contract ImmutableSignedZoneV2 is uint256 substandardIndexEnd = 65 + (expectedOrderHashesSize * 32); bytes32[] memory expectedOrderHashes = abi.decode(context[1:substandardIndexEnd + 1], (bytes32[])); - // revert if order hashes in context and payload do not match - // every expected order hash need to exist in fulfilling order hashes - if (!_everyElementExists(expectedOrderHashes, zoneParameters.orderHashes)) { + // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes + if (!_bytes32ArrayIncludes(zoneParameters.orderHashes, expectedOrderHashes)) { revert SubstandardViolation(4, "invalid order hashes", zoneParameters.orderHash); } @@ -495,30 +494,30 @@ contract ImmutableSignedZoneV2 is } /** - * @dev helper function to check if every element of array1 exists in array2 + * @dev helper function to check if every element of values exists in sourceArray * optimised for performance checking arrays sized 0-15 * - * @param array1 subset array - * @param array2 superset array + * @param sourceArray source array + * @param values values array */ - function _everyElementExists(bytes32[] memory array1, bytes32[] calldata array2) internal pure returns (bool) { + function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) internal pure returns (bool) { // cache the length in memory for loop optimisation - uint256 array1Size = array1.length; - uint256 array2Size = array2.length; + uint256 sourceArraySize = sourceArray.length; + uint256 valuesSize = values.length; - // we can assume all items (order hashes) are unique - // therefore if subset is bigger than superset, revert - if (array1Size > array2Size) { + // we can assume all items are unique + // therefore if values is bigger than superset sourceArray, return false + if (valuesSize > sourceArraySize) { return false; } // Iterate through each element and compare them - for (uint256 i = 0; i < array1Size; ) { + for (uint256 i = 0; i < valuesSize; ) { bool found = false; - bytes32 item = array1[i]; - for (uint256 j = 0; j < array2Size; ) { - if (item == array2[j]) { - // if item from array1 is in array2, break + bytes32 item = values[i]; + for (uint256 j = 0; j < sourceArraySize; ) { + if (item == sourceArray[j]) { + // if item from values is in sourceArray, break found = true; break; } @@ -527,7 +526,7 @@ contract ImmutableSignedZoneV2 is } } if (!found) { - // if any item from array1 is not found in array2, return false + // if any item from values is not found in sourceArray, return false return false; } unchecked { @@ -535,7 +534,7 @@ contract ImmutableSignedZoneV2 is } } - // All elements from array1 exist in array2 + // All elements from values exist in sourceArray return true; } From 65171970ef48f2d17414f1e8e6973a320f4f35fe Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 16:02:32 +1100 Subject: [PATCH 06/79] Re-order functions for clarity --- .../seaport/zones/ImmutableSignedZoneV2.sol | 210 +++++++++--------- 1 file changed, 106 insertions(+), 104 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index ec3c6bd7..46fd60df 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -159,6 +159,69 @@ contract ImmutableSignedZoneV2 is emit SignerRemoved(signer); } + /** + * @notice Update the API endpoint returned by this zone. + * + * @param newApiEndpoint The new API endpoint. + */ + function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyOwner { + // Update to the new API endpoint. + _sip7APIEndpoint = newApiEndpoint; + } + + /** + * @dev Returns Seaport metadata for this contract, returning the + * contract name and supported schemas. + * + * @return name The contract name + * @return schemas The supported SIPs + */ + function getSeaportMetadata() + external + view + override(SIP5Interface, ZoneInterface) + returns (string memory name, Schema[] memory schemas) + { + name = _ZONE_NAME; + + // supported SIP (7) + schemas = new Schema[](1); + schemas[0].id = 7; + schemas[0].metadata = abi.encode(_domainSeparator(), _sip7APIEndpoint, _getSupportedSubstandards(), _documentationURI); + } + + /** + * @notice Returns signing information about the zone. + * + * @return domainSeparator The domain separator used for signing. + */ + function sip7Information() + external + view + override + returns ( + bytes32 domainSeparator, + string memory apiEndpoint, + uint256[] memory substandards, + string memory documentationURI + ) + { + domainSeparator = _domainSeparator(); + apiEndpoint = _sip7APIEndpoint; + + substandards = _getSupportedSubstandards(); + + documentationURI = _documentationURI; + } + + /** + * @notice ERC-165 interface support + * @param interfaceId The interface ID to check for support. + */ + function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface) returns (bool) { + return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); + } + /** * @notice Check if a given order including extraData is currently valid. * @@ -250,80 +313,40 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Internal view function to get the EIP-712 domain separator. If the - * chainId matches the chainId set on deployment, the cached domain - * separator will be returned; otherwise, it will be derived from - * scratch. + * @dev get the supported substandards of the contract * - * @return The domain separator. - */ - function _domainSeparator() internal view returns (bytes32) { - return block.chainid == _CHAIN_ID ? _DOMAIN_SEPARATOR : _deriveDomainSeparator(); - } - - /** - * @dev Returns Seaport metadata for this contract, returning the - * contract name and supported schemas. + * @return substandards array of substandards supported * - * @return name The contract name - * @return schemas The supported SIPs */ - function getSeaportMetadata() - external - view - override(SIP5Interface, ZoneInterface) - returns (string memory name, Schema[] memory schemas) - { - name = _ZONE_NAME; - - // supported SIP (7) - schemas = new Schema[](1); - schemas[0].id = 7; - - schemas[0].metadata = abi.encode(_domainSeparator(), _sip7APIEndpoint, _getSupportedSubstandards(), _documentationURI); + function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { + // support substandards 3, 4 and 6 + substandards = new uint256[](3); + substandards[0] = 3; + substandards[1] = 4; + substandards[2] = 6; } /** - * @dev Internal view function to derive the EIP-712 domain separator. + * @dev Derive the signedOrder hash from the orderHash and expiration. * - * @return domainSeparator The derived domain separator. - */ - function _deriveDomainSeparator() internal view returns (bytes32 domainSeparator) { - return keccak256(abi.encode(_EIP_712_DOMAIN_TYPEHASH, _NAME_HASH, _VERSION_HASH, block.chainid, address(this))); - } - - /** - * @notice Update the API endpoint returned by this zone. + * @param fulfiller The expected fulfiller address. + * @param expiration The signature expiration timestamp. + * @param orderHash The order hash. + * @param context The optional variable-length context. * - * @param newApiEndpoint The new API endpoint. - */ - function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyOwner { - // Update to the new API endpoint. - _sip7APIEndpoint = newApiEndpoint; - } - - /** - * @notice Returns signing information about the zone. + * @return signedOrderHash The signedOrder hash. * - * @return domainSeparator The domain separator used for signing. */ - function sip7Information() - external - view - override - returns ( - bytes32 domainSeparator, - string memory apiEndpoint, - uint256[] memory substandards, - string memory documentationURI - ) - { - domainSeparator = _domainSeparator(); - apiEndpoint = _sip7APIEndpoint; - - substandards = _getSupportedSubstandards(); - - documentationURI = _documentationURI; + function _deriveSignedOrderHash( + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes calldata context + ) internal view returns (bytes32 signedOrderHash) { + // Derive the signed order hash. + signedOrderHash = keccak256( + abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context)) + ); } /** @@ -431,43 +454,6 @@ contract ImmutableSignedZoneV2 is return 65; } - /** - * @dev get the supported substandards of the contract - * - * @return substandards array of substandards supported - * - */ - function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { - // support substandards 3, 4 and 6 - substandards = new uint256[](3); - substandards[0] = 3; - substandards[1] = 4; - substandards[2] = 6; - } - - /** - * @dev Derive the signedOrder hash from the orderHash and expiration. - * - * @param fulfiller The expected fulfiller address. - * @param expiration The signature expiration timestamp. - * @param orderHash The order hash. - * @param context The optional variable-length context. - * - * @return signedOrderHash The signedOrder hash. - * - */ - function _deriveSignedOrderHash( - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes calldata context - ) internal view returns (bytes32 signedOrderHash) { - // Derive the signed order hash. - signedOrderHash = keccak256( - abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context)) - ); - } - /** * @dev Derive the received items hash based on received item array * @@ -538,8 +524,24 @@ contract ImmutableSignedZoneV2 is return true; } - // TODO: confirm this works for ERC165 - function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface) returns (bool) { - return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); + /** + * @dev Internal view function to get the EIP-712 domain separator. If the + * chainId matches the chainId set on deployment, the cached domain + * separator will be returned; otherwise, it will be derived from + * scratch. + * + * @return The domain separator. + */ + function _domainSeparator() internal view returns (bytes32) { + return block.chainid == _CHAIN_ID ? _DOMAIN_SEPARATOR : _deriveDomainSeparator(); + } + + /** + * @dev Internal view function to derive the EIP-712 domain separator. + * + * @return domainSeparator The derived domain separator. + */ + function _deriveDomainSeparator() internal view returns (bytes32 domainSeparator) { + return keccak256(abi.encode(_EIP_712_DOMAIN_TYPEHASH, _NAME_HASH, _VERSION_HASH, block.chainid, address(this))); } } From f7ea5e6d8d11c9068476521bb0a3d5f9ca0ef4db Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 26 Mar 2024 20:49:21 +1100 Subject: [PATCH 07/79] Hardhat 0.8.20 compiler config --- hardhat.config.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index e513296e..2dc610a3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -35,6 +35,15 @@ const config: HardhatUserConfig = { }, }, }, + { + version: "0.8.20", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, { version: "0.8.17", settings: { From 348fa1588a7612f34776df79a056b169504bd65d Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 28 Mar 2024 09:43:08 +1100 Subject: [PATCH 08/79] Revert tsconfig change --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index d3c1893e..2620723b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,5 +10,5 @@ "skipLibCheck": true }, "include": ["./index.ts", "./test/**/*.ts"], - "files": ["./hardhat.config.ts"] + "files": ["hardhat.config.ts"] } From 06ff5df9e4803338323d92446796be14b15bfe55 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 28 Mar 2024 11:18:21 +1100 Subject: [PATCH 09/79] Switch access control to AccessControlEnumerable --- .../seaport/zones/ImmutableSignedZoneV2.sol | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 46fd60df..39168657 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -9,7 +9,7 @@ import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; import {SIP7EventsAndErrors} from "./interfaces/SIP7EventsAndErrors.sol"; import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; @@ -28,7 +28,7 @@ contract ImmutableSignedZoneV2 is ZoneInterface, SIP5Interface, SIP7Interface, - Ownable + AccessControlEnumerable { /// @dev The EIP-712 digest parameters. bytes32 internal immutable _VERSION_HASH = keccak256(bytes("2.0")); @@ -94,6 +94,7 @@ contract ImmutableSignedZoneV2 is constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) { // Set the zone name. _ZONE_NAME = zoneName; + // set name hash _NAME_HASH = keccak256(bytes(zoneName)); @@ -107,8 +108,8 @@ contract ImmutableSignedZoneV2 is // Emit an event to signal a SIP-5 contract has been deployed. emit SeaportCompatibleContractDeployed(); - // Transfer ownership to the address specified in the constructor - _transferOwnership(owner); + // Grant admin role to the specified owner + _grantRole(DEFAULT_ADMIN_ROLE, owner); } /** @@ -116,7 +117,7 @@ contract ImmutableSignedZoneV2 is * * @param signer The new signer address to add. */ - function addSigner(address signer) external override onlyOwner { + function addSigner(address signer) external override onlyRole(DEFAULT_ADMIN_ROLE) { // Do not allow the zero address to be added as a signer. if (signer == address(0)) { revert SignerCannotBeZeroAddress(); @@ -146,7 +147,7 @@ contract ImmutableSignedZoneV2 is * * @param signer The signer address to remove. */ - function removeSigner(address signer) external override onlyOwner { + function removeSigner(address signer) external override onlyRole(DEFAULT_ADMIN_ROLE) { // Revert if the signer is not active. if (!_signers[signer].active) { revert SignerNotActive(signer); @@ -164,7 +165,7 @@ contract ImmutableSignedZoneV2 is * * @param newApiEndpoint The new API endpoint. */ - function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyOwner { + function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyRole(DEFAULT_ADMIN_ROLE) { // Update to the new API endpoint. _sip7APIEndpoint = newApiEndpoint; } @@ -218,7 +219,7 @@ contract ImmutableSignedZoneV2 is * @notice ERC-165 interface support * @param interfaceId The interface ID to check for support. */ - function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface) returns (bool) { + function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface, AccessControlEnumerable) returns (bool) { return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); } From 11b88a8c2b8df7f5b098adb76f7928ebf8789a1a Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 28 Mar 2024 11:40:44 +1100 Subject: [PATCH 10/79] Forge install openzeppelin contracts 5.0.2 --- .gitmodules | 3 +++ lib/openzeppelin-contracts-5.0.2 | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/openzeppelin-contracts-5.0.2 diff --git a/.gitmodules b/.gitmodules index 24318171..dacdffa1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "lib/immutable-seaport-core-1.5.0+im1"] path = lib/immutable-seaport-core-1.5.0+im1 url = https://github.com/immutable/seaport-core +[submodule "lib/openzeppelin-contracts-5.0.2"] + path = lib/openzeppelin-contracts-5.0.2 + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts-5.0.2 b/lib/openzeppelin-contracts-5.0.2 new file mode 160000 index 00000000..dbb6104c --- /dev/null +++ b/lib/openzeppelin-contracts-5.0.2 @@ -0,0 +1 @@ +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 From bdaa64c88fa17124451f39eb60560360e747530a Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 28 Mar 2024 11:45:35 +1100 Subject: [PATCH 11/79] Add openzeppelin contracts 5.0.2 remapping --- remappings.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/remappings.txt b/remappings.txt index a84ebf6c..3323b1c8 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ @openzeppelin/contracts/=lib/openzeppelin-contracts-4.9.3/contracts/ +openzeppelin-contracts-5.0.2/=lib/openzeppelin-contracts-5.0.2/contracts/ openzeppelin-contracts-upgradeable-4.9.3/=lib/openzeppelin-contracts-upgradeable-4.9.3/contracts/ solidity-bits/=lib/solidity-bits/ solidity-bytes-utils/=lib/solidity-bytes-utils/ From f5aa6e5553e3e2a531920346aaa8c643c278e0d7 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 12:26:02 +1100 Subject: [PATCH 12/79] Basic test for substandard 3 --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol new file mode 100644 index 00000000..a87d05f4 --- /dev/null +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -0,0 +1,240 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; +import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; + +contract ImmutableSignedZoneV2Test is Test { + event SeaportCompatibleContractDeployed(); // SIP-5 + error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 + error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) + + function test_contructor_grantsAdminRoleToOwner() public { + address owner = address(0x2); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + bool ownerHasAdminRole = zone.hasRole(zone.DEFAULT_ADMIN_ROLE(), owner); + assertTrue(ownerHasAdminRole); + } + + function test_contructor_emitsSeaportCompatibleContractDeployedEvent() public { + vm.expectEmit(); + emit SeaportCompatibleContractDeployed(); + new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + } + + function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = new bytes(0x04); + uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); + assertEq(substandardLengthResult, 0); + } + + function test_validateSubstandard3_revertsIfContextLengthIsInvalid() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = abi.encodePacked(bytes1(0x03), bytes10(0)); + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, + "invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", + zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard3(context, zoneParameters); + } + + function test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ReceivedItem[] memory consideration = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + consideration[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: consideration, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = abi.encodePacked(bytes1(0x03), bytes32(0)); + vm.expectRevert( + abi.encodeWithSelector( + SubstandardViolation.selector, + 3, + "invalid consideration hash", + zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard3(context, zoneParameters); + } + + function test_validateSubstandard3_returns33OnSuccess() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ReceivedItem[] memory consideration = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + consideration[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: consideration, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); + uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); + assertEq(substandardLengthResult, 33); + } +} + +contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { + constructor( + string memory zoneName, + string memory apiEndpoint, + string memory documentationURI, + address owner + ) ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) {} + + function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards) { + return _getSupportedSubstandards(); + } + + function exposed_deriveSignedOrderHash( + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes calldata context + ) external view returns (bytes32 signedOrderHash) { + return _deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + } + + function exposed_validateSubstandards( + bytes calldata context, + ZoneParameters calldata zoneParameters + ) external pure { + return _validateSubstandards(context, zoneParameters); + } + + function exposed_validateSubstandard3( + bytes calldata context, + ZoneParameters calldata zoneParameters + ) external pure returns (uint256) { + return _validateSubstandard3(context, zoneParameters); + } + + function exposed_validateSubstandard4( + bytes calldata context, + ZoneParameters calldata zoneParameters + ) external pure returns (uint256) { + return _validateSubstandard4(context, zoneParameters); + } + + function exposed_validateSubstandard6( + bytes calldata context, + ZoneParameters calldata zoneParameters + ) external pure returns (uint256) { + return _validateSubstandard6(context, zoneParameters); + } + + function exposed_deriveReceivedItemsHash( + ReceivedItem[] calldata receivedItems, + uint256 scalingFactorNumerator, + uint256 scalingFactorDenominator + ) external pure returns (bytes32) { + return _deriveReceivedItemsHash(receivedItems, scalingFactorNumerator, scalingFactorDenominator); + } + + function exposed_bytes32ArrayIncludes( + bytes32[] calldata sourceArray, + bytes32[] memory values + ) external pure returns (bool) { + return _bytes32ArrayIncludes(sourceArray, values); + } + + function exposed_domainSeparator() external view returns (bytes32) { + return _domainSeparator(); + } + + function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator) { + return _deriveDomainSeparator(); + } +} From 5d8cb504ccb8ebd134463a261537543676eaef93 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 13:56:39 +1100 Subject: [PATCH 13/79] Split work --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index a87d05f4..18502a35 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -13,6 +13,8 @@ contract ImmutableSignedZoneV2Test is Test { error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) + /* constructor */ + function test_contructor_grantsAdminRoleToOwner() public { address owner = address(0x2); ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( @@ -36,6 +38,28 @@ contract ImmutableSignedZoneV2Test is Test { ); } + /* addSigner - L */ + + /* removeSigner - L */ + + /* updateAPIEndpoint - N */ + + /* getSeaportMetadata - L */ + + /* sip7Information - L */ + + /* supportsInterface - L */ + + /* validateOrder */ + + /* _getSupportedSubstandards - L */ + + /* _deriveSignedOrderHash - N */ + + /* _validateSubstandards */ + + /* _validateSubstandard3 */ + function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( "MyZoneName", @@ -164,6 +188,18 @@ contract ImmutableSignedZoneV2Test is Test { uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); assertEq(substandardLengthResult, 33); } + + /* _validateSubstandard4 - N */ + + /* _validateSubstandard6 - N */ + + /* _deriveReceivedItemsHash - N */ + + /* _bytes32ArrayIncludes - N */ + + /* _domainSeparator - N */ + + /* _deriveDomainSeparator - N */ } contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { From 4f3ba6ed5a8a544664573363fb7fc27c2f364fb0 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 14:42:29 +1100 Subject: [PATCH 14/79] Format with forge --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 18502a35..5bc14e49 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -10,6 +10,7 @@ import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; contract ImmutableSignedZoneV2Test is Test { event SeaportCompatibleContractDeployed(); // SIP-5 + error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) @@ -145,10 +146,7 @@ contract ImmutableSignedZoneV2Test is Test { bytes memory context = abi.encodePacked(bytes1(0x03), bytes32(0)); vm.expectRevert( abi.encodeWithSelector( - SubstandardViolation.selector, - 3, - "invalid consideration hash", - zoneParameters.orderHash + SubstandardViolation.selector, 3, "invalid consideration hash", zoneParameters.orderHash ) ); zone.exposed_validateSubstandard3(context, zoneParameters); @@ -203,12 +201,9 @@ contract ImmutableSignedZoneV2Test is Test { } contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { - constructor( - string memory zoneName, - string memory apiEndpoint, - string memory documentationURI, - address owner - ) ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) {} + constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) + ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) + {} function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards) { return _getSupportedSubstandards(); @@ -223,31 +218,34 @@ contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { return _deriveSignedOrderHash(fulfiller, expiration, orderHash, context); } - function exposed_validateSubstandards( - bytes calldata context, - ZoneParameters calldata zoneParameters - ) external pure { + function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + { return _validateSubstandards(context, zoneParameters); } - function exposed_validateSubstandard3( - bytes calldata context, - ZoneParameters calldata zoneParameters - ) external pure returns (uint256) { + function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { return _validateSubstandard3(context, zoneParameters); } - function exposed_validateSubstandard4( - bytes calldata context, - ZoneParameters calldata zoneParameters - ) external pure returns (uint256) { + function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { return _validateSubstandard4(context, zoneParameters); } - function exposed_validateSubstandard6( - bytes calldata context, - ZoneParameters calldata zoneParameters - ) external pure returns (uint256) { + function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { return _validateSubstandard6(context, zoneParameters); } @@ -259,10 +257,11 @@ contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { return _deriveReceivedItemsHash(receivedItems, scalingFactorNumerator, scalingFactorDenominator); } - function exposed_bytes32ArrayIncludes( - bytes32[] calldata sourceArray, - bytes32[] memory values - ) external pure returns (bool) { + function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) + external + pure + returns (bool) + { return _bytes32ArrayIncludes(sourceArray, values); } From 79ea23a28e3f0ef50c777103c5fb426860b8b40a Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 15:08:04 +1100 Subject: [PATCH 15/79] Add signer tests --- .../seaport/zones/ImmutableSignedZoneV2.sol | 2 +- test/payment-splitter/PaymentSplitter.t.sol | 6 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 84 +++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 39168657..eabdd24d 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -123,7 +123,7 @@ contract ImmutableSignedZoneV2 is revert SignerCannotBeZeroAddress(); } - // Revert if the signer is already added. + // Revert if the signer is already active. if (_signers[signer].active) { revert SignerAlreadyActive(signer); } diff --git a/test/payment-splitter/PaymentSplitter.t.sol b/test/payment-splitter/PaymentSplitter.t.sol index 4125cae9..4910aa30 100644 --- a/test/payment-splitter/PaymentSplitter.t.sol +++ b/test/payment-splitter/PaymentSplitter.t.sol @@ -165,7 +165,7 @@ contract PaymentSplitterTest is Test { mockToken1.mint(address(paymentSplitter), 100); mockToken2.mint(address(paymentSplitter), 100); - + vm.prank(fundsAdmin); vm.expectEmit(true, true, true, false, address(paymentSplitter)); @@ -203,7 +203,7 @@ contract PaymentSplitterTest is Test { mockToken1.mint(address(paymentSplitter), 100); mockToken2.mint(address(paymentSplitter), 100); - + vm.prank(fundsAdmin); paymentSplitter.releaseAll(); @@ -295,7 +295,7 @@ contract PaymentSplitterTest is Test { function testReceiveNativeTokenEvent() public { vm.deal(address(this), 100); - vm.expectEmit(true, true, false, false, address(paymentSplitter)); + vm.expectEmit(true, true, false, false, address(paymentSplitter)); emit PaymentReceived(address(this), 100); Address.sendValue(payable(address(paymentSplitter)), 100); } diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 5bc14e49..0f0987fc 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -10,7 +10,11 @@ import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; contract ImmutableSignedZoneV2Test is Test { event SeaportCompatibleContractDeployed(); // SIP-5 + event SignerAdded(address signer); // SIP-7 + error SignerCannotBeZeroAddress(); // SIP-7 + error SignerAlreadyActive(address signer); // SIP-7 + error SignerCannotBeReauthorized(address signer); // SIP-7 error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) @@ -41,6 +45,86 @@ contract ImmutableSignedZoneV2Test is Test { /* addSigner - L */ + function test_addSigner_revertsIfCalledByNonAdminRole() public { + address owner = makeAddr("owner"); + address randomAddress = makeAddr("random"); + address signerToAdd = makeAddr("signerToAdd"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.expectRevert( + "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" + ); + vm.prank(randomAddress); + zone.addSigner(signerToAdd); + } + + function test_addSigner_revertsIfSignerIsTheZeroAddress() public { + address owner = makeAddr("owner"); + address signerToAdd = address(0); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.expectRevert(abi.encodeWithSelector(SignerCannotBeZeroAddress.selector)); + vm.prank(owner); + zone.addSigner(signerToAdd); + } + + function test_addSigner_emitsSignerAddedEvent() public { + address owner = makeAddr("owner"); + address signerToAdd = makeAddr("signerToAdd"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.expectEmit(address(zone)); + emit SignerAdded(signerToAdd); + vm.prank(owner); + zone.addSigner(signerToAdd); + } + + function test_addSigner_revertsIfSignerAlreadyActive() public { + address owner = makeAddr("owner"); + address signerToAdd = makeAddr("signerToAdd"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.prank(owner); + zone.addSigner(signerToAdd); + vm.expectRevert(abi.encodeWithSelector(SignerAlreadyActive.selector, signerToAdd)); + vm.prank(owner); + zone.addSigner(signerToAdd); + } + + function test_addSigner_revertsIfSignerWasPreviouslyActive() public { + address owner = makeAddr("owner"); + address signerToAdd = makeAddr("signerToAdd"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.prank(owner); + zone.addSigner(signerToAdd); + vm.prank(owner); + zone.removeSigner(signerToAdd); + vm.expectRevert(abi.encodeWithSelector(SignerCannotBeReauthorized.selector, signerToAdd)); + vm.prank(owner); + zone.addSigner(signerToAdd); + } + /* removeSigner - L */ /* updateAPIEndpoint - N */ From 2660f60ff78bee44cc07335fbd934226214a624c Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 15:21:27 +1100 Subject: [PATCH 16/79] Add removeSigner tests --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 0f0987fc..72c043ec 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -11,10 +11,12 @@ import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; contract ImmutableSignedZoneV2Test is Test { event SeaportCompatibleContractDeployed(); // SIP-5 event SignerAdded(address signer); // SIP-7 + event SignerRemoved(address signer); // SIP-7 error SignerCannotBeZeroAddress(); // SIP-7 error SignerAlreadyActive(address signer); // SIP-7 error SignerCannotBeReauthorized(address signer); // SIP-7 + error SignerNotActive(address signer); // SIP-7 error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) @@ -127,6 +129,54 @@ contract ImmutableSignedZoneV2Test is Test { /* removeSigner - L */ + function test_removeSigner_revertsIfCalledByNonAdminRole() public { + address owner = makeAddr("owner"); + address randomAddress = makeAddr("random"); + address signerToRemove = makeAddr("signerToRemove"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.expectRevert( + "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" + ); + vm.prank(randomAddress); + zone.removeSigner(signerToRemove); + } + + function test_removeSigner_revertsIfSignerNotActive() public { + address owner = makeAddr("owner"); + address signerToRemove = makeAddr("signerToRemove"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.expectRevert(abi.encodeWithSelector(SignerNotActive.selector, signerToRemove)); + vm.prank(owner); + zone.removeSigner(signerToRemove); + } + + function test_removeSigner_emitsSignerRemovedEvent() public { + address owner = makeAddr("owner"); + address signerToRemove = makeAddr("signerToRemove"); + ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + vm.prank(owner); + zone.addSigner(signerToRemove); + vm.expectEmit(address(zone)); + emit SignerRemoved(signerToRemove); + vm.prank(owner); + zone.removeSigner(signerToRemove); + } + /* updateAPIEndpoint - N */ /* getSeaportMetadata - L */ From 24fa78d3ed01dddf2d8863df0809aaa9d3935635 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:42:26 +1100 Subject: [PATCH 17/79] TD-1326: chore: add tests for _validateSubstandard4 --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 18502a35..65cf29e8 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -190,6 +190,93 @@ contract ImmutableSignedZoneV2Test is Test { } /* _validateSubstandard4 - N */ + function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = new bytes(0x04); + uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); + assertEq(substandardLengthResult, 0); + } + + function test_validateSubstandard4_revertsIfContextLengthIsInvalid() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = abi.encodePacked(bytes1(0x04), bytes10(0)); + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, + "invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", + zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard4(context, zoneParameters); + } + + function test_validateSubstandard4_revertsIfDerivedOrderHashesIsNotEqualToHashesInContext() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x0)); + vm.expectRevert( + abi.encodeWithSelector( + SubstandardViolation.selector, + 4, + "invalid order hashes", + zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard4(context, zoneParameters); + } /* _validateSubstandard6 - N */ From 3f43ff5c8fdc9f315c11510afdc2ea5ea94ed9dc Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 16:03:10 +1100 Subject: [PATCH 18/79] newline --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 79324209..518f2336 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -322,6 +322,7 @@ contract ImmutableSignedZoneV2Test is Test { } /* _validateSubstandard4 - N */ + function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( "MyZoneName", From ce47a097795df4f9154978cd9f393052f22cacc6 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 16:19:46 +1100 Subject: [PATCH 19/79] use makeAddr --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 518f2336..c4e9aee0 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -23,7 +23,7 @@ contract ImmutableSignedZoneV2Test is Test { /* constructor */ function test_contructor_grantsAdminRoleToOwner() public { - address owner = address(0x2); + address owner = makeAddr("owner"); ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( "MyZoneName", "https://www.immutable.com", @@ -41,7 +41,7 @@ contract ImmutableSignedZoneV2Test is Test { "MyZoneName", "https://www.immutable.com", "https://www.immutable.com/docs", - address(0x2) + makeAddr("owner") ); } From e5390f949151552f7b086d201a3a47ff74aadbba Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:27:46 +1100 Subject: [PATCH 20/79] TD-1326: chore: fix _validateSubstandard4 logic and add tests --- .../seaport/zones/ImmutableSignedZoneV2.sol | 2 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index eabdd24d..005f8beb 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -418,7 +418,7 @@ contract ImmutableSignedZoneV2 is } uint256 expectedOrderHashesSize = uint256(bytes32(context[33:65])); - uint256 substandardIndexEnd = 65 + (expectedOrderHashesSize * 32); + uint256 substandardIndexEnd = 64 + (expectedOrderHashesSize * 32); bytes32[] memory expectedOrderHashes = abi.decode(context[1:substandardIndexEnd + 1], (bytes32[])); // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index c4e9aee0..ee642581 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -411,6 +411,34 @@ contract ImmutableSignedZoneV2Test is Test { zone.exposed_validateSubstandard4(context, zoneParameters); } + function test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess() public { + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + address(0x2) + ); + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9)); + uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); + // bytes1 + bytes32 + bytes32 + bytes32 = 97 + assertEq(substandardLengthResult, 97); + } + /* _validateSubstandard6 - N */ /* _deriveReceivedItemsHash - N */ From 1966bc2fde77c9ed3b3ce65ceccc7423a29f6208 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:33:07 +1100 Subject: [PATCH 21/79] TD-1326: style: forge fmt source contract --- .../seaport/zones/ImmutableSignedZoneV2.sol | 134 +++++++++++------- 1 file changed, 79 insertions(+), 55 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 005f8beb..4e947ec2 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -32,29 +32,17 @@ contract ImmutableSignedZoneV2 is { /// @dev The EIP-712 digest parameters. bytes32 internal immutable _VERSION_HASH = keccak256(bytes("2.0")); - bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = - keccak256( - abi.encodePacked( - "EIP712Domain(", - "string name,", - "string version,", - "uint256 chainId,", - "address verifyingContract", - ")" - ) - ); - - bytes32 internal immutable _SIGNED_ORDER_TYPEHASH = - keccak256( - abi.encodePacked( - "SignedOrder(", - "address fulfiller,", - "uint64 expiration,", - "bytes32 orderHash,", - "bytes context", - ")" - ) - ); + bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = keccak256( + abi.encodePacked( + "EIP712Domain(", "string name,", "string version,", "uint256 chainId,", "address verifyingContract", ")" + ) + ); + + bytes32 internal immutable _SIGNED_ORDER_TYPEHASH = keccak256( + abi.encodePacked( + "SignedOrder(", "address fulfiller,", "uint64 expiration,", "bytes32 orderHash,", "bytes context", ")" + ) + ); uint256 internal immutable _CHAIN_ID = block.chainid; bytes32 internal immutable _DOMAIN_SEPARATOR; @@ -188,7 +176,8 @@ contract ImmutableSignedZoneV2 is // supported SIP (7) schemas = new Schema[](1); schemas[0].id = 7; - schemas[0].metadata = abi.encode(_domainSeparator(), _sip7APIEndpoint, _getSupportedSubstandards(), _documentationURI); + schemas[0].metadata = + abi.encode(_domainSeparator(), _sip7APIEndpoint, _getSupportedSubstandards(), _documentationURI); } /** @@ -219,7 +208,12 @@ contract ImmutableSignedZoneV2 is * @notice ERC-165 interface support * @param interfaceId The interface ID to check for support. */ - function supportsInterface(bytes4 interfaceId) public view override(ERC165, ZoneInterface, AccessControlEnumerable) returns (bool) { + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC165, ZoneInterface, AccessControlEnumerable) + returns (bool) + { return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); } @@ -232,9 +226,12 @@ contract ImmutableSignedZoneV2 is * @return validOrderMagicValue A magic value indicating if the order is * currently valid. */ - function validateOrder( - ZoneParameters calldata zoneParameters - ) external view override returns (bytes4 validOrderMagicValue) { + function validateOrder(ZoneParameters calldata zoneParameters) + external + view + override + returns (bytes4 validOrderMagicValue) + { // Put the extraData and orderHash on the stack for cheaper access. bytes calldata extraData = zoneParameters.extraData; bytes32 orderHash = zoneParameters.orderHash; @@ -338,16 +335,14 @@ contract ImmutableSignedZoneV2 is * @return signedOrderHash The signedOrder hash. * */ - function _deriveSignedOrderHash( - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes calldata context - ) internal view returns (bytes32 signedOrderHash) { + function _deriveSignedOrderHash(address fulfiller, uint64 expiration, bytes32 orderHash, bytes calldata context) + internal + view + returns (bytes32 signedOrderHash) + { // Derive the signed order hash. - signedOrderHash = keccak256( - abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context)) - ); + signedOrderHash = + keccak256(abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context))); } /** @@ -356,19 +351,16 @@ contract ImmutableSignedZoneV2 is * @param context bytes payload of context * @param zoneParameters zone parameters */ - function _validateSubstandards( - bytes calldata context, - ZoneParameters calldata zoneParameters - ) internal pure { + function _validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure { uint256 startIndex = 0; - if (startIndex > context.length) { return; } + if (startIndex > context.length) return; startIndex = _validateSubstandard3(context[startIndex:], zoneParameters) + startIndex; - if (startIndex > context.length) { return; } + if (startIndex > context.length) return; startIndex = _validateSubstandard4(context[startIndex:], zoneParameters) + startIndex; - if (startIndex > context.length) { return; } + if (startIndex > context.length) return; startIndex = _validateSubstandard6(context[startIndex:], zoneParameters) + startIndex; if (startIndex != context.length) { @@ -383,14 +375,21 @@ contract ImmutableSignedZoneV2 is * @param zoneParameters zone parameters * @return Length of substandard segment */ - function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) + internal + pure + returns (uint256) + { if (uint8(context[0]) != 3) { return 0; } if (context.length < 33) { // TODO: Does size of error message have a gas impact? - revert InvalidExtraData("invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", zoneParameters.orderHash); + revert InvalidExtraData( + "invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", + zoneParameters.orderHash + ); } if (_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1) != bytes32(context[1:33])) { @@ -407,14 +406,21 @@ contract ImmutableSignedZoneV2 is * @param zoneParameters zone parameters * @return Length of substandard segment */ - function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) + internal + pure + returns (uint256) + { if (uint8(context[0]) != 4) { return 0; } // substandard ID + array offset + array length if (context.length < 65) { - revert InvalidExtraData("invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", zoneParameters.orderHash); + revert InvalidExtraData( + "invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", + zoneParameters.orderHash + ); } uint256 expectedOrderHashesSize = uint256(bytes32(context[33:65])); @@ -436,19 +442,29 @@ contract ImmutableSignedZoneV2 is * @param zoneParameters zone parameters * @return Length of substandard segment */ - function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure returns (uint256) { + function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) + internal + pure + returns (uint256) + { if (uint8(context[0]) != 6) { return 0; } if (context.length < 65) { - revert InvalidExtraData("invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", zoneParameters.orderHash); + revert InvalidExtraData( + "invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", zoneParameters.orderHash + ); } uint256 originalFirstOfferItemAmount = uint256(bytes32(context[1:33])); bytes32 expectedReceivedItemsHash = bytes32(context[33:65]); - if (_deriveReceivedItemsHash(zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount) != expectedReceivedItemsHash) { + if ( + _deriveReceivedItemsHash( + zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount + ) != expectedReceivedItemsHash + ) { revert SubstandardViolation(6, "invalid consideration hash", zoneParameters.orderHash); } @@ -462,7 +478,11 @@ contract ImmutableSignedZoneV2 is * @param scalingFactorNumerator scaling factor numerator * @param scalingFactorDenominator scaling factor denominator */ - function _deriveReceivedItemsHash(ReceivedItem[] calldata receivedItems, uint256 scalingFactorNumerator, uint256 scalingFactorDenominator) internal pure returns (bytes32) { + function _deriveReceivedItemsHash( + ReceivedItem[] calldata receivedItems, + uint256 scalingFactorNumerator, + uint256 scalingFactorDenominator + ) internal pure returns (bytes32) { uint256 numberOfItems = receivedItems.length; bytes memory receivedItemsHash; @@ -487,7 +507,11 @@ contract ImmutableSignedZoneV2 is * @param sourceArray source array * @param values values array */ - function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) internal pure returns (bool) { + function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) + internal + pure + returns (bool) + { // cache the length in memory for loop optimisation uint256 sourceArraySize = sourceArray.length; uint256 valuesSize = values.length; @@ -499,10 +523,10 @@ contract ImmutableSignedZoneV2 is } // Iterate through each element and compare them - for (uint256 i = 0; i < valuesSize; ) { + for (uint256 i = 0; i < valuesSize;) { bool found = false; bytes32 item = values[i]; - for (uint256 j = 0; j < sourceArraySize; ) { + for (uint256 j = 0; j < sourceArraySize;) { if (item == sourceArray[j]) { // if item from values is in sourceArray, break found = true; From 87f27fd0668cd1c10971dde96a96ae8b5094166f Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:34:05 +1100 Subject: [PATCH 22/79] TD-1326: style: forge fmt test contract --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index ee642581..9ca2d6bc 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -401,12 +401,7 @@ contract ImmutableSignedZoneV2Test is Test { bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x0)); vm.expectRevert( - abi.encodeWithSelector( - SubstandardViolation.selector, - 4, - "invalid order hashes", - zoneParameters.orderHash - ) + abi.encodeWithSelector(SubstandardViolation.selector, 4, "invalid order hashes", zoneParameters.orderHash) ); zone.exposed_validateSubstandard4(context, zoneParameters); } @@ -433,7 +428,12 @@ contract ImmutableSignedZoneV2Test is Test { zoneHash: bytes32(0) }); - bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9)); + bytes memory context = abi.encodePacked( + bytes1(0x04), + bytes32(uint256(32)), + bytes32(uint256(1)), + bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) + ); uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); // bytes1 + bytes32 + bytes32 + bytes32 = 97 assertEq(substandardLengthResult, 97); From edc8222f95510fb719ac6cd4dc0d9252ac90f632 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 17:17:21 +1100 Subject: [PATCH 23/79] Add zone helper functions --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 71 +++++++++++++------ 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 9ca2d6bc..423f2381 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -20,6 +20,8 @@ contract ImmutableSignedZoneV2Test is Test { error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) + address internal OWNER = makeAddr("owner"); + /* constructor */ function test_contructor_grantsAdminRoleToOwner() public { @@ -48,34 +50,19 @@ contract ImmutableSignedZoneV2Test is Test { /* addSigner - L */ function test_addSigner_revertsIfCalledByNonAdminRole() public { - address owner = makeAddr("owner"); - address randomAddress = makeAddr("random"); - address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); + ImmutableSignedZoneV2 zone = _newZone(); vm.expectRevert( "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" ); - vm.prank(randomAddress); - zone.addSigner(signerToAdd); + vm.prank(makeAddr("random")); + zone.addSigner(makeAddr("signerToAdd")); } function test_addSigner_revertsIfSignerIsTheZeroAddress() public { - address owner = makeAddr("owner"); - address signerToAdd = address(0); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); + ImmutableSignedZoneV2 zone = _newZone(); vm.expectRevert(abi.encodeWithSelector(SignerCannotBeZeroAddress.selector)); - vm.prank(owner); - zone.addSigner(signerToAdd); + vm.prank(OWNER); + zone.addSigner(address(0)); } function test_addSigner_emitsSignerAddedEvent() public { @@ -448,8 +435,50 @@ contract ImmutableSignedZoneV2Test is Test { /* _domainSeparator - N */ /* _deriveDomainSeparator - N */ + + /* Helper functions */ + + function _newZone() internal returns (ImmutableSignedZoneV2) { + return new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + OWNER + ); + } + + function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { + return new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + OWNER + ); + } } +// abstract contract ImmutableSignedZoneV2TestHelper { +// address internal OWNER = makeAddr("owner"); + +// function _newZone() internal returns (ImmutableSignedZoneV2) { +// return new ImmutableSignedZoneV2( +// "MyZoneName", +// "https://www.immutable.com", +// "https://www.immutable.com/docs", +// OWNER +// ); +// } + +// function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { +// return new ImmutableSignedZoneV2Harness( +// "MyZoneName", +// "https://www.immutable.com", +// "https://www.immutable.com/docs", +// OWNER +// ); +// } +// } + contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) From d72fd3d640e384002abcc2d38782304a346bc300 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 17:30:18 +1100 Subject: [PATCH 24/79] solhints --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 423f2381..9e4d163d 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -1,6 +1,6 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 - +// solhint-disable-next-line one-contract-per-file pragma solidity ^0.8.17; import "forge-std/Test.sol"; @@ -8,6 +8,8 @@ import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/Con import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +/* solhint-disable func-name-mixedcase */ + contract ImmutableSignedZoneV2Test is Test { event SeaportCompatibleContractDeployed(); // SIP-5 event SignerAdded(address signer); // SIP-7 @@ -552,3 +554,5 @@ contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { return _deriveDomainSeparator(); } } + +/* solhint-enable func-name-mixedcase */ From 67005ac07c7e0d8e1811414ec5eeb189fb62540c Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 17:46:08 +1100 Subject: [PATCH 25/79] Use TestHelper --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 129 +----------------- .../zones/ImmutableSignedZoneV2Harness.t.sol | 81 +++++++++++ .../ImmutableSignedZoneV2TestHelper.t.sol | 30 ++++ 3 files changed, 116 insertions(+), 124 deletions(-) create mode 100644 test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol create mode 100644 test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 9e4d163d..05b554a9 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -1,16 +1,17 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -// solhint-disable-next-line one-contract-per-file + pragma solidity ^0.8.17; -import "forge-std/Test.sol"; import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; -import "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; +import {ImmutableSignedZoneV2TestHelper} from "./ImmutableSignedZoneV2TestHelper.t.sol"; /* solhint-disable func-name-mixedcase */ -contract ImmutableSignedZoneV2Test is Test { +contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { event SeaportCompatibleContractDeployed(); // SIP-5 event SignerAdded(address signer); // SIP-7 event SignerRemoved(address signer); // SIP-7 @@ -22,8 +23,6 @@ contract ImmutableSignedZoneV2Test is Test { error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) - address internal OWNER = makeAddr("owner"); - /* constructor */ function test_contructor_grantsAdminRoleToOwner() public { @@ -437,122 +436,4 @@ contract ImmutableSignedZoneV2Test is Test { /* _domainSeparator - N */ /* _deriveDomainSeparator - N */ - - /* Helper functions */ - - function _newZone() internal returns (ImmutableSignedZoneV2) { - return new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - OWNER - ); - } - - function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { - return new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - OWNER - ); - } } - -// abstract contract ImmutableSignedZoneV2TestHelper { -// address internal OWNER = makeAddr("owner"); - -// function _newZone() internal returns (ImmutableSignedZoneV2) { -// return new ImmutableSignedZoneV2( -// "MyZoneName", -// "https://www.immutable.com", -// "https://www.immutable.com/docs", -// OWNER -// ); -// } - -// function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { -// return new ImmutableSignedZoneV2Harness( -// "MyZoneName", -// "https://www.immutable.com", -// "https://www.immutable.com/docs", -// OWNER -// ); -// } -// } - -contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { - constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) - ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) - {} - - function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards) { - return _getSupportedSubstandards(); - } - - function exposed_deriveSignedOrderHash( - address fulfiller, - uint64 expiration, - bytes32 orderHash, - bytes calldata context - ) external view returns (bytes32 signedOrderHash) { - return _deriveSignedOrderHash(fulfiller, expiration, orderHash, context); - } - - function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) - external - pure - { - return _validateSubstandards(context, zoneParameters); - } - - function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) - external - pure - returns (uint256) - { - return _validateSubstandard3(context, zoneParameters); - } - - function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) - external - pure - returns (uint256) - { - return _validateSubstandard4(context, zoneParameters); - } - - function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) - external - pure - returns (uint256) - { - return _validateSubstandard6(context, zoneParameters); - } - - function exposed_deriveReceivedItemsHash( - ReceivedItem[] calldata receivedItems, - uint256 scalingFactorNumerator, - uint256 scalingFactorDenominator - ) external pure returns (bytes32) { - return _deriveReceivedItemsHash(receivedItems, scalingFactorNumerator, scalingFactorDenominator); - } - - function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) - external - pure - returns (bool) - { - return _bytes32ArrayIncludes(sourceArray, values); - } - - function exposed_domainSeparator() external view returns (bytes32) { - return _domainSeparator(); - } - - function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator) { - return _deriveDomainSeparator(); - } -} - -/* solhint-enable func-name-mixedcase */ diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol new file mode 100644 index 00000000..c0ca763d --- /dev/null +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol @@ -0,0 +1,81 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +pragma solidity ^0.8.17; + +import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; + +contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { + constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) + ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) + {} + + function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards) { + return _getSupportedSubstandards(); + } + + function exposed_deriveSignedOrderHash( + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes calldata context + ) external view returns (bytes32 signedOrderHash) { + return _deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + } + + function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + { + return _validateSubstandards(context, zoneParameters); + } + + function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { + return _validateSubstandard3(context, zoneParameters); + } + + function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { + return _validateSubstandard4(context, zoneParameters); + } + + function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256) + { + return _validateSubstandard6(context, zoneParameters); + } + + function exposed_deriveReceivedItemsHash( + ReceivedItem[] calldata receivedItems, + uint256 scalingFactorNumerator, + uint256 scalingFactorDenominator + ) external pure returns (bytes32) { + return _deriveReceivedItemsHash(receivedItems, scalingFactorNumerator, scalingFactorDenominator); + } + + function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) + external + pure + returns (bool) + { + return _bytes32ArrayIncludes(sourceArray, values); + } + + function exposed_domainSeparator() external view returns (bytes32) { + return _domainSeparator(); + } + + function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator) { + return _deriveDomainSeparator(); + } +} diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol new file mode 100644 index 00000000..7673fe9f --- /dev/null +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -0,0 +1,30 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; + +abstract contract ImmutableSignedZoneV2TestHelper is Test { + address internal OWNER = makeAddr("owner"); + + function _newZone() internal returns (ImmutableSignedZoneV2) { + return new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + OWNER + ); + } + + function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { + return new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + OWNER + ); + } +} From b4ef4bb2069d289115991e5c033a5ba0526b8ab4 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 17:51:25 +1100 Subject: [PATCH 26/79] Use zone helpers --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 128 ++++-------------- 1 file changed, 25 insertions(+), 103 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 05b554a9..a936cc95 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -67,101 +67,63 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_addSigner_emitsSignerAddedEvent() public { - address owner = makeAddr("owner"); address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); + ImmutableSignedZoneV2 zone = _newZone(); vm.expectEmit(address(zone)); emit SignerAdded(signerToAdd); - vm.prank(owner); + vm.prank(OWNER); zone.addSigner(signerToAdd); } function test_addSigner_revertsIfSignerAlreadyActive() public { - address owner = makeAddr("owner"); address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); - vm.prank(owner); + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); zone.addSigner(signerToAdd); vm.expectRevert(abi.encodeWithSelector(SignerAlreadyActive.selector, signerToAdd)); - vm.prank(owner); + vm.prank(OWNER); zone.addSigner(signerToAdd); } function test_addSigner_revertsIfSignerWasPreviouslyActive() public { - address owner = makeAddr("owner"); address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); - vm.prank(owner); + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); zone.addSigner(signerToAdd); - vm.prank(owner); + vm.prank(OWNER); zone.removeSigner(signerToAdd); vm.expectRevert(abi.encodeWithSelector(SignerCannotBeReauthorized.selector, signerToAdd)); - vm.prank(owner); + vm.prank(OWNER); zone.addSigner(signerToAdd); } /* removeSigner - L */ function test_removeSigner_revertsIfCalledByNonAdminRole() public { - address owner = makeAddr("owner"); - address randomAddress = makeAddr("random"); - address signerToRemove = makeAddr("signerToRemove"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); + ImmutableSignedZoneV2 zone = _newZone(); vm.expectRevert( "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" ); - vm.prank(randomAddress); - zone.removeSigner(signerToRemove); + vm.prank(makeAddr("random")); + zone.removeSigner(makeAddr("signerToRemove")); } function test_removeSigner_revertsIfSignerNotActive() public { - address owner = makeAddr("owner"); address signerToRemove = makeAddr("signerToRemove"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); + ImmutableSignedZoneV2 zone = _newZone(); vm.expectRevert(abi.encodeWithSelector(SignerNotActive.selector, signerToRemove)); - vm.prank(owner); + vm.prank(OWNER); zone.removeSigner(signerToRemove); } function test_removeSigner_emitsSignerRemovedEvent() public { - address owner = makeAddr("owner"); address signerToRemove = makeAddr("signerToRemove"); - ImmutableSignedZoneV2 zone = new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - owner - ); - vm.prank(owner); + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); zone.addSigner(signerToRemove); vm.expectEmit(address(zone)); emit SignerRemoved(signerToRemove); - vm.prank(owner); + vm.prank(OWNER); zone.removeSigner(signerToRemove); } @@ -184,12 +146,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard3 */ function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -208,12 +165,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -238,12 +190,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ReceivedItem[] memory consideration = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, @@ -275,12 +222,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_returns33OnSuccess() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ReceivedItem[] memory consideration = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, @@ -312,12 +254,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard4 - N */ function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -336,12 +273,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -366,12 +298,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_revertsIfDerivedOrderHashesIsNotEqualToHashesInContext() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); ZoneParameters memory zoneParameters = ZoneParameters({ @@ -395,12 +322,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess() public { - ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - address(0x2) - ); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); ZoneParameters memory zoneParameters = ZoneParameters({ From 7b025a8a35c595ab6211751c388d7e6edea9aa71 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 3 Apr 2024 17:53:16 +1100 Subject: [PATCH 27/79] Removed unused import --- test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol index c0ca763d..65cffef1 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.17; -import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { From 5372044299aa2e4fe45ff4d2c90f70d6023e8794 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 06:41:21 +1100 Subject: [PATCH 28/79] TD-1326: chore: add tests for _validateSubstandard6 --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index a936cc95..3609b76f 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -351,6 +351,119 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard6 - N */ + function test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = new bytes(0x04); + uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); + assertEq(substandardLengthResult, 0); + } + + function test_validateSubstandard6_revertsIfContextLengthIsInvalid() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = abi.encodePacked(bytes1(0x06), bytes10(0)); + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, + "invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", + zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard6(context, zoneParameters); + } + + function test_validateSubstandard6_revertsIfDerivedReceivedItemsHashesIsNotEqualToHashesInContext() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); + receivedItems[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: spentItems, + consideration: receivedItems, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), bytes32(uint256(0x123456))); + vm.expectRevert( + abi.encodeWithSelector( + SubstandardViolation.selector, 6, "invalid consideration hash", zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandard6(context, zoneParameters); + } + + function test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); + receivedItems[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: spentItems, + consideration: receivedItems, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); + + bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; + bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); + + uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); + // bytes1 + uint256 + bytes32 = 65 + assertEq(substandardLengthResult, 65); + } + /* _deriveReceivedItemsHash - N */ /* _bytes32ArrayIncludes - N */ From e84f19b8c31732c22da7e6aa9fb527f7bc940b7c Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 06:55:17 +1100 Subject: [PATCH 29/79] TD-1326: chore: add tests for _deriveReceivedItemsHash --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 3609b76f..1db900a2 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -455,10 +455,8 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); - bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); - uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); // bytes1 + uint256 + bytes32 = 65 assertEq(substandardLengthResult, 65); @@ -466,6 +464,34 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveReceivedItemsHash - N */ + function test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](0); + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 0, 0); + assertEq(receivedItemsHash, bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)); + } + + function test_deriveReceivedItemsHash_returnsHashForValidReceivedItems() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](2); + receivedItems[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + receivedItems[1] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 199, + amount: 1, + recipient: payable(address(0x3)) + }); + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10); + assertEq(receivedItemsHash, bytes32(0xf01bacf40a3dd95740faaaad186bf1c000a9edc06008ea07c789ea761d7f3ffb)); + } + /* _bytes32ArrayIncludes - N */ /* _domainSeparator - N */ From a4ada413a61531e65f3fd983aa9d62e8ac3f6d73 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 08:27:03 +1100 Subject: [PATCH 30/79] solhints --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 4 +++- test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 1db900a2..63b9630d 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -9,7 +9,7 @@ import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {ImmutableSignedZoneV2TestHelper} from "./ImmutableSignedZoneV2TestHelper.t.sol"; -/* solhint-disable func-name-mixedcase */ +// solhint-disable func-name-mixedcase contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { event SeaportCompatibleContractDeployed(); // SIP-5 @@ -498,3 +498,5 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveDomainSeparator - N */ } + +// solhint-enable func-name-mixedcase diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol index 65cffef1..829fece5 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol @@ -6,6 +6,8 @@ pragma solidity ^0.8.17; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +// solhint-disable func-name-mixedcase + contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) ImmutableSignedZoneV2(zoneName, apiEndpoint, documentationURI, owner) @@ -79,3 +81,5 @@ contract ImmutableSignedZoneV2Harness is ImmutableSignedZoneV2 { return _deriveDomainSeparator(); } } + +// solhint-enable func-name-mixedcase From 9d2590a3f8d5c53e35318bc7481d80a17a87d0b4 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 08:30:02 +1100 Subject: [PATCH 31/79] Add test for _getSupportedSubstandards --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 63b9630d..da91b47f 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -139,6 +139,15 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _getSupportedSubstandards - L */ + function test_getSupportedSubstandards() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + uint256[] memory supportedSubstandards = zone.exposed_getSupportedSubstandards(); + assertEq(supportedSubstandards.length, 3); + assertEq(supportedSubstandards[0], 3); + assertEq(supportedSubstandards[1], 4); + assertEq(supportedSubstandards[2], 6); + } + /* _deriveSignedOrderHash - N */ /* _validateSubstandards */ From 345f59685418b65f7496fee47e24a09150624510 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 08:48:26 +1100 Subject: [PATCH 32/79] Add test for sip7Information --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index da91b47f..e15a710b 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -1,6 +1,7 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; @@ -133,6 +134,27 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* sip7Information - L */ + function test_sip7Information() public { + string memory expectedApiEndpoint = "https://www.immutable.com"; + string memory expectedDocumentationURI = "https://www.immutable.com/docs"; + + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + "MyZoneName", + expectedApiEndpoint, + expectedDocumentationURI, + OWNER + ); + + bytes32 expectedDomainSeparator = zone.exposed_deriveDomainSeparator(); + uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); + + (bytes32 domainSeparator, string memory apiEndpoint, uint256[] memory substandards, string memory documentationURI) = zone.sip7Information(); + assertEq(domainSeparator, expectedDomainSeparator); + assertEq(apiEndpoint, expectedApiEndpoint); + assertEq(substandards, expectedSubstandards); + assertEq(documentationURI, expectedDocumentationURI); + } + /* supportsInterface - L */ /* validateOrder */ From f3625a0fdd27c64d0e9e13002cd79eba3c6701e8 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 09:08:22 +1100 Subject: [PATCH 33/79] Add test for getSeaportMetadata --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index e15a710b..284bf2aa 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -4,7 +4,7 @@ // solhint-disable-next-line compiler-version pragma solidity ^0.8.17; -import {ReceivedItem, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ReceivedItem, Schema, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; @@ -132,6 +132,35 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* getSeaportMetadata - L */ + function test_getSeaportMetadata() public { + string memory expectedZoneName = "MyZoneName"; + string memory expectedApiEndpoint = "https://www.immutable.com"; + string memory expectedDocumentationURI = "https://www.immutable.com/docs"; + + ImmutableSignedZoneV2Harness zone = new ImmutableSignedZoneV2Harness( + expectedZoneName, + expectedApiEndpoint, + expectedDocumentationURI, + OWNER + ); + + bytes32 expectedDomainSeparator = zone.exposed_deriveDomainSeparator(); + uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); + + (string memory name, Schema[] memory schemas) = zone.getSeaportMetadata(); + (bytes32 domainSeparator, string memory apiEndpoint, uint256[] memory substandards, string memory documentationURI) = abi.decode( + schemas[0].metadata, + (bytes32, string, uint256[], string) + ); + assertEq(name, expectedZoneName); + assertEq(schemas.length, 1); + assertEq(schemas[0].id, 7); + assertEq(domainSeparator, expectedDomainSeparator); + assertEq(apiEndpoint, expectedApiEndpoint); + assertEq(substandards, expectedSubstandards); + assertEq(documentationURI, expectedDocumentationURI); + } + /* sip7Information - L */ function test_sip7Information() public { From 16d3100c5312728dfca718c41a2c399c56486111 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 09:08:48 +1100 Subject: [PATCH 34/79] Forge format --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 284bf2aa..da308970 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -148,10 +148,12 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); (string memory name, Schema[] memory schemas) = zone.getSeaportMetadata(); - (bytes32 domainSeparator, string memory apiEndpoint, uint256[] memory substandards, string memory documentationURI) = abi.decode( - schemas[0].metadata, - (bytes32, string, uint256[], string) - ); + ( + bytes32 domainSeparator, + string memory apiEndpoint, + uint256[] memory substandards, + string memory documentationURI + ) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); assertEq(name, expectedZoneName); assertEq(schemas.length, 1); assertEq(schemas[0].id, 7); @@ -177,7 +179,12 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32 expectedDomainSeparator = zone.exposed_deriveDomainSeparator(); uint256[] memory expectedSubstandards = zone.exposed_getSupportedSubstandards(); - (bytes32 domainSeparator, string memory apiEndpoint, uint256[] memory substandards, string memory documentationURI) = zone.sip7Information(); + ( + bytes32 domainSeparator, + string memory apiEndpoint, + uint256[] memory substandards, + string memory documentationURI + ) = zone.sip7Information(); assertEq(domainSeparator, expectedDomainSeparator); assertEq(apiEndpoint, expectedApiEndpoint); assertEq(substandards, expectedSubstandards); From 143b64550730cd3b81a27b2091319d0cd3a6441b Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:31:35 +1100 Subject: [PATCH 35/79] TD-1326: chore: add tests for _bytes32ArrayIncludes --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index da308970..e8486e1e 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -561,6 +561,60 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _bytes32ArrayIncludes - N */ + function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsEmpty() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory emptySourceArray = new bytes32[](0); + bytes32[] memory valuesArray = new bytes32[](2); + bool includes = zone.exposed_bytes32ArrayIncludes(emptySourceArray, valuesArray); + assertFalse(includes); + } + + function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsSmallerThanValuesArray() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](1); + bytes32[] memory valuesArray = new bytes32[](2); + bool includesEmptySource = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); + assertFalse(includesEmptySource); + } + + function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayDoesNotIncludeValuesArray() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](2); + sourceArray[0] = bytes32(uint256(1)); + sourceArray[1] = bytes32(uint256(2)); + bytes32[] memory valuesArray = new bytes32[](2); + valuesArray[0] = bytes32(uint256(3)); + valuesArray[1] = bytes32(uint256(4)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); + assertFalse(includes); + } + + function test_bytes32ArrayIncludes_returnsTrueIfSourceArrayIncludesValuesArray() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](2); + sourceArray[0] = bytes32(uint256(1)); + sourceArray[1] = bytes32(uint256(2)); + bytes32[] memory valuesArray = new bytes32[](2); + valuesArray[0] = bytes32(uint256(1)); + valuesArray[1] = bytes32(uint256(2)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); + assertTrue(includes); + } + + function test_bytes32ArrayIncludes_returnsTrueIfValuesArrayIsASubsetOfSourceArray() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](4); + sourceArray[0] = bytes32(uint256(1)); + sourceArray[1] = bytes32(uint256(2)); + sourceArray[2] = bytes32(uint256(3)); + sourceArray[3] = bytes32(uint256(4)); + bytes32[] memory valuesArray = new bytes32[](2); + valuesArray[0] = bytes32(uint256(1)); + valuesArray[1] = bytes32(uint256(2)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); + assertTrue(includes); + } + /* _domainSeparator - N */ /* _deriveDomainSeparator - N */ From 7ceb9f6f5407847abfe9338c2c57c1604d605a92 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 09:46:32 +1100 Subject: [PATCH 36/79] Add test for supportsInterface --- .../trading/seaport/zones/ImmutableSignedZoneV2.sol | 3 ++- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 4e947ec2..6f97cfd0 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -214,7 +214,8 @@ contract ImmutableSignedZoneV2 is override(ERC165, ZoneInterface, AccessControlEnumerable) returns (bool) { - return interfaceId == type(ZoneInterface).interfaceId || super.supportsInterface(interfaceId); + return interfaceId == type(ZoneInterface).interfaceId || interfaceId == type(SIP5Interface).interfaceId + || interfaceId == type(SIP7Interface).interfaceId || super.supportsInterface(interfaceId); } /** diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index e8486e1e..7a092484 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -193,6 +193,15 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* supportsInterface - L */ + function test_supportsInterface() public { + ImmutableSignedZoneV2 zone = _newZone(); + assertTrue(zone.supportsInterface(0x01ffc9a7)); // ERC165 interface + assertFalse(zone.supportsInterface(0xffffffff)); // ERC165 compliance + assertTrue(zone.supportsInterface(0x3839be19)); // ZoneInterface + assertTrue(zone.supportsInterface(0x2e778efc)); // SIP-5 interface + assertTrue(zone.supportsInterface(0x1a511c70)); // SIP-7 interface + } + /* validateOrder */ /* _getSupportedSubstandards - L */ From a49aa296290f792603782afd0255861c27a0dfd9 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:58:11 +1100 Subject: [PATCH 37/79] TD-1326: chore: add tests for _domainSeparator and _deriveDomainSeparator --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 7a092484..c6f7237c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -626,7 +626,28 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _domainSeparator - N */ + function test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparator = zone.exposed_domainSeparator(); + assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); + } + + function test_domainSeparator_returnsUpdatedDomainSeparatorIfChainIDIsDifferentFromValueSetOnDeployment() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparatorCached = zone.exposed_domainSeparator(); + vm.chainId(31338); + bytes32 domainSeparatorDerived = zone.exposed_domainSeparator(); + assertFalse(domainSeparatorCached == domainSeparatorDerived); + assertEq(domainSeparatorDerived, bytes32(0x835aabb0d2af048df195a75a990b42533471d4a4e82842cd54a892eaac463d74)); + } + /* _deriveDomainSeparator - N */ + + function test_deriveDomainSeparator_returnsDomainSeparatorForChainID() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparator = zone.exposed_deriveDomainSeparator(); + assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); + } } // solhint-enable func-name-mixedcase From 70f72b33d2c432d4e6783439dabdd06d1092276f Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:04:59 +1100 Subject: [PATCH 38/79] TD-1326: chore: add tests for _updateAPIEndpoint and _deriveSignedOrderHash --- foundry.toml | 3 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 9c682eb7..1d9a4670 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,6 +3,7 @@ src = 'contracts' out = 'foundry-out' # libs = ["lib", "node_modules"] libs = ["lib"] - +# memory_limit = 1073741824 +# gas_limit = "18446744073709551615" # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index c6f7237c..a0c1d76c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -130,6 +130,25 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* updateAPIEndpoint - N */ + function test_updateAPIEndpoint_revertsIfCalledByNonAdminRole() public { + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(makeAddr("random")); + vm.expectRevert( + "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" + ); + zone.updateAPIEndpoint("https://www.new-immutable.com"); + } + + function test_updateAPIEndpoint_updatesAPIEndpointIfCalledByAdminRole() public { + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); + string memory expectedApiEndpoint = "https://www.new-immutable.com"; + zone.updateAPIEndpoint(expectedApiEndpoint); + (, Schema[] memory schemas) = zone.getSeaportMetadata(); + (, string memory apiEndpoint,,) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); + assertEq(apiEndpoint, expectedApiEndpoint); + } + /* getSeaportMetadata - L */ function test_getSeaportMetadata() public { @@ -217,6 +236,15 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveSignedOrderHash - N */ + function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; + uint64 expiration = 1234995; + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + bytes memory context = new bytes(0x0); + zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + } + /* _validateSubstandards */ /* _validateSubstandard3 */ From 7e09385679619efd4d44e9a2a97cca585d947b57 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:05:33 +1100 Subject: [PATCH 39/79] Revert "TD-1326: chore: add tests for _updateAPIEndpoint and _deriveSignedOrderHash" This reverts commit 70f72b33d2c432d4e6783439dabdd06d1092276f. --- foundry.toml | 3 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 28 ------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/foundry.toml b/foundry.toml index 1d9a4670..9c682eb7 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,6 @@ src = 'contracts' out = 'foundry-out' # libs = ["lib", "node_modules"] libs = ["lib"] -# memory_limit = 1073741824 -# gas_limit = "18446744073709551615" + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index a0c1d76c..c6f7237c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -130,25 +130,6 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* updateAPIEndpoint - N */ - function test_updateAPIEndpoint_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); - vm.prank(makeAddr("random")); - vm.expectRevert( - "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" - ); - zone.updateAPIEndpoint("https://www.new-immutable.com"); - } - - function test_updateAPIEndpoint_updatesAPIEndpointIfCalledByAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); - vm.prank(OWNER); - string memory expectedApiEndpoint = "https://www.new-immutable.com"; - zone.updateAPIEndpoint(expectedApiEndpoint); - (, Schema[] memory schemas) = zone.getSeaportMetadata(); - (, string memory apiEndpoint,,) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); - assertEq(apiEndpoint, expectedApiEndpoint); - } - /* getSeaportMetadata - L */ function test_getSeaportMetadata() public { @@ -236,15 +217,6 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveSignedOrderHash - N */ - function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; - uint64 expiration = 1234995; - bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); - bytes memory context = new bytes(0x0); - zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); - } - /* _validateSubstandards */ /* _validateSubstandard3 */ From 418768b82176b7f1dc630279a6db3af89efbf5b3 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:07:14 +1100 Subject: [PATCH 40/79] TD-1326: chore: add tests for _updateAPIEndpoint and _deriveSignedOrderHash --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index c6f7237c..a0c1d76c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -130,6 +130,25 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* updateAPIEndpoint - N */ + function test_updateAPIEndpoint_revertsIfCalledByNonAdminRole() public { + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(makeAddr("random")); + vm.expectRevert( + "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" + ); + zone.updateAPIEndpoint("https://www.new-immutable.com"); + } + + function test_updateAPIEndpoint_updatesAPIEndpointIfCalledByAdminRole() public { + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); + string memory expectedApiEndpoint = "https://www.new-immutable.com"; + zone.updateAPIEndpoint(expectedApiEndpoint); + (, Schema[] memory schemas) = zone.getSeaportMetadata(); + (, string memory apiEndpoint,,) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); + assertEq(apiEndpoint, expectedApiEndpoint); + } + /* getSeaportMetadata - L */ function test_getSeaportMetadata() public { @@ -217,6 +236,15 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveSignedOrderHash - N */ + function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; + uint64 expiration = 1234995; + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + bytes memory context = new bytes(0x0); + zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + } + /* _validateSubstandards */ /* _validateSubstandard3 */ From 632fd6833b7f106292f3b519b9fb5b81dee869e0 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 15:34:43 +1100 Subject: [PATCH 41/79] Fix bytes context initialisation --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index a0c1d76c..3b63f868 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -241,8 +241,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; uint64 expiration = 1234995; bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); - bytes memory context = new bytes(0x0); - zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + bytes memory context = hex"9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91"; + bytes32 derivedSignedOrderHash = zone.exposed_deriveSignedOrderHash(fulfiller, expiration, orderHash, context); + assertEq(derivedSignedOrderHash, 0x40c87207c5a0c362da24cb974859c70655de00fee9400f3a805ac360b90bd8c5); } /* _validateSubstandards */ From 4aa943fc1884da9b4498bbe5c515da526d571dff Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 15:46:05 +1100 Subject: [PATCH 42/79] Fix all usages of bytes instantiation --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 3b63f868..e14bb432 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -264,8 +264,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); - bytes memory context = new bytes(0x04); - uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); + uint256 substandardLengthResult = zone.exposed_validateSubstandard3(hex"04", zoneParameters); assertEq(substandardLengthResult, 0); } @@ -372,8 +371,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); - bytes memory context = new bytes(0x04); - uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); + uint256 substandardLengthResult = zone.exposed_validateSubstandard4(hex"02", zoneParameters); assertEq(substandardLengthResult, 0); } @@ -470,8 +468,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); - bytes memory context = new bytes(0x04); - uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); + uint256 substandardLengthResult = zone.exposed_validateSubstandard6(hex"04", zoneParameters); assertEq(substandardLengthResult, 0); } From cf6d06099148df80e502602be300b3107e28e719 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 4 Apr 2024 17:27:48 +1100 Subject: [PATCH 43/79] Delegate tasks --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index e14bb432..8dfe2b34 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -221,7 +221,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { assertTrue(zone.supportsInterface(0x1a511c70)); // SIP-7 interface } - /* validateOrder */ + /* validateOrder - N */ /* _getSupportedSubstandards - L */ @@ -246,7 +246,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { assertEq(derivedSignedOrderHash, 0x40c87207c5a0c362da24cb974859c70655de00fee9400f3a805ac360b90bd8c5); } - /* _validateSubstandards */ + /* _validateSubstandards - L */ /* _validateSubstandard3 */ From 37c360afa3714fc71c0769a4987c2a05386157a8 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Fri, 5 Apr 2024 10:33:30 +1100 Subject: [PATCH 44/79] Add tests for _validateSubstandards --- .../seaport/zones/ImmutableSignedZoneV2.sol | 9 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 257 ++++++++++++++++++ 2 files changed, 262 insertions(+), 4 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 6f97cfd0..3532c39e 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -354,17 +354,18 @@ contract ImmutableSignedZoneV2 is */ function _validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure { uint256 startIndex = 0; + uint256 contextLength = context.length; - if (startIndex > context.length) return; + if (startIndex == contextLength) return; startIndex = _validateSubstandard3(context[startIndex:], zoneParameters) + startIndex; - if (startIndex > context.length) return; + if (startIndex == contextLength) return; startIndex = _validateSubstandard4(context[startIndex:], zoneParameters) + startIndex; - if (startIndex > context.length) return; + if (startIndex == contextLength) return; startIndex = _validateSubstandard6(context[startIndex:], zoneParameters) + startIndex; - if (startIndex != context.length) { + if (startIndex != contextLength) { revert InvalidExtraData("invalid context, unexpected context length", zoneParameters.orderHash); } } diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 8dfe2b34..2962ff25 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -248,6 +248,263 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandards - L */ + function test_validateSubstandards_emptyContext() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + bytes memory context = new bytes(0); + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_substandard3() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory consideration = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + consideration[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: consideration, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_substandard4() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + bytes memory context = abi.encodePacked( + bytes1(0x04), + bytes32(uint256(32)), + bytes32(uint256(1)), + bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) + ); + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_substandard6() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); + receivedItems[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: spentItems, + consideration: receivedItems, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); + bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; + bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_multipleSubstandardsInCorrectOrder() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory consideration = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + consideration[0] = receivedItem; + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: consideration, + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes memory substandard4Data = abi.encode(orderHashes); + + bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data); + + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_substandards3Then6() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + + 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; + + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: spentItems, + consideration: receivedItems, + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); + bytes32 substandard3Data = bytes32(0xec07a42041c18889c5c5dcd348923ea9f3d0979735bd8b3b687ebda38d9b6a31); + bytes memory substandard6Data = abi.encodePacked(uint256(10), substandard3Data); + bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x06), substandard6Data); + + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_allSubstandards() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + + 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); + + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: spentItems, + consideration: receivedItems, + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); + bytes32 substandard3Data = bytes32(0xec07a42041c18889c5c5dcd348923ea9f3d0979735bd8b3b687ebda38d9b6a31); + bytes memory substandard4Data = abi.encode(orderHashes); + bytes memory substandard6Data = abi.encodePacked(uint256(10), substandard3Data); + bytes memory context = abi.encodePacked( + bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data, bytes1(0x06), substandard6Data + ); + + zone.exposed_validateSubstandards(context, zoneParameters); + } + + function test_validateSubstandards_revertsOnMultipleSubstandardsInIncorrectOrder() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory consideration = new ReceivedItem[](1); + ReceivedItem memory receivedItem = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + consideration[0] = receivedItem; + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0), + fulfiller: address(0x2), + offerer: address(0x3), + offer: new SpentItem[](0), + consideration: consideration, + extraData: new bytes(0), + orderHashes: orderHashes, + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes memory substandard4Data = abi.encode(orderHashes); + + bytes memory context = abi.encodePacked(bytes1(0x04), substandard4Data, bytes1(0x03), substandard3Data); + + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, "invalid context, unexpected context length", zoneParameters.orderHash + ) + ); + zone.exposed_validateSubstandards(context, zoneParameters); + } + /* _validateSubstandard3 */ function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { From fc52c0b2daf4a2fe19b310b36f96b1c4ea73e4a8 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:14:47 +1100 Subject: [PATCH 45/79] TD-1326: chore: add tests for _validateOrder --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 2962ff25..ca9da8ec 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -23,6 +23,12 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { error SignerNotActive(address signer); // SIP-7 error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) + error UnsupportedExtraDataVersion(uint8 version); // SIP-6 + error SignatureExpired(uint256 currentTimestamp, uint256 expiration, bytes32 orderHash); // SIP-7 + error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); // SIP-7 + + address internal immutable FULFILLER = makeAddr("fulfiller"); + address internal immutable OFFERER = makeAddr("offerer"); /* constructor */ @@ -223,6 +229,171 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* validateOrder - N */ + function test_validateOrder_revertsIfEmptyExtraData() public { + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: new bytes(0), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, + "extraData is empty", + zoneParameters.orderHash + ) + ); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_revertsIfExtraDataLengthIsLessThan93() public { + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"01"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + InvalidExtraData.selector, + "extraData length must be at least 93 bytes", + zoneParameters.orderHash + ) + ); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_revertsIfExtraDataVersionIsNotSupported() public { + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"01f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + UnsupportedExtraDataVersion.selector, + uint8(1) + ) + ); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_revertsIfSignatureHasExpired() public { + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"00f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + SignatureExpired.selector, + 1714829338, + 1712271399, + bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) + ) + ); + vm.warp(1714829338); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller() public { + address randomFulfiller = makeAddr("random"); + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: randomFulfiller, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a08374382308b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + InvalidFulfiller.selector, + FULFILLER, + randomFulfiller, + bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) + ) + ); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_revertsIfSignerIsNotActive() public { + ImmutableSignedZoneV2 zone = _newZone(); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + vm.expectRevert( + abi.encodeWithSelector( + SignerNotActive.selector, + address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C) + ) + ); + zone.validateOrder(zoneParameters); + } + + function test_validateOrder_returnsMagicValueOnSuccessfulValidation() public { + ImmutableSignedZoneV2 zone = _newZone(); + vm.prank(OWNER); + zone.addSigner(address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C)); + ZoneParameters memory zoneParameters = ZoneParameters({ + orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), + fulfiller: FULFILLER, + offerer: OFFERER, + offer: new SpentItem[](0), + consideration: new ReceivedItem[](0), + extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230"), + orderHashes: new bytes32[](0), + startTime: 0, + endTime: 0, + zoneHash: bytes32(0) + }); + assertEq(zone.validateOrder(zoneParameters), bytes4(0x17b1f942)); + } + /* _getSupportedSubstandards - L */ function test_getSupportedSubstandards() public { From cdf917413916ff241d408081a185884efc84f100 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Fri, 5 Apr 2024 11:17:32 +1100 Subject: [PATCH 46/79] Forge format --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index ca9da8ec..9fdf21f2 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -244,11 +244,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { zoneHash: bytes32(0) }); vm.expectRevert( - abi.encodeWithSelector( - InvalidExtraData.selector, - "extraData is empty", - zoneParameters.orderHash - ) + abi.encodeWithSelector(InvalidExtraData.selector, "extraData is empty", zoneParameters.orderHash) ); zone.validateOrder(zoneParameters); } @@ -269,9 +265,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); vm.expectRevert( abi.encodeWithSelector( - InvalidExtraData.selector, - "extraData length must be at least 93 bytes", - zoneParameters.orderHash + InvalidExtraData.selector, "extraData length must be at least 93 bytes", zoneParameters.orderHash ) ); zone.validateOrder(zoneParameters); @@ -285,30 +279,29 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes(hex"01f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + extraData: bytes( + hex"01f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" + ), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); - vm.expectRevert( - abi.encodeWithSelector( - UnsupportedExtraDataVersion.selector, - uint8(1) - ) - ); + vm.expectRevert(abi.encodeWithSelector(UnsupportedExtraDataVersion.selector, uint8(1))); zone.validateOrder(zoneParameters); } function test_validateOrder_revertsIfSignatureHasExpired() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes(hex"00f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + extraData: bytes( + hex"00f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" + ), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, @@ -335,7 +328,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a08374382308b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8"), + extraData: bytes( + hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a08374382308b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" + ), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, @@ -360,17 +355,16 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230"), + extraData: bytes( + hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230" + ), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); vm.expectRevert( - abi.encodeWithSelector( - SignerNotActive.selector, - address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C) - ) + abi.encodeWithSelector(SignerNotActive.selector, address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C)) ); zone.validateOrder(zoneParameters); } @@ -385,7 +379,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes(hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230"), + extraData: bytes( + hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230" + ), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, From bf451afdf362997d31290bbb3bd90956006a1994 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Fri, 5 Apr 2024 11:40:43 +1100 Subject: [PATCH 47/79] Formatting --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 130 ++++++++++++++---- 1 file changed, 102 insertions(+), 28 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 9fdf21f2..42679398 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -179,6 +179,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { uint256[] memory substandards, string memory documentationURI ) = abi.decode(schemas[0].metadata, (bytes32, string, uint256[], string)); + assertEq(name, expectedZoneName); assertEq(schemas.length, 1); assertEq(schemas[0].id, 7); @@ -210,6 +211,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { uint256[] memory substandards, string memory documentationURI ) = zone.sip7Information(); + assertEq(domainSeparator, expectedDomainSeparator); assertEq(apiEndpoint, expectedApiEndpoint); assertEq(substandards, expectedSubstandards); @@ -417,6 +419,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandards_emptyContext() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -429,13 +432,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); - bytes memory context = new bytes(0); - zone.exposed_validateSubstandards(context, zoneParameters); + + zone.exposed_validateSubstandards(new bytes(0), zoneParameters); } function test_validateSubstandards_substandard3() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - ReceivedItem[] memory consideration = new ReceivedItem[](1); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, token: address(0x2), @@ -443,20 +447,22 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - consideration[0] = receivedItem; + receivedItems[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), offerer: address(0x3), offer: new SpentItem[](0), - consideration: consideration, + consideration: receivedItems, extraData: new bytes(0), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); zone.exposed_validateSubstandards(context, zoneParameters); @@ -464,8 +470,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandards_substandard4() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -485,11 +493,16 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32(uint256(1)), bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) ); + zone.exposed_validateSubstandards(context, zoneParameters); } function test_validateSubstandards_substandard6() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ itemType: ItemType.ERC721, @@ -498,8 +511,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -516,12 +528,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); + zone.exposed_validateSubstandards(context, zoneParameters); } function test_validateSubstandards_multipleSubstandardsInCorrectOrder() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - ReceivedItem[] memory consideration = new ReceivedItem[](1); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, token: address(0x2), @@ -529,25 +543,27 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - consideration[0] = receivedItem; + receivedItems[0] = receivedItem; + bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), offerer: address(0x3), offer: new SpentItem[](0), - consideration: consideration, + consideration: receivedItems, extraData: new bytes(0), orderHashes: orderHashes, startTime: 0, endTime: 0, zoneHash: bytes32(0) }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); bytes memory substandard4Data = abi.encode(orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data); zone.exposed_validateSubstandards(context, zoneParameters); @@ -635,7 +651,8 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandards_revertsOnMultipleSubstandardsInIncorrectOrder() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - ReceivedItem[] memory consideration = new ReceivedItem[](1); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, token: address(0x2), @@ -643,25 +660,27 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - consideration[0] = receivedItem; + receivedItems[0] = receivedItem; + bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), offerer: address(0x3), offer: new SpentItem[](0), - consideration: consideration, + consideration: receivedItems, extraData: new bytes(0), orderHashes: orderHashes, startTime: 0, endTime: 0, zoneHash: bytes32(0) }); - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); bytes memory substandard4Data = abi.encode(orderHashes); - bytes memory context = abi.encodePacked(bytes1(0x04), substandard4Data, bytes1(0x03), substandard3Data); vm.expectRevert( @@ -676,6 +695,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -688,12 +708,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + uint256 substandardLengthResult = zone.exposed_validateSubstandard3(hex"04", zoneParameters); assertEq(substandardLengthResult, 0); } function test_validateSubstandard3_revertsIfContextLengthIsInvalid() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -706,7 +728,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + bytes memory context = abi.encodePacked(bytes1(0x03), bytes10(0)); + vm.expectRevert( abi.encodeWithSelector( InvalidExtraData.selector, @@ -719,7 +743,8 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - ReceivedItem[] memory consideration = new ReceivedItem[](1); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, token: address(0x2), @@ -727,20 +752,23 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - consideration[0] = receivedItem; + receivedItems[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), offerer: address(0x3), offer: new SpentItem[](0), - consideration: consideration, + consideration: receivedItems, extraData: new bytes(0), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); + bytes memory context = abi.encodePacked(bytes1(0x03), bytes32(0)); + vm.expectRevert( abi.encodeWithSelector( SubstandardViolation.selector, 3, "invalid consideration hash", zoneParameters.orderHash @@ -751,7 +779,8 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard3_returns33OnSuccess() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); - ReceivedItem[] memory consideration = new ReceivedItem[](1); + + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ itemType: ItemType.ERC721, token: address(0x2), @@ -759,22 +788,25 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - consideration[0] = receivedItem; + receivedItems[0] = receivedItem; + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), offerer: address(0x3), offer: new SpentItem[](0), - consideration: consideration, + consideration: receivedItems, extraData: new bytes(0), orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); + uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); assertEq(substandardLengthResult, 33); } @@ -783,6 +815,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -795,12 +828,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + uint256 substandardLengthResult = zone.exposed_validateSubstandard4(hex"02", zoneParameters); assertEq(substandardLengthResult, 0); } function test_validateSubstandard4_revertsIfContextLengthIsInvalid() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -813,7 +848,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + bytes memory context = abi.encodePacked(bytes1(0x04), bytes10(0)); + vm.expectRevert( abi.encodeWithSelector( InvalidExtraData.selector, @@ -826,8 +863,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard4_revertsIfDerivedOrderHashesIsNotEqualToHashesInContext() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -842,6 +881,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x0)); + vm.expectRevert( abi.encodeWithSelector(SubstandardViolation.selector, 4, "invalid order hashes", zoneParameters.orderHash) ); @@ -850,8 +890,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -871,6 +913,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32(uint256(1)), bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) ); + uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); // bytes1 + bytes32 + bytes32 + bytes32 = 97 assertEq(substandardLengthResult, 97); @@ -880,6 +923,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -892,12 +936,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + uint256 substandardLengthResult = zone.exposed_validateSubstandard6(hex"04", zoneParameters); assertEq(substandardLengthResult, 0); } function test_validateSubstandard6_revertsIfContextLengthIsInvalid() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -910,7 +956,9 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { endTime: 0, zoneHash: bytes32(0) }); + bytes memory context = abi.encodePacked(bytes1(0x06), bytes10(0)); + vm.expectRevert( abi.encodeWithSelector( InvalidExtraData.selector, @@ -923,6 +971,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard6_revertsIfDerivedReceivedItemsHashesIsNotEqualToHashesInContext() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ itemType: ItemType.ERC721, @@ -931,8 +983,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -947,6 +998,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), bytes32(uint256(0x123456))); + vm.expectRevert( abi.encodeWithSelector( SubstandardViolation.selector, 6, "invalid consideration hash", zoneParameters.orderHash @@ -957,6 +1009,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + + SpentItem[] memory spentItems = new SpentItem[](1); + spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ itemType: ItemType.ERC721, @@ -965,8 +1021,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); - SpentItem[] memory spentItems = new SpentItem[](1); - spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), fulfiller: address(0x2), @@ -983,6 +1038,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); + uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); // bytes1 + uint256 + bytes32 = 65 assertEq(substandardLengthResult, 65); @@ -992,13 +1048,16 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](0); + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 0, 0); assertEq(receivedItemsHash, bytes32(0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)); } function test_deriveReceivedItemsHash_returnsHashForValidReceivedItems() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](2); receivedItems[0] = ReceivedItem({ itemType: ItemType.ERC721, @@ -1014,6 +1073,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { amount: 1, recipient: payable(address(0x3)) }); + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10); assertEq(receivedItemsHash, bytes32(0xf01bacf40a3dd95740faaaad186bf1c000a9edc06008ea07c789ea761d7f3ffb)); } @@ -1022,46 +1082,55 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsEmpty() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory emptySourceArray = new bytes32[](0); bytes32[] memory valuesArray = new bytes32[](2); + bool includes = zone.exposed_bytes32ArrayIncludes(emptySourceArray, valuesArray); assertFalse(includes); } function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsSmallerThanValuesArray() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](1); bytes32[] memory valuesArray = new bytes32[](2); + bool includesEmptySource = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); assertFalse(includesEmptySource); } function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayDoesNotIncludeValuesArray() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](2); sourceArray[0] = bytes32(uint256(1)); sourceArray[1] = bytes32(uint256(2)); bytes32[] memory valuesArray = new bytes32[](2); valuesArray[0] = bytes32(uint256(3)); valuesArray[1] = bytes32(uint256(4)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); assertFalse(includes); } function test_bytes32ArrayIncludes_returnsTrueIfSourceArrayIncludesValuesArray() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](2); sourceArray[0] = bytes32(uint256(1)); sourceArray[1] = bytes32(uint256(2)); bytes32[] memory valuesArray = new bytes32[](2); valuesArray[0] = bytes32(uint256(1)); valuesArray[1] = bytes32(uint256(2)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); assertTrue(includes); } function test_bytes32ArrayIncludes_returnsTrueIfValuesArrayIsASubsetOfSourceArray() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32[] memory sourceArray = new bytes32[](4); sourceArray[0] = bytes32(uint256(1)); sourceArray[1] = bytes32(uint256(2)); @@ -1070,6 +1139,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32[] memory valuesArray = new bytes32[](2); valuesArray[0] = bytes32(uint256(1)); valuesArray[1] = bytes32(uint256(2)); + bool includes = zone.exposed_bytes32ArrayIncludes(sourceArray, valuesArray); assertTrue(includes); } @@ -1078,15 +1148,18 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparator = zone.exposed_domainSeparator(); assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); } function test_domainSeparator_returnsUpdatedDomainSeparatorIfChainIDIsDifferentFromValueSetOnDeployment() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparatorCached = zone.exposed_domainSeparator(); vm.chainId(31338); bytes32 domainSeparatorDerived = zone.exposed_domainSeparator(); + assertFalse(domainSeparatorCached == domainSeparatorDerived); assertEq(domainSeparatorDerived, bytes32(0x835aabb0d2af048df195a75a990b42533471d4a4e82842cd54a892eaac463d74)); } @@ -1095,6 +1168,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_deriveDomainSeparator_returnsDomainSeparatorForChainID() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 domainSeparator = zone.exposed_deriveDomainSeparator(); assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); } From 06b350871b26b598b8298fa830ae4f09d017369b Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:24:17 +1100 Subject: [PATCH 48/79] TD-1326: chore: add test for _deriveReceivedItemsHash --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 42679398..1072b189 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -29,6 +29,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { address internal immutable FULFILLER = makeAddr("fulfiller"); address internal immutable OFFERER = makeAddr("offerer"); + uint256 public constant MAX_UINT_TYPE = type(uint256).max; /* constructor */ @@ -1078,6 +1079,21 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { assertEq(receivedItemsHash, bytes32(0xf01bacf40a3dd95740faaaad186bf1c000a9edc06008ea07c789ea761d7f3ffb)); } + function test_deriveReceivedItemsHash_returnsHashForReceivedItemWithAVeryLargeAmount() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ReceivedItem[] memory receivedItems = new ReceivedItem[](1); + receivedItems[0] = ReceivedItem({ + itemType: ItemType.ERC721, + token: address(0x2), + identifier: 222, + amount: 1, + recipient: payable(address(0x3)) + }); + + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, MAX_UINT_TYPE, 10); + assertEq(receivedItemsHash, bytes32(0xa349370a3f10599b502d8886045635579be4cfa14bb3140589e5619e492f876c)); + } + /* _bytes32ArrayIncludes - N */ function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsEmpty() public { From 8fe89c7c4d1cb50c21c272d16564539a473ece8b Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:48:18 +1100 Subject: [PATCH 49/79] TD-1326: chore: add helper utility to build extradata --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 88 +++++++++++++------ .../ImmutableSignedZoneV2TestHelper.t.sol | 19 ++++ 2 files changed, 81 insertions(+), 26 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 1072b189..ebb38ca4 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -27,8 +27,6 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { error SignatureExpired(uint256 currentTimestamp, uint256 expiration, bytes32 orderHash); // SIP-7 error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); // SIP-7 - address internal immutable FULFILLER = makeAddr("fulfiller"); - address internal immutable OFFERER = makeAddr("offerer"); uint256 public constant MAX_UINT_TYPE = type(uint256).max; /* constructor */ @@ -295,16 +293,19 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfSignatureHasExpired() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + uint64 expiration = 100; + + bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes( - hex"00f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000660f3027d9ef9e6e50a74cc24433373b9cdd97693a02adcc94e562bb59a5af68190ecaea4414dcbe74618f6c77d11cbcf4a8345bbdf46e665249904925c95929ba6606638b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" - ), + extraData: extraData, orderHashes: new bytes32[](0), startTime: 0, endTime: 0, @@ -313,27 +314,31 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { vm.expectRevert( abi.encodeWithSelector( SignatureExpired.selector, - 1714829338, - 1712271399, + 1000, + 100, bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) ) ); - vm.warp(1714829338); + // set current block.timestamp to be 1000 + vm.warp(1000); zone.validateOrder(zoneParameters); } function test_validateOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller() public { + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); address randomFulfiller = makeAddr("random"); - ImmutableSignedZoneV2 zone = _newZone(); + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + uint64 expiration = 100; + + bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: randomFulfiller, offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes( - hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a08374382308b779c6b502204fca6bb0539cdc3dc258fe3ce7b53be0c4ad620899167fedaa8" - ), + extraData: extraData, orderHashes: new bytes32[](0), startTime: 0, endTime: 0, @@ -351,41 +356,72 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfSignerIsNotActive() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); + uint64 expiration = 100; + + bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData: bytes( - hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230" - ), + extraData:extraData, orderHashes: new bytes32[](0), startTime: 0, endTime: 0, zoneHash: bytes32(0) }); vm.expectRevert( - abi.encodeWithSelector(SignerNotActive.selector, address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C)) + abi.encodeWithSelector(SignerNotActive.selector, address(0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2)) ); zone.validateOrder(zoneParameters); } function test_validateOrder_returnsMagicValueOnSuccessfulValidation() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(); vm.prank(OWNER); - zone.addSigner(address(0xb14f041201f3546C5636A6E2e8E3C6d04A2A480C)); + 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); + + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); + bytes32 substandard3Data = bytes32(0xec07a42041c18889c5c5dcd348923ea9f3d0979735bd8b3b687ebda38d9b6a31); + bytes memory substandard4Data = abi.encode(orderHashes); + bytes memory substandard6Data = abi.encodePacked(uint256(10), substandard3Data); + bytes memory context = abi.encodePacked( + bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data, bytes1(0x06), substandard6Data + ); + + bytes memory extraData = _buildExtraData(orderHash, expiration, zone, context); + ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, offerer: OFFERER, - offer: new SpentItem[](0), - consideration: new ReceivedItem[](0), - extraData: bytes( - hex"0071458637cd221877830a21f543e8b731e93c362700000000660f35b870eb77aa71239beb73729a53fac6c035dd6008dfafbacea3f8492bfcfeff3f2bc2e6116f4f94b56f20a5672ae38c9a4764fb152aa37f6134e12a9a0837438230" - ), - orderHashes: new bytes32[](0), + offer: spentItems, + consideration: receivedItems, + extraData: extraData, + orderHashes: orderHashes, startTime: 0, endTime: 0, zoneHash: bytes32(0) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol index 7673fe9f..baad3309 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -6,9 +6,13 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; abstract contract ImmutableSignedZoneV2TestHelper is Test { address internal OWNER = makeAddr("owner"); + address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 + address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a + address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 function _newZone() internal returns (ImmutableSignedZoneV2) { return new ImmutableSignedZoneV2( @@ -27,4 +31,19 @@ abstract contract ImmutableSignedZoneV2TestHelper is Test { OWNER ); } + + function _buildExtraData( + bytes32 orderHash, + uint64 expiration, + ImmutableSignedZoneV2Harness zone, + bytes memory context + ) internal returns (bytes memory) { + (, uint256 signerPK) = makeAddrAndKey("signer"); + bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, expiration, orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 r, bytes32 s) = vm.sign(signerPK, signatureDigest); + bytes memory extraData = abi.encodePacked(hex"00", FULFILLER, expiration, r, s, context); + return extraData; + } + } From 0a4d5024a4cc5d037e95106dcdb324c529854f02 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:48:36 +1100 Subject: [PATCH 50/79] TD-1326: chore: fix up large amount test --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index ebb38ca4..1be4d853 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -1122,11 +1122,11 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { itemType: ItemType.ERC721, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); - bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, MAX_UINT_TYPE, 10); + bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, MAX_UINT_TYPE, 100); assertEq(receivedItemsHash, bytes32(0xa349370a3f10599b502d8886045635579be4cfa14bb3140589e5619e492f876c)); } From 55176a9c10527e362034807d62986c023ca01f31 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:19:46 +1100 Subject: [PATCH 51/79] TD-1326: chore: fix up token types and amounts for received items. --- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 1be4d853..08df46d5 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -478,10 +478,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[0] = receivedItem; @@ -500,7 +500,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); - bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes32 substandard3Data = bytes32(0x7426c58179a9510d8d9f42ecb0deff6c2fdb177027f684c57f1f2795e25b433e); bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); zone.exposed_validateSubstandards(context, zoneParameters); } @@ -542,10 +542,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); @@ -563,7 +563,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); - bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; + bytes32 substandard6Data = 0x6d0303fb2c992bf1970cab0fae2e4cd817df77741cee30dd7917b719a165af3e; bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); zone.exposed_validateSubstandards(context, zoneParameters); @@ -574,10 +574,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[0] = receivedItem; @@ -598,8 +598,8 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { zoneHash: bytes32(0) }); - // console.logBytes32(zone.exposed_deriveReceivedItemsHash(consideration, 1, 1)); - bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); + bytes32 substandard3Data = bytes32(0x7426c58179a9510d8d9f42ecb0deff6c2fdb177027f684c57f1f2795e25b433e); bytes memory substandard4Data = abi.encode(orderHashes); bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data); @@ -691,10 +691,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[0] = receivedItem; @@ -716,7 +716,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); - bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes32 substandard3Data = bytes32(0x7426c58179a9510d8d9f42ecb0deff6c2fdb177027f684c57f1f2795e25b433e); bytes memory substandard4Data = abi.encode(orderHashes); bytes memory context = abi.encodePacked(bytes1(0x04), substandard4Data, bytes1(0x03), substandard3Data); @@ -783,10 +783,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[0] = receivedItem; @@ -819,10 +819,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[0] = receivedItem; @@ -841,7 +841,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 1, 1)); - bytes32 substandard3Data = bytes32(0x9062b0574be745508bed2ff7f8f5057446b89d16d35980b2a26f8e4cb03ddf91); + bytes32 substandard3Data = bytes32(0x7426c58179a9510d8d9f42ecb0deff6c2fdb177027f684c57f1f2795e25b433e); bytes memory context = abi.encodePacked(bytes1(0x03), substandard3Data); uint256 substandardLengthResult = zone.exposed_validateSubstandard3(context, zoneParameters); @@ -1014,10 +1014,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); @@ -1052,10 +1052,10 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); @@ -1073,7 +1073,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { }); // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); - bytes32 substandard6Data = 0xff3642433fc0f83e6d23869de6d358c7c36e3257da4bd89a3b6d17dd25e7c823; + bytes32 substandard6Data = 0x6d0303fb2c992bf1970cab0fae2e4cd817df77741cee30dd7917b719a165af3e; bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), substandard6Data); uint256 substandardLengthResult = zone.exposed_validateSubstandard6(context, zoneParameters); @@ -1097,37 +1097,39 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { ReceivedItem[] memory receivedItems = new ReceivedItem[](2); receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); receivedItems[1] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 199, - amount: 1, + amount: 10, recipient: payable(address(0x3)) }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10)); bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, 100, 10); - assertEq(receivedItemsHash, bytes32(0xf01bacf40a3dd95740faaaad186bf1c000a9edc06008ea07c789ea761d7f3ffb)); + assertEq(receivedItemsHash, bytes32(0x8f5c27e415d7805dea8816d4030dc2c0ce11f8f48a0adcde373021dec7b41aad)); } function test_deriveReceivedItemsHash_returnsHashForReceivedItemWithAVeryLargeAmount() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ - itemType: ItemType.ERC721, + itemType: ItemType.ERC20, token: address(0x2), identifier: 222, amount: 10, recipient: payable(address(0x3)) }); + // console.logBytes32(zone.exposed_deriveReceivedItemsHash(receivedItems, MAX_UINT_TYPE, 100)); bytes32 receivedItemsHash = zone.exposed_deriveReceivedItemsHash(receivedItems, MAX_UINT_TYPE, 100); - assertEq(receivedItemsHash, bytes32(0xa349370a3f10599b502d8886045635579be4cfa14bb3140589e5619e492f876c)); + assertEq(receivedItemsHash, bytes32(0xdb99f7eb854f29cd6f8faedea38d7da25073ef9876653ff45ab5c10e51f8ce4f)); } /* _bytes32ArrayIncludes - N */ From fc416b613e5d31abf960d3a7258a49d32d37d0b4 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:28:49 +1100 Subject: [PATCH 52/79] TD-1326: chore: fix up function signature --- test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol | 10 +++++----- .../zones/ImmutableSignedZoneV2TestHelper.t.sol | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 08df46d5..bb61eb30 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -297,7 +297,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -330,7 +330,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -360,7 +360,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(orderHash, expiration, zone, new bytes(0)); + bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -368,7 +368,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { offerer: OFFERER, offer: new SpentItem[](0), consideration: new ReceivedItem[](0), - extraData:extraData, + extraData: extraData, orderHashes: new bytes32[](0), startTime: 0, endTime: 0, @@ -412,7 +412,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data, bytes1(0x06), substandard6Data ); - bytes memory extraData = _buildExtraData(orderHash, expiration, zone, context); + bytes memory extraData = _buildExtraData(zone, orderHash, expiration, context); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol index baad3309..ccf3246b 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -9,7 +9,7 @@ import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; abstract contract ImmutableSignedZoneV2TestHelper is Test { - address internal OWNER = makeAddr("owner"); + address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 @@ -33,9 +33,9 @@ abstract contract ImmutableSignedZoneV2TestHelper is Test { } function _buildExtraData( + ImmutableSignedZoneV2Harness zone, bytes32 orderHash, uint64 expiration, - ImmutableSignedZoneV2Harness zone, bytes memory context ) internal returns (bytes memory) { (, uint256 signerPK) = makeAddrAndKey("signer"); From d97783af6f40fa4f25d4ea4f3808cacd7247bca2 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Fri, 5 Apr 2024 17:21:45 +1100 Subject: [PATCH 53/79] WIP integration test --- .../seaport/ImmutableSeaportHarness.t.sol | 18 ++ ...utableSeaportSignedZoneV2Integration.t.sol | 218 ++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 test/trading/seaport/ImmutableSeaportHarness.t.sol create mode 100644 test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol diff --git a/test/trading/seaport/ImmutableSeaportHarness.t.sol b/test/trading/seaport/ImmutableSeaportHarness.t.sol new file mode 100644 index 00000000..4f29a5b8 --- /dev/null +++ b/test/trading/seaport/ImmutableSeaportHarness.t.sol @@ -0,0 +1,18 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +pragma solidity ^0.8.17; + +import {ImmutableSeaport} from "../../../contracts/trading/seaport/ImmutableSeaport.sol"; + +contract ImmutableSeaportHarness is ImmutableSeaport { + constructor(address conduitController, address owner) ImmutableSeaport(conduitController, owner) {} + + function exposed_deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) external pure returns (bytes32 value) { + return _deriveEIP712Digest(domainSeparator, orderHash); + } + + function exposed_domainSeparator() external view returns (bytes32) { + return _domainSeparator(); + } +} diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol new file mode 100644 index 00000000..8a5c1135 --- /dev/null +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -0,0 +1,218 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {ImmutableSignedZoneV2} from "../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; +import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.sol"; +import {ImmutableERC20FixedSupplyNoBurn} from "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; +import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; +import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; +import {Consideration} from "seaport-core/src/lib/Consideration.sol"; +import { + AdvancedOrder, + BasicOrderParameters, + ConsiderationItem, + CriteriaResolver, + Execution, + Fulfillment, + FulfillmentComponent, + OrderComponents, + OfferItem, + OrderParameters +} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +// solhint-disable func-name-mixedcase + +contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { + ImmutableSeaportHarness seaport; + ImmutableSignedZoneV2 zone; + ImmutableERC20FixedSupplyNoBurn erc20Token; + ImmutableERC1155 erc1155Token; + + address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 + address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 + address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 + address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a + address internal immutable PROTOCOL_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); + address internal immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); + address internal immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); + + // bytes32 internal immutable ORDER_TYPE_HASH = keccak256( + // abi.encodePacked( + // abi.encodePacked( + // "OrderComponents(", + // "address offerer,", + // "address zone,", + // "OfferItem[] offer,", + // "ConsiderationItem[] consideration,", + // "uint8 orderType,", + // "uint256 startTime,", + // "uint256 endTime,", + // "bytes32 zoneHash,", + // "uint256 salt,", + // "bytes32 conduitKey,", + // "uint256 counter", + // ")" + // ), + // keccak256( + // abi.encodePacked( + // "ConsiderationItem(", + // "uint8 itemType,", + // "address token,", + // "uint256 identifierOrCriteria,", + // "uint256 startAmount,", + // "uint256 endAmount,", + // "address recipient", + // ")" + // ) + // ), + // keccak256( + // abi.encodePacked( + // "OfferItem(", + // "uint8 itemType,", + // "address token,", + // "uint256 identifierOrCriteria,", + // "uint256 startAmount,", + // "uint256 endAmount", + // ")" + // ) + // ) + // ) + // ); + + + function setUp() public { + // OAL + OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); + + // tokens + erc20Token = new ImmutableERC20FixedSupplyNoBurn("TestERC20", "ERC20", 1000, OWNER, OWNER); + erc1155Token = new ImmutableERC1155( + OWNER, + "TestERC1155", + "", + "", + address(operatorAllowlist), + ROYALTY_FEE_RECEIVER, + 100 // 1% + ); + + // seaport + ConduitController conduitController = new ConduitController(); + seaport = new ImmutableSeaportHarness(address(conduitController), OWNER); + address[] memory allowlistAddress = new address[](1); + allowlistAddress[0] = address(seaport); + operatorAllowlist.addAddressesToAllowlist(allowlistAddress); + zone = new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + OWNER + ); + zone.addSigner(SIGNER); + seaport.setAllowedZone(address(zone), true); + } + + function test_fulfillAdvancedOrder_withPartialFill() public { + OfferItem[] memory offerItems = new OfferItem[](1); + + offerItems[0] = OfferItem({ + itemType: ItemType.ERC1155, + token: address(erc1155Token), + identifierOrCriteria: uint256(50), + startAmount: uint256(100), + endAmount: uint256(100) + }); + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + // original item + considerationItems[0] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(200), + endAmount: uint256(200), + recipient: OFFERER + }); + // protocol fee - 2% + considerationItems[1] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(4), + endAmount: uint256(4), + recipient: PROTOCOL_FEE_RECEIVER + }); + // royalty fee - 1% + considerationItems[2] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(2), + endAmount: uint256(2), + recipient: ROYALTY_FEE_RECEIVER + }); + // ecosystem fee - 3% + considerationItems[3] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(6), + endAmount: uint256(6), + recipient: ECOSYSTEM_FEE_RECEIVER + }); + + OrderParameters orderParameters = OrderParameters({ + offerer: OFFERER, + zone: address(zone), + offer: offerItems, + consideration: considerationItems, + orderType: OrderType.PARTIAL_RESTRICTED, + startTime: uint256(1000), + endTime: uint256(5000), + zoneHash: bytes32(0), + salt: uint256(123), + conduitKey: bytes32(0), + totalOriginalConsiderationItems: uint256(1) + }); + + bytes32 orderHash = seaport.getOrderHash( + OrderComponents({ + offerer: orderParameters.offerer, + zone: orderParameters.zone, + offer: orderParameters.offer, + consideration: orderParameters.consideration[0:1], + orderType: orderParameters.orderType, + startTime: orderParameters.startTime, + endTime: orderParameters.endTime, + zoneHash: orderParameters.zoneHash, + salt: orderParameters.salt, + conduitKey: orderParameters.conduitKey, + counter: seaport.getCounter(orderParameters.offerer) + }) + ); + + bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); + + (, uint256 offererPK) = makeAddrAndKey("offerer"); + (, bytes32 r, bytes32 s) = vm.sign(signerPK, orderDigest); + bytes memory orderSignature = abi.encodePacked(r, s); + + AdvancedOrder advancedOrder = AdvancedOrder({ + parameters: orderParameters, + numerator: uint120(1), + denominator: uint120(1), + signature: orderSignature, + extraData: new bytes(0) + }); + + seaport.fulfillAdvancedOrder(AdvancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + } +} + +// solhint-enable func-name-mixedcase From 305983a0c8493a64b82e66e824fcad6a07392419 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 10:00:26 +1000 Subject: [PATCH 54/79] Complete integration test --- ...utableSeaportSignedZoneV2Integration.t.sol | 97 +++++++++++++++---- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 8a5c1135..0c970ce4 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -1,12 +1,12 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "forge-std/Test.sol"; -import {ImmutableSignedZoneV2} from "../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2Harness} from "./zones/ImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; -import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.sol"; +import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; import {ImmutableERC20FixedSupplyNoBurn} from "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; @@ -29,10 +29,10 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { - ImmutableSeaportHarness seaport; - ImmutableSignedZoneV2 zone; - ImmutableERC20FixedSupplyNoBurn erc20Token; - ImmutableERC1155 erc1155Token; + ImmutableSeaportHarness internal seaport; + ImmutableSignedZoneV2Harness internal zone; + ImmutableERC20FixedSupplyNoBurn internal erc20Token; + ImmutableERC1155 internal erc1155Token; address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 @@ -87,7 +87,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { function setUp() public { - // OAL + // operator allowlist OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); // tokens @@ -101,26 +101,31 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { ROYALTY_FEE_RECEIVER, 100 // 1% ); + vm.prank(OWNER); + erc1155Token.grantMinterRole(OWNER); - // seaport - ConduitController conduitController = new ConduitController(); - seaport = new ImmutableSeaportHarness(address(conduitController), OWNER); - address[] memory allowlistAddress = new address[](1); - allowlistAddress[0] = address(seaport); - operatorAllowlist.addAddressesToAllowlist(allowlistAddress); - zone = new ImmutableSignedZoneV2( + // zone + zone = new ImmutableSignedZoneV2Harness( "MyZoneName", "https://www.immutable.com", "https://www.immutable.com/docs", OWNER ); zone.addSigner(SIGNER); + + // seaport + ConduitController conduitController = new ConduitController(); + seaport = new ImmutableSeaportHarness(address(conduitController), OWNER); seaport.setAllowedZone(address(zone), true); + + // operator allowlist addresses + address[] memory allowlistAddress = new address[](1); + allowlistAddress[0] = address(seaport); + operatorAllowlist.addAddressesToAllowlist(allowlistAddress); } function test_fulfillAdvancedOrder_withPartialFill() public { OfferItem[] memory offerItems = new OfferItem[](1); - offerItems[0] = OfferItem({ itemType: ItemType.ERC1155, token: address(erc1155Token), @@ -173,7 +178,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { offer: offerItems, consideration: considerationItems, orderType: OrderType.PARTIAL_RESTRICTED, - startTime: uint256(1000), + startTime: uint256(0), endTime: uint256(5000), zoneHash: bytes32(0), salt: uint256(123), @@ -200,18 +205,68 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); (, uint256 offererPK) = makeAddrAndKey("offerer"); - (, bytes32 r, bytes32 s) = vm.sign(signerPK, orderDigest); - bytes memory orderSignature = abi.encodePacked(r, s); + (, bytes32 listingR, bytes32 listingS) = vm.sign(signerPK, orderDigest); + bytes memory orderSignature = abi.encodePacked(listingR, listingS); + + (, uint256 signerPK) = makeAddrAndKey("signer"); + ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); + receivedItems[0] = ReceivedItem({ + itemType: considerationItems[0].itemType, + token: considerationItems[0].token, + identifier: considerationItems[0].identifierOrCriteria, + amount: considerationItems[0].startAmount, + recipient: considerationItems[0].recipient + }); + receivedItems[1] = ReceivedItem({ + itemType: considerationItems[1].itemType, + token: considerationItems[1].token, + identifier: considerationItems[1].identifierOrCriteria, + amount: considerationItems[1].startAmount, + recipient: considerationItems[1].recipient + }); + receivedItems[2] = ReceivedItem({ + itemType: considerationItems[2].itemType, + token: considerationItems[2].token, + identifier: considerationItems[2].identifierOrCriteria, + amount: considerationItems[2].startAmount, + recipient: considerationItems[2].recipient + }); + receivedItems[3] = ReceivedItem({ + itemType: considerationItems[3].itemType, + token: considerationItems[3].token, + identifier: considerationItems[3].identifierOrCriteria, + amount: considerationItems[3].startAmount, + recipient: considerationItems[3].recipient + }); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint256(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + bytes memory extraData = abi.encodePacked(hex"00", FULFILLER, uint256(4000), signedOrderR, signedOrderS, context); AdvancedOrder advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), denominator: uint120(1), signature: orderSignature, - extraData: new bytes(0) + extraData: extraData }); - seaport.fulfillAdvancedOrder(AdvancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + // mints + vm.prank(OWNER); + erc20Token.transfer(FULFILLER, considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + considerationItems[3].startAmount); + vm.prank(OWNER); + erc1155Token.safeMint(FULFILLER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); + + // approvals + vm.prank(OFFERER); + erc1155Token.setApprovalForAll(address(seaport), true); + vm.prank(FULFILLER); + erc20Token.approve(address(seaport), considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + considerationItems[3].startAmount); + + // fulfill + seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); } } From 8ad95a05ab6f6bcdc05b528101e3cc3f95947806 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 10:34:05 +1000 Subject: [PATCH 55/79] revert solidity pragma --- .../seaport/ImmutableSeaportSignedZoneV2Integration.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 0c970ce4..260d144d 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -1,7 +1,7 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -pragma solidity ^0.8.19; +pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {ImmutableSignedZoneV2Harness} from "./zones/ImmutableSignedZoneV2Harness.t.sol"; From a7e53928fa68d6c5ab2d10327258c0e534dd525f Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 10:35:57 +1000 Subject: [PATCH 56/79] forge fmt --- ...utableSeaportSignedZoneV2Integration.t.sol | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 260d144d..6237b305 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -7,7 +7,8 @@ import "forge-std/Test.sol"; import {ImmutableSignedZoneV2Harness} from "./zones/ImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; -import {ImmutableERC20FixedSupplyNoBurn} from "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; +import {ImmutableERC20FixedSupplyNoBurn} from + "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; import {Consideration} from "seaport-core/src/lib/Consideration.sol"; @@ -26,7 +27,7 @@ import { import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -// solhint-disable func-name-mixedcase +// solhint-disable func-name-mixedcase private-vars-leading-underscore contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { ImmutableSeaportHarness internal seaport; @@ -85,7 +86,6 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // ) // ); - function setUp() public { // operator allowlist OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); @@ -243,7 +243,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint256(4000), orderHash, context); bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - bytes memory extraData = abi.encodePacked(hex"00", FULFILLER, uint256(4000), signedOrderR, signedOrderS, context); + bytes memory extraData = + abi.encodePacked(hex"00", FULFILLER, uint256(4000), signedOrderR, signedOrderS, context); AdvancedOrder advancedOrder = AdvancedOrder({ parameters: orderParameters, @@ -255,7 +256,11 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // mints vm.prank(OWNER); - erc20Token.transfer(FULFILLER, considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + considerationItems[3].startAmount); + erc20Token.transfer( + FULFILLER, + considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + + considerationItems[3].startAmount + ); vm.prank(OWNER); erc1155Token.safeMint(FULFILLER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); @@ -263,11 +268,15 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { vm.prank(OFFERER); erc1155Token.setApprovalForAll(address(seaport), true); vm.prank(FULFILLER); - erc20Token.approve(address(seaport), considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + considerationItems[3].startAmount); + erc20Token.approve( + address(seaport), + considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount + + considerationItems[3].startAmount + ); // fulfill seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); } } -// solhint-enable func-name-mixedcase +// solhint-enable func-name-mixedcase private-vars-leading-underscore From cf4702517601a7a5f02e4e6ef6f30563f6290120 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 10:36:26 +1000 Subject: [PATCH 57/79] Remove unused import --- .../seaport/ImmutableSeaportSignedZoneV2Integration.t.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 6237b305..806b1cc8 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -14,12 +14,8 @@ import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/Operato import {Consideration} from "seaport-core/src/lib/Consideration.sol"; import { AdvancedOrder, - BasicOrderParameters, ConsiderationItem, CriteriaResolver, - Execution, - Fulfillment, - FulfillmentComponent, OrderComponents, OfferItem, OrderParameters From a1e685c8d43068a38672aadb6679f1104acabea8 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 10:40:14 +1000 Subject: [PATCH 58/79] solhints --- .../seaport/ImmutableSeaportSignedZoneV2Integration.t.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 806b1cc8..cf6ba175 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -1,8 +1,10 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; +// solhint-disable-next-line no-global-import import "forge-std/Test.sol"; import {ImmutableSignedZoneV2Harness} from "./zones/ImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; @@ -11,7 +13,6 @@ import {ImmutableERC20FixedSupplyNoBurn} from "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; -import {Consideration} from "seaport-core/src/lib/Consideration.sol"; import { AdvancedOrder, ConsiderationItem, @@ -23,7 +24,7 @@ import { import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -// solhint-disable func-name-mixedcase private-vars-leading-underscore +// solhint-disable func-name-mixedcase, private-vars-leading-underscore contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { ImmutableSeaportHarness internal seaport; @@ -275,4 +276,4 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { } } -// solhint-enable func-name-mixedcase private-vars-leading-underscore +// solhint-enable func-name-mixedcase, private-vars-leading-underscore From 032cf1f1f5d715ddccea45e382b76106e63cf14e Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 11:15:00 +1000 Subject: [PATCH 59/79] WIP --- .../seaport/ImmutableSeaportSignedZoneV2Integration.t.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index cf6ba175..51976bdc 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -84,6 +84,13 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // ); function setUp() public { + string constant operatorAllowlistArtifact = "../../../foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; + string constant erc1155TokenArtifact = "../../../foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; + string constant erc20TokenArtifact = "../../../foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; + + // address _weth9 = deployCode(weth9Artifact); + // weth9 = WETH9(_weth9); + // operator allowlist OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); From d20e73f59e3875de02fa23579e9e2ab2dbaf9552 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 14:25:09 +1000 Subject: [PATCH 60/79] wip --- ...utableSeaportSignedZoneV2Integration.t.sol | 167 ++++++++---------- .../seaport/utils/IImmutableERC1155.t.sol | 27 +++ .../utils/IOperatorAllowlistUpgradeable.t.sol | 16 ++ .../zones/IImmutableSignedZoneV2Harness.t.sol | 58 ++++++ .../zones/ImmutableSignedZoneV2Harness.t.sol | 1 + 5 files changed, 171 insertions(+), 98 deletions(-) create mode 100644 test/trading/seaport/utils/IImmutableERC1155.t.sol create mode 100644 test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol create mode 100644 test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 51976bdc..b21a3c3a 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -6,31 +6,31 @@ pragma solidity ^0.8.17; // solhint-disable-next-line no-global-import import "forge-std/Test.sol"; -import {ImmutableSignedZoneV2Harness} from "./zones/ImmutableSignedZoneV2Harness.t.sol"; +import {IImmutableSignedZoneV2Harness} from "./zones/IImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; -import {ImmutableERC20FixedSupplyNoBurn} from - "../../../contracts/token/erc20/preset/ImmutableERC20FixedSupplyNoBurn.sol"; -import {ImmutableERC1155} from "../../../contracts/token/erc1155/preset/ImmutableERC1155.sol"; -import {OperatorAllowlistUpgradeable} from "../../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; +import {IImmutableERC1155} from "./utils/IImmutableERC1155.t.sol"; +import {IOperatorAllowlistUpgradeable} from "./utils/IOperatorAllowlistUpgradeable.t.sol"; import { AdvancedOrder, ConsiderationItem, CriteriaResolver, OrderComponents, OfferItem, - OrderParameters + OrderParameters, + ReceivedItem } from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { - ImmutableSeaportHarness internal seaport; - ImmutableSignedZoneV2Harness internal zone; - ImmutableERC20FixedSupplyNoBurn internal erc20Token; - ImmutableERC1155 internal erc1155Token; + string constant internal OPERATOR_ALLOWLIST_ARTIFACT = "../../../foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; + string constant internal ERC1155_ARTIFACT = "../../../foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; + string constant internal ERC20_ARTIFACT = "../../../foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; + string constant internal ZONE_ARTIFACT = "../../../foundry-out/ImmutableSignedZoneV2Harness.sol/ImmutableSignedZoneV2Harness.json"; address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 @@ -40,91 +40,53 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { address internal immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); address internal immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); - // bytes32 internal immutable ORDER_TYPE_HASH = keccak256( - // abi.encodePacked( - // abi.encodePacked( - // "OrderComponents(", - // "address offerer,", - // "address zone,", - // "OfferItem[] offer,", - // "ConsiderationItem[] consideration,", - // "uint8 orderType,", - // "uint256 startTime,", - // "uint256 endTime,", - // "bytes32 zoneHash,", - // "uint256 salt,", - // "bytes32 conduitKey,", - // "uint256 counter", - // ")" - // ), - // keccak256( - // abi.encodePacked( - // "ConsiderationItem(", - // "uint8 itemType,", - // "address token,", - // "uint256 identifierOrCriteria,", - // "uint256 startAmount,", - // "uint256 endAmount,", - // "address recipient", - // ")" - // ) - // ), - // keccak256( - // abi.encodePacked( - // "OfferItem(", - // "uint8 itemType,", - // "address token,", - // "uint256 identifierOrCriteria,", - // "uint256 startAmount,", - // "uint256 endAmount", - // ")" - // ) - // ) - // ) - // ); + ImmutableSeaportHarness internal seaport; + IImmutableSignedZoneV2Harness internal zone; + IERC20 internal erc20Token; + IImmutableERC1155 internal erc1155Token; function setUp() public { - string constant operatorAllowlistArtifact = "../../../foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; - string constant erc1155TokenArtifact = "../../../foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; - string constant erc20TokenArtifact = "../../../foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; - - // address _weth9 = deployCode(weth9Artifact); - // weth9 = WETH9(_weth9); - // operator allowlist - OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); + // OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); + IOperatorAllowlistUpgradeable operatorAllowlist = IOperatorAllowlistUpgradeable(deployCode(OPERATOR_ALLOWLIST_ARTIFACT)); // tokens - erc20Token = new ImmutableERC20FixedSupplyNoBurn("TestERC20", "ERC20", 1000, OWNER, OWNER); - erc1155Token = new ImmutableERC1155( - OWNER, - "TestERC1155", - "", - "", - address(operatorAllowlist), - ROYALTY_FEE_RECEIVER, - 100 // 1% - ); + // erc20Token = new ImmutableERC20FixedSupplyNoBurn("TestERC20", "ERC20", 1000, OWNER, OWNER); + erc20Token = IERC20(deployCode(ERC20_ARTIFACT)); + // erc1155Token = new ImmutableERC1155( + // OWNER, + // "TestERC1155", + // "", + // "", + // address(operatorAllowlist), + // ROYALTY_FEE_RECEIVER, + // 100 // 1% + // ); + erc1155Token = IImmutableERC1155(deployCode(ERC1155_ARTIFACT, abi.encode(OWNER,"TestERC1155","","",address(operatorAllowlist),ROYALTY_FEE_RECEIVER,uint96(100)))); vm.prank(OWNER); erc1155Token.grantMinterRole(OWNER); // zone - zone = new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - OWNER - ); + zone = IImmutableSignedZoneV2Harness(deployCode(ZONE_ARTIFACT, abi.encode("MyZoneName","https://www.immutable.com","https://www.immutable.com/docs",OWNER))); + // zone = new ImmutableSignedZoneV2Harness( + // "MyZoneName", + // "https://www.immutable.com", + // "https://www.immutable.com/docs", + // OWNER + // ); + vm.prank(OWNER); zone.addSigner(SIGNER); // seaport ConduitController conduitController = new ConduitController(); seaport = new ImmutableSeaportHarness(address(conduitController), OWNER); + vm.prank(OWNER); seaport.setAllowedZone(address(zone), true); // operator allowlist addresses address[] memory allowlistAddress = new address[](1); allowlistAddress[0] = address(seaport); + vm.prank(OWNER); operatorAllowlist.addAddressesToAllowlist(allowlistAddress); } @@ -146,7 +108,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { identifierOrCriteria: uint256(0), startAmount: uint256(200), endAmount: uint256(200), - recipient: OFFERER + recipient: payable(OFFERER) }); // protocol fee - 2% considerationItems[1] = ConsiderationItem({ @@ -155,7 +117,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { identifierOrCriteria: uint256(0), startAmount: uint256(4), endAmount: uint256(4), - recipient: PROTOCOL_FEE_RECEIVER + recipient: payable(PROTOCOL_FEE_RECEIVER) }); // royalty fee - 1% considerationItems[2] = ConsiderationItem({ @@ -164,7 +126,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { identifierOrCriteria: uint256(0), startAmount: uint256(2), endAmount: uint256(2), - recipient: ROYALTY_FEE_RECEIVER + recipient: payable(ROYALTY_FEE_RECEIVER) }); // ecosystem fee - 3% considerationItems[3] = ConsiderationItem({ @@ -173,10 +135,13 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { identifierOrCriteria: uint256(0), startAmount: uint256(6), endAmount: uint256(6), - recipient: ECOSYSTEM_FEE_RECEIVER + recipient: payable(ECOSYSTEM_FEE_RECEIVER) }); - OrderParameters orderParameters = OrderParameters({ + ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); + originalConsiderationItems[0] = considerationItems[0]; + + OrderParameters memory orderParameters = OrderParameters({ offerer: OFFERER, zone: address(zone), offer: offerItems, @@ -195,7 +160,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { offerer: orderParameters.offerer, zone: orderParameters.zone, offer: orderParameters.offer, - consideration: orderParameters.consideration[0:1], + consideration: originalConsiderationItems, orderType: orderParameters.orderType, startTime: orderParameters.startTime, endTime: orderParameters.endTime, @@ -208,49 +173,55 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - (, uint256 offererPK) = makeAddrAndKey("offerer"); - (, bytes32 listingR, bytes32 listingS) = vm.sign(signerPK, orderDigest); - bytes memory orderSignature = abi.encodePacked(listingR, listingS); + bytes memory orderSignature; + { + (, uint256 offererPK) = makeAddrAndKey("offerer"); + (, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); + orderSignature = abi.encodePacked(listingR, listingS); + } - (, uint256 signerPK) = makeAddrAndKey("signer"); ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - receivedItems[0] = ReceivedItem({ + expectedReceivedItems[0] = ReceivedItem({ itemType: considerationItems[0].itemType, token: considerationItems[0].token, identifier: considerationItems[0].identifierOrCriteria, amount: considerationItems[0].startAmount, recipient: considerationItems[0].recipient }); - receivedItems[1] = ReceivedItem({ + expectedReceivedItems[1] = ReceivedItem({ itemType: considerationItems[1].itemType, token: considerationItems[1].token, identifier: considerationItems[1].identifierOrCriteria, amount: considerationItems[1].startAmount, recipient: considerationItems[1].recipient }); - receivedItems[2] = ReceivedItem({ + expectedReceivedItems[2] = ReceivedItem({ itemType: considerationItems[2].itemType, token: considerationItems[2].token, identifier: considerationItems[2].identifierOrCriteria, amount: considerationItems[2].startAmount, recipient: considerationItems[2].recipient }); - receivedItems[3] = ReceivedItem({ + expectedReceivedItems[3] = ReceivedItem({ itemType: considerationItems[3].itemType, token: considerationItems[3].token, identifier: considerationItems[3].identifierOrCriteria, amount: considerationItems[3].startAmount, recipient: considerationItems[3].recipient }); - bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); - bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint256(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - bytes memory extraData = - abi.encodePacked(hex"00", FULFILLER, uint256(4000), signedOrderR, signedOrderS, context); - AdvancedOrder advancedOrder = AdvancedOrder({ + bytes memory extraData; + { + (, uint256 signerPK) = makeAddrAndKey("signer"); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + extraData = abi.encodePacked(hex"00", FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + } + + AdvancedOrder memory advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), denominator: uint120(1), diff --git a/test/trading/seaport/utils/IImmutableERC1155.t.sol b/test/trading/seaport/utils/IImmutableERC1155.t.sol new file mode 100644 index 00000000..c32b7946 --- /dev/null +++ b/test/trading/seaport/utils/IImmutableERC1155.t.sol @@ -0,0 +1,27 @@ +// Copyright Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache 2.0 + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.17; + +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; + +/** + * @notice Interface for Immutable's ERC1155 + */ +interface IImmutableERC1155 is IERC1155 { + /** + * @notice Mints a new token + * @param to The address that will receive the minted tokens + * @param id The id of the token to mint + * @param value The amount of tokens to mint + * @param data Additional data + */ + function safeMint(address to, uint256 id, uint256 value, bytes memory data) external; + + /** + * @notice Grants minter role to the user + * @param user The address to grant the MINTER_ROLE to + */ + function grantMinterRole(address user) external; +} diff --git a/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol b/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol new file mode 100644 index 00000000..e16bf606 --- /dev/null +++ b/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol @@ -0,0 +1,16 @@ +// Copyright Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache 2.0 + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.17; + +/** + * @notice Required interface of an OperatorAllowlist compliant contract + */ +interface IOperatorAllowlistUpgradeable { + /** + * @notice Adds a list of multiple addresses to Allowlist + * @param addressTargets the addresses to be added to the allowlist + */ + function addAddressesToAllowlist(address[] calldata addressTargets) external; +} diff --git a/test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol new file mode 100644 index 00000000..a994838b --- /dev/null +++ b/test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol @@ -0,0 +1,58 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.17; + +import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; +import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {SIP7Interface} from "../../../../contracts/trading/seaport/zones/interfaces/SIP7Interface.sol"; + +// solhint-disable func-name-mixedcase + +interface IImmutableSignedZoneV2Harness is ZoneInterface, SIP7Interface { + function exposed_getSupportedSubstandards() external pure returns (uint256[] memory substandards); + + function exposed_deriveSignedOrderHash( + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes calldata context + ) external view returns (bytes32 signedOrderHash); + + function exposed_validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure; + + function exposed_validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256); + + function exposed_validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256); + + function exposed_validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) + external + pure + returns (uint256); + + function exposed_deriveReceivedItemsHash( + ReceivedItem[] calldata receivedItems, + uint256 scalingFactorNumerator, + uint256 scalingFactorDenominator + ) external pure returns (bytes32); + + function exposed_bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) + external + pure + returns (bool); + + function exposed_domainSeparator() external view returns (bytes32); + + function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator); +} + +// solhint-enable func-name-mixedcase diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol index 829fece5..23f9ea2e 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol @@ -1,6 +1,7 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; From d7a3e38c54899ac6d58419b9d83f457076df013d Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 14:44:37 +1000 Subject: [PATCH 61/79] Getting it to compile --- foundry.toml | 1 + ...utableSeaportSignedZoneV2Integration.t.sol | 28 ++++--------------- .../utils/IOperatorAllowlistUpgradeable.t.sol | 7 +++++ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/foundry.toml b/foundry.toml index 9c682eb7..8e6b1d92 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,6 +3,7 @@ src = 'contracts' out = 'foundry-out' # libs = ["lib", "node_modules"] libs = ["lib"] +fs_permissions = [{ access = "read", path = "./foundry-out" }] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index b21a3c3a..41f90dce 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -27,10 +27,10 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { - string constant internal OPERATOR_ALLOWLIST_ARTIFACT = "../../../foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; - string constant internal ERC1155_ARTIFACT = "../../../foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; - string constant internal ERC20_ARTIFACT = "../../../foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; - string constant internal ZONE_ARTIFACT = "../../../foundry-out/ImmutableSignedZoneV2Harness.sol/ImmutableSignedZoneV2Harness.json"; + string constant internal OPERATOR_ALLOWLIST_ARTIFACT = "./foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; + string constant internal ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; + string constant internal ERC20_ARTIFACT = "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; + string constant internal ZONE_ARTIFACT = "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 @@ -47,33 +47,17 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { function setUp() public { // operator allowlist - // OperatorAllowlistUpgradeable operatorAllowlist = new OperatorAllowlistUpgradeable(); IOperatorAllowlistUpgradeable operatorAllowlist = IOperatorAllowlistUpgradeable(deployCode(OPERATOR_ALLOWLIST_ARTIFACT)); + operatorAllowlist.initialize(OWNER, OWNER, OWNER); // tokens - // erc20Token = new ImmutableERC20FixedSupplyNoBurn("TestERC20", "ERC20", 1000, OWNER, OWNER); - erc20Token = IERC20(deployCode(ERC20_ARTIFACT)); - // erc1155Token = new ImmutableERC1155( - // OWNER, - // "TestERC1155", - // "", - // "", - // address(operatorAllowlist), - // ROYALTY_FEE_RECEIVER, - // 100 // 1% - // ); + erc20Token = IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20","ERC20",uint256(1000),OWNER,OWNER))); erc1155Token = IImmutableERC1155(deployCode(ERC1155_ARTIFACT, abi.encode(OWNER,"TestERC1155","","",address(operatorAllowlist),ROYALTY_FEE_RECEIVER,uint96(100)))); vm.prank(OWNER); erc1155Token.grantMinterRole(OWNER); // zone zone = IImmutableSignedZoneV2Harness(deployCode(ZONE_ARTIFACT, abi.encode("MyZoneName","https://www.immutable.com","https://www.immutable.com/docs",OWNER))); - // zone = new ImmutableSignedZoneV2Harness( - // "MyZoneName", - // "https://www.immutable.com", - // "https://www.immutable.com/docs", - // OWNER - // ); vm.prank(OWNER); zone.addSigner(SIGNER); diff --git a/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol b/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol index e16bf606..f521d61b 100644 --- a/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol +++ b/test/trading/seaport/utils/IOperatorAllowlistUpgradeable.t.sol @@ -8,6 +8,13 @@ pragma solidity ^0.8.17; * @notice Required interface of an OperatorAllowlist compliant contract */ interface IOperatorAllowlistUpgradeable { + /** + * @notice Grants `DEFAULT_ADMIN_ROLE` to the supplied `admin` address + * @param _roleAdmin the address to grant `DEFAULT_ADMIN_ROLE` to + * @param _upgradeAdmin the address to grant `UPGRADE_ROLE` to + */ + function initialize(address _roleAdmin, address _upgradeAdmin, address _registerarAdmin) external; + /** * @notice Adds a list of multiple addresses to Allowlist * @param addressTargets the addresses to be added to the allowlist From 72336064f0ccc0f9f1c6a84c232d537eb2573543 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 15:33:07 +1000 Subject: [PATCH 62/79] Test works --- .../seaport/zones/ImmutableSignedZoneV2.sol | 3 +- .../seaport/ImmutableSeaportHarness.t.sol | 5 + ...utableSeaportSignedZoneV2Integration.t.sol | 125 +++++++++++------- .../ImmutableSignedZoneV2TestHelper.t.sol | 4 + 4 files changed, 85 insertions(+), 52 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 3532c39e..eb40fff3 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -1,5 +1,6 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 + // solhint-disable compiler-version pragma solidity ^0.8.17; @@ -12,7 +13,7 @@ import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; +import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; /** * @title ImmutableSignedZoneV2 diff --git a/test/trading/seaport/ImmutableSeaportHarness.t.sol b/test/trading/seaport/ImmutableSeaportHarness.t.sol index 4f29a5b8..661bc61d 100644 --- a/test/trading/seaport/ImmutableSeaportHarness.t.sol +++ b/test/trading/seaport/ImmutableSeaportHarness.t.sol @@ -1,10 +1,13 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; import {ImmutableSeaport} from "../../../contracts/trading/seaport/ImmutableSeaport.sol"; +// solhint-disable func-name-mixedcase + contract ImmutableSeaportHarness is ImmutableSeaport { constructor(address conduitController, address owner) ImmutableSeaport(conduitController, owner) {} @@ -16,3 +19,5 @@ contract ImmutableSeaportHarness is ImmutableSeaport { return _domainSeparator(); } } + +// solhint-enable func-name-mixedcase diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 41f90dce..71a8a52f 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -27,10 +27,15 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { - string constant internal OPERATOR_ALLOWLIST_ARTIFACT = "./foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; - string constant internal ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; - string constant internal ERC20_ARTIFACT = "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; - string constant internal ZONE_ARTIFACT = "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; + // Foundry artifacts allow the test to deploy contracts separately that aren't compatible with + // the solidity version compiler that the test and its dependencies resolve to. + string internal constant OPERATOR_ALLOWLIST_ARTIFACT = + "./foundry-out/OperatorAllowlistUpgradeable.sol/OperatorAllowlistUpgradeable.json"; + string internal constant ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; + string internal constant ERC20_ARTIFACT = + "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; + string internal constant ZONE_ARTIFACT = + "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 @@ -47,17 +52,28 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { function setUp() public { // operator allowlist - IOperatorAllowlistUpgradeable operatorAllowlist = IOperatorAllowlistUpgradeable(deployCode(OPERATOR_ALLOWLIST_ARTIFACT)); + IOperatorAllowlistUpgradeable operatorAllowlist = + IOperatorAllowlistUpgradeable(deployCode(OPERATOR_ALLOWLIST_ARTIFACT)); operatorAllowlist.initialize(OWNER, OWNER, OWNER); // tokens - erc20Token = IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20","ERC20",uint256(1000),OWNER,OWNER))); - erc1155Token = IImmutableERC1155(deployCode(ERC1155_ARTIFACT, abi.encode(OWNER,"TestERC1155","","",address(operatorAllowlist),ROYALTY_FEE_RECEIVER,uint96(100)))); + erc20Token = IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20", "ERC20", uint256(1000), OWNER, OWNER))); + erc1155Token = IImmutableERC1155( + deployCode( + ERC1155_ARTIFACT, + abi.encode(OWNER, "TestERC1155", "", "", address(operatorAllowlist), ROYALTY_FEE_RECEIVER, uint96(100)) + ) + ); vm.prank(OWNER); erc1155Token.grantMinterRole(OWNER); // zone - zone = IImmutableSignedZoneV2Harness(deployCode(ZONE_ARTIFACT, abi.encode("MyZoneName","https://www.immutable.com","https://www.immutable.com/docs",OWNER))); + zone = IImmutableSignedZoneV2Harness( + deployCode( + ZONE_ARTIFACT, + abi.encode("MyZoneName", "https://www.immutable.com", "https://www.immutable.com/docs", OWNER) + ) + ); vm.prank(OWNER); zone.addSigner(SIGNER); @@ -75,6 +91,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { } function test_fulfillAdvancedOrder_withPartialFill() public { + // offer items OfferItem[] memory offerItems = new OfferItem[](1); offerItems[0] = OfferItem({ itemType: ItemType.ERC1155, @@ -84,9 +101,10 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { endAmount: uint256(100) }); - ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + // consideration items + ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); // original item - considerationItems[0] = ConsiderationItem({ + originalConsiderationItems[0] = ConsiderationItem({ itemType: ItemType.ERC20, token: address(erc20Token), identifierOrCriteria: uint256(0), @@ -94,6 +112,9 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { endAmount: uint256(200), recipient: payable(OFFERER) }); + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + considerationItems[0] = originalConsiderationItems[0]; // protocol fee - 2% considerationItems[1] = ConsiderationItem({ itemType: ItemType.ERC20, @@ -122,9 +143,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { recipient: payable(ECOSYSTEM_FEE_RECEIVER) }); - ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); - originalConsiderationItems[0] = considerationItems[0]; - + // order OrderParameters memory orderParameters = OrderParameters({ offerer: OFFERER, zone: address(zone), @@ -139,6 +158,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { totalOriginalConsiderationItems: uint256(1) }); + // order hash bytes32 orderHash = seaport.getOrderHash( OrderComponents({ offerer: orderParameters.offerer, @@ -155,56 +175,58 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { }) ); - bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - + // order signature bytes memory orderSignature; { (, uint256 offererPK) = makeAddrAndKey("offerer"); - (, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); - orderSignature = abi.encodePacked(listingR, listingS); + bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); + (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); + orderSignature = abi.encodePacked(listingR, listingS, listingV); } - ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); - expectedReceivedItems[0] = ReceivedItem({ - itemType: considerationItems[0].itemType, - token: considerationItems[0].token, - identifier: considerationItems[0].identifierOrCriteria, - amount: considerationItems[0].startAmount, - recipient: considerationItems[0].recipient - }); - expectedReceivedItems[1] = ReceivedItem({ - itemType: considerationItems[1].itemType, - token: considerationItems[1].token, - identifier: considerationItems[1].identifierOrCriteria, - amount: considerationItems[1].startAmount, - recipient: considerationItems[1].recipient - }); - expectedReceivedItems[2] = ReceivedItem({ - itemType: considerationItems[2].itemType, - token: considerationItems[2].token, - identifier: considerationItems[2].identifierOrCriteria, - amount: considerationItems[2].startAmount, - recipient: considerationItems[2].recipient - }); - expectedReceivedItems[3] = ReceivedItem({ - itemType: considerationItems[3].itemType, - token: considerationItems[3].token, - identifier: considerationItems[3].identifierOrCriteria, - amount: considerationItems[3].startAmount, - recipient: considerationItems[3].recipient - }); - + // extra data bytes memory extraData; { (, uint256 signerPK) = makeAddrAndKey("signer"); + ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); + expectedReceivedItems[0] = ReceivedItem({ + itemType: considerationItems[0].itemType, + token: considerationItems[0].token, + identifier: considerationItems[0].identifierOrCriteria, + amount: considerationItems[0].startAmount, + recipient: considerationItems[0].recipient + }); + expectedReceivedItems[1] = ReceivedItem({ + itemType: considerationItems[1].itemType, + token: considerationItems[1].token, + identifier: considerationItems[1].identifierOrCriteria, + amount: considerationItems[1].startAmount, + recipient: considerationItems[1].recipient + }); + expectedReceivedItems[2] = ReceivedItem({ + itemType: considerationItems[2].itemType, + token: considerationItems[2].token, + identifier: considerationItems[2].identifierOrCriteria, + amount: considerationItems[2].startAmount, + recipient: considerationItems[2].recipient + }); + expectedReceivedItems[3] = ReceivedItem({ + itemType: considerationItems[3].itemType, + token: considerationItems[3].token, + identifier: considerationItems[3].identifierOrCriteria, + amount: considerationItems[3].startAmount, + recipient: considerationItems[3].recipient + }); bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); - bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); + bytes32 eip712SignedOrderHash = + zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData = abi.encodePacked(hex"00", FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); } + // advanced order AdvancedOrder memory advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), @@ -221,7 +243,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { + considerationItems[3].startAmount ); vm.prank(OWNER); - erc1155Token.safeMint(FULFILLER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); + erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); // approvals vm.prank(OFFERER); @@ -233,7 +255,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { + considerationItems[3].startAmount ); - // fulfill + // fulfillment + vm.prank(FULFILLER); seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); } } diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol index ccf3246b..05045675 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -1,18 +1,22 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version pragma solidity ^0.8.17; +// solhint-disable-next-line no-global-import import "forge-std/Test.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; abstract contract ImmutableSignedZoneV2TestHelper is Test { + // solhint-disable private-vars-leading-underscore address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 + // solhint-enable private-vars-leading-underscore function _newZone() internal returns (ImmutableSignedZoneV2) { return new ImmutableSignedZoneV2( From 45652128f0c171bcae36d15f9660cb50d5172e22 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 16:33:11 +1000 Subject: [PATCH 63/79] partial fill test --- ...utableSeaportSignedZoneV2Integration.t.sol | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 71a8a52f..83c37444 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -41,7 +41,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a - address internal immutable PROTOCOL_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); + address internal immutable PROTOCOL_FEE_RECEIVER = makeAddr("protocol_fee_receiver"); address internal immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); address internal immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); @@ -57,7 +57,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { operatorAllowlist.initialize(OWNER, OWNER, OWNER); // tokens - erc20Token = IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20", "ERC20", uint256(1000), OWNER, OWNER))); + erc20Token = + IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20", "ERC20", type(uint256).max, OWNER, OWNER))); erc1155Token = IImmutableERC1155( deployCode( ERC1155_ARTIFACT, @@ -108,8 +109,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { itemType: ItemType.ERC20, token: address(erc20Token), identifierOrCriteria: uint256(0), - startAmount: uint256(200), - endAmount: uint256(200), + startAmount: uint256(200_000_000_000_000_000_000), // 200^18 + endAmount: uint256(200_000_000_000_000_000_000), // 200^18 recipient: payable(OFFERER) }); @@ -120,8 +121,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { itemType: ItemType.ERC20, token: address(erc20Token), identifierOrCriteria: uint256(0), - startAmount: uint256(4), - endAmount: uint256(4), + startAmount: uint256(4_000_000_000_000_000_000), + endAmount: uint256(4_000_000_000_000_000_000), recipient: payable(PROTOCOL_FEE_RECEIVER) }); // royalty fee - 1% @@ -129,8 +130,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { itemType: ItemType.ERC20, token: address(erc20Token), identifierOrCriteria: uint256(0), - startAmount: uint256(2), - endAmount: uint256(2), + startAmount: uint256(2_000_000_000_000_000_000), + endAmount: uint256(2_000_000_000_000_000_000), recipient: payable(ROYALTY_FEE_RECEIVER) }); // ecosystem fee - 3% @@ -138,8 +139,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { itemType: ItemType.ERC20, token: address(erc20Token), identifierOrCriteria: uint256(0), - startAmount: uint256(6), - endAmount: uint256(6), + startAmount: uint256(6_000_000_000_000_000_000), + endAmount: uint256(6_000_000_000_000_000_000), recipient: payable(ECOSYSTEM_FEE_RECEIVER) }); @@ -230,7 +231,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { AdvancedOrder memory advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), - denominator: uint120(1), + denominator: uint120(100), signature: orderSignature, extraData: extraData }); @@ -239,8 +240,10 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { vm.prank(OWNER); erc20Token.transfer( FULFILLER, - considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount - + considerationItems[3].startAmount + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) / 100 ); vm.prank(OWNER); erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); @@ -249,15 +252,24 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { vm.prank(OFFERER); erc1155Token.setApprovalForAll(address(seaport), true); vm.prank(FULFILLER); - erc20Token.approve( - address(seaport), - considerationItems[0].startAmount + considerationItems[1].startAmount + considerationItems[2].startAmount - + considerationItems[3].startAmount - ); + erc20Token.approve(address(seaport), type(uint256).max); // fulfillment vm.prank(FULFILLER); seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + + // assertions + assertEq( + erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 99 / 100 + ); + assertEq( + erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 1 / 100 + ); + assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount / 100); + assertEq(erc20Token.balanceOf(FULFILLER), 0); + assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount / 100); + assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount / 100); + assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount / 100); } } From 7660043f4e7124ad5f0b2444d0feabfb387d4cf2 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Mon, 8 Apr 2024 17:22:49 +1000 Subject: [PATCH 64/79] Add more partial fill tests --- ...utableSeaportSignedZoneV2Integration.t.sol | 412 +++++++++++++++++- 1 file changed, 408 insertions(+), 4 deletions(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 83c37444..e047bf2e 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -37,10 +37,11 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { string internal constant ZONE_ARTIFACT = "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; - address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 - address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 - address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 - address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a + address internal immutable OWNER = makeAddr("owner"); + address internal immutable SIGNER = makeAddr("signer"); + address internal immutable FULFILLER = makeAddr("fulfiller"); + address internal immutable FULFILLER_TWO = makeAddr("fulfiller_two"); + address internal immutable OFFERER = makeAddr("offerer"); address internal immutable PROTOCOL_FEE_RECEIVER = makeAddr("protocol_fee_receiver"); address internal immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); address internal immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); @@ -271,6 +272,409 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount / 100); assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount / 100); } + + function test_fulfillAdvancedOrder_withMultiplePartialFills() public { + // offer items + OfferItem[] memory offerItems = new OfferItem[](1); + offerItems[0] = OfferItem({ + itemType: ItemType.ERC1155, + token: address(erc1155Token), + identifierOrCriteria: uint256(50), + startAmount: uint256(100), + endAmount: uint256(100) + }); + + // consideration items + ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); + // original item + originalConsiderationItems[0] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(200_000_000_000_000_000_000), // 200^18 + endAmount: uint256(200_000_000_000_000_000_000), // 200^18 + recipient: payable(OFFERER) + }); + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + considerationItems[0] = originalConsiderationItems[0]; + // protocol fee - 2% + considerationItems[1] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(4_000_000_000_000_000_000), + endAmount: uint256(4_000_000_000_000_000_000), + recipient: payable(PROTOCOL_FEE_RECEIVER) + }); + // royalty fee - 1% + considerationItems[2] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(2_000_000_000_000_000_000), + endAmount: uint256(2_000_000_000_000_000_000), + recipient: payable(ROYALTY_FEE_RECEIVER) + }); + // ecosystem fee - 3% + considerationItems[3] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(6_000_000_000_000_000_000), + endAmount: uint256(6_000_000_000_000_000_000), + recipient: payable(ECOSYSTEM_FEE_RECEIVER) + }); + + // order + OrderParameters memory orderParameters = OrderParameters({ + offerer: OFFERER, + zone: address(zone), + offer: offerItems, + consideration: considerationItems, + orderType: OrderType.PARTIAL_RESTRICTED, + startTime: uint256(0), + endTime: uint256(5000), + zoneHash: bytes32(0), + salt: uint256(123), + conduitKey: bytes32(0), + totalOriginalConsiderationItems: uint256(1) + }); + + // order hash + bytes32 orderHash = seaport.getOrderHash( + OrderComponents({ + offerer: orderParameters.offerer, + zone: orderParameters.zone, + offer: orderParameters.offer, + consideration: originalConsiderationItems, + orderType: orderParameters.orderType, + startTime: orderParameters.startTime, + endTime: orderParameters.endTime, + zoneHash: orderParameters.zoneHash, + salt: orderParameters.salt, + conduitKey: orderParameters.conduitKey, + counter: seaport.getCounter(orderParameters.offerer) + }) + ); + + // order signature + bytes memory orderSignature; + { + (, uint256 offererPK) = makeAddrAndKey("offerer"); + bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); + (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); + orderSignature = abi.encodePacked(listingR, listingS, listingV); + } + + // extra data + bytes memory extraData; + { + (, uint256 signerPK) = makeAddrAndKey("signer"); + ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); + expectedReceivedItems[0] = ReceivedItem({ + itemType: considerationItems[0].itemType, + token: considerationItems[0].token, + identifier: considerationItems[0].identifierOrCriteria, + amount: considerationItems[0].startAmount, + recipient: considerationItems[0].recipient + }); + expectedReceivedItems[1] = ReceivedItem({ + itemType: considerationItems[1].itemType, + token: considerationItems[1].token, + identifier: considerationItems[1].identifierOrCriteria, + amount: considerationItems[1].startAmount, + recipient: considerationItems[1].recipient + }); + expectedReceivedItems[2] = ReceivedItem({ + itemType: considerationItems[2].itemType, + token: considerationItems[2].token, + identifier: considerationItems[2].identifierOrCriteria, + amount: considerationItems[2].startAmount, + recipient: considerationItems[2].recipient + }); + expectedReceivedItems[3] = ReceivedItem({ + itemType: considerationItems[3].itemType, + token: considerationItems[3].token, + identifier: considerationItems[3].identifierOrCriteria, + amount: considerationItems[3].startAmount, + recipient: considerationItems[3].recipient + }); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = + zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + } + + // advanced orders + AdvancedOrder memory advancedOrder = AdvancedOrder({ + parameters: orderParameters, + numerator: uint120(1), + denominator: uint120(100), + signature: orderSignature, + extraData: extraData + }); + + // mints + vm.prank(OWNER); + erc20Token.transfer( + FULFILLER, + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) * 2 / 100 + ); + vm.prank(OWNER); + erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); + + // approvals + vm.prank(OFFERER); + erc1155Token.setApprovalForAll(address(seaport), true); + vm.prank(FULFILLER); + erc20Token.approve(address(seaport), type(uint256).max); + + // fulfill twice + vm.prank(FULFILLER); + seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + vm.prank(FULFILLER); + seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + + // assertions + assertEq( + erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 98 / 100 + ); + assertEq( + erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount * 2 / 100 + ); + assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount * 2 / 100); + assertEq(erc20Token.balanceOf(FULFILLER), 0); + assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount * 2 / 100); + assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount * 2 / 100); + assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount * 2 / 100); + } + + function test_fulfillAdvancedOrder_withOverfilling() public { + // offer items + OfferItem[] memory offerItems = new OfferItem[](1); + offerItems[0] = OfferItem({ + itemType: ItemType.ERC1155, + token: address(erc1155Token), + identifierOrCriteria: uint256(50), + startAmount: uint256(100), + endAmount: uint256(100) + }); + + // consideration items + ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); + // original item + originalConsiderationItems[0] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(200_000_000_000_000_000_000), // 200^18 + endAmount: uint256(200_000_000_000_000_000_000), // 200^18 + recipient: payable(OFFERER) + }); + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + considerationItems[0] = originalConsiderationItems[0]; + // protocol fee - 2% + considerationItems[1] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(4_000_000_000_000_000_000), + endAmount: uint256(4_000_000_000_000_000_000), + recipient: payable(PROTOCOL_FEE_RECEIVER) + }); + // royalty fee - 1% + considerationItems[2] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(2_000_000_000_000_000_000), + endAmount: uint256(2_000_000_000_000_000_000), + recipient: payable(ROYALTY_FEE_RECEIVER) + }); + // ecosystem fee - 3% + considerationItems[3] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(6_000_000_000_000_000_000), + endAmount: uint256(6_000_000_000_000_000_000), + recipient: payable(ECOSYSTEM_FEE_RECEIVER) + }); + + // order + OrderParameters memory orderParameters = OrderParameters({ + offerer: OFFERER, + zone: address(zone), + offer: offerItems, + consideration: considerationItems, + orderType: OrderType.PARTIAL_RESTRICTED, + startTime: uint256(0), + endTime: uint256(5000), + zoneHash: bytes32(0), + salt: uint256(123), + conduitKey: bytes32(0), + totalOriginalConsiderationItems: uint256(1) + }); + + // order hash + bytes32 orderHash = seaport.getOrderHash( + OrderComponents({ + offerer: orderParameters.offerer, + zone: orderParameters.zone, + offer: orderParameters.offer, + consideration: originalConsiderationItems, + orderType: orderParameters.orderType, + startTime: orderParameters.startTime, + endTime: orderParameters.endTime, + zoneHash: orderParameters.zoneHash, + salt: orderParameters.salt, + conduitKey: orderParameters.conduitKey, + counter: seaport.getCounter(orderParameters.offerer) + }) + ); + + // order signature + bytes memory orderSignature; + { + (, uint256 offererPK) = makeAddrAndKey("offerer"); + bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); + (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); + orderSignature = abi.encodePacked(listingR, listingS, listingV); + } + + // substandard 6 data expected received items + ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); + expectedReceivedItems[0] = ReceivedItem({ + itemType: considerationItems[0].itemType, + token: considerationItems[0].token, + identifier: considerationItems[0].identifierOrCriteria, + amount: considerationItems[0].startAmount, + recipient: considerationItems[0].recipient + }); + expectedReceivedItems[1] = ReceivedItem({ + itemType: considerationItems[1].itemType, + token: considerationItems[1].token, + identifier: considerationItems[1].identifierOrCriteria, + amount: considerationItems[1].startAmount, + recipient: considerationItems[1].recipient + }); + expectedReceivedItems[2] = ReceivedItem({ + itemType: considerationItems[2].itemType, + token: considerationItems[2].token, + identifier: considerationItems[2].identifierOrCriteria, + amount: considerationItems[2].startAmount, + recipient: considerationItems[2].recipient + }); + expectedReceivedItems[3] = ReceivedItem({ + itemType: considerationItems[3].itemType, + token: considerationItems[3].token, + identifier: considerationItems[3].identifierOrCriteria, + amount: considerationItems[3].startAmount, + recipient: considerationItems[3].recipient + }); + + // extra data + bytes memory extraData1; + bytes memory extraData2; + { + (, uint256 signerPK) = makeAddrAndKey("signer"); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = + zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + extraData1 = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + } + { + (, uint256 signerPK) = makeAddrAndKey("signer"); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = + zone.exposed_deriveSignedOrderHash(FULFILLER_TWO, uint64(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + extraData2 = abi.encodePacked(bytes1(0), FULFILLER_TWO, uint64(4000), signedOrderR, signedOrderS, context); + } + + // advanced orders + AdvancedOrder memory advancedOrder1 = AdvancedOrder({ + parameters: orderParameters, + numerator: uint120(50), + denominator: uint120(100), + signature: orderSignature, + extraData: extraData1 + }); + + AdvancedOrder memory advancedOrder2 = AdvancedOrder({ + parameters: orderParameters, + numerator: uint120(1), + denominator: uint120(1), + signature: orderSignature, + extraData: extraData2 + }); + + // mints + vm.prank(OWNER); + erc20Token.transfer( + FULFILLER, + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) / 2 + ); + vm.prank(OWNER); + erc20Token.transfer( + FULFILLER_TWO, + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) + ); + vm.prank(OWNER); + erc1155Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria, offerItems[0].startAmount, new bytes(0)); + + // approvals + vm.prank(OFFERER); + erc1155Token.setApprovalForAll(address(seaport), true); + vm.prank(FULFILLER); + erc20Token.approve(address(seaport), type(uint256).max); + vm.prank(FULFILLER_TWO); + erc20Token.approve(address(seaport), type(uint256).max); + + // fulfill twice + vm.prank(FULFILLER); + seaport.fulfillAdvancedOrder(advancedOrder1, new CriteriaResolver[](0), bytes32(0), FULFILLER); + vm.prank(FULFILLER_TWO); + seaport.fulfillAdvancedOrder(advancedOrder2, new CriteriaResolver[](0), bytes32(0), FULFILLER_TWO); + + // assertions + assertEq(erc1155Token.balanceOf(OFFERER, offerItems[0].identifierOrCriteria), 0); + assertEq(erc1155Token.balanceOf(FULFILLER, offerItems[0].identifierOrCriteria), offerItems[0].startAmount / 2); + assertEq( + erc1155Token.balanceOf(FULFILLER_TWO, offerItems[0].identifierOrCriteria), offerItems[0].startAmount / 2 + ); + assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount); + assertEq(erc20Token.balanceOf(FULFILLER), 0); + assertEq( + erc20Token.balanceOf(FULFILLER_TWO), + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) / 2 + ); + assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount); + assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount); + assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount); + } } // solhint-enable func-name-mixedcase, private-vars-leading-underscore From 4ed227f27baf4dd87b1755fc792ac6fb5beed6c0 Mon Sep 17 00:00:00 2001 From: Naveen <116692862+naveen-imtb@users.noreply.github.com> Date: Tue, 9 Apr 2024 05:44:06 +1000 Subject: [PATCH 65/79] TD-1326: chore: add integration test for complete fulfilment with ERC721 --- ...utableSeaportSignedZoneV2Integration.t.sol | 191 ++++++++++++++++++ .../seaport/utils/IImmutableERC721.t.sol | 23 +++ 2 files changed, 214 insertions(+) create mode 100644 test/trading/seaport/utils/IImmutableERC721.t.sol diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index e047bf2e..9049a53b 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -10,6 +10,7 @@ import {IImmutableSignedZoneV2Harness} from "./zones/IImmutableSignedZoneV2Harne import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; import {IImmutableERC1155} from "./utils/IImmutableERC1155.t.sol"; +import {IImmutableERC721} from "./utils/IImmutableERC721.t.sol"; import {IOperatorAllowlistUpgradeable} from "./utils/IOperatorAllowlistUpgradeable.t.sol"; import { AdvancedOrder, @@ -22,6 +23,7 @@ import { } from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore @@ -34,6 +36,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { string internal constant ERC1155_ARTIFACT = "./foundry-out/ImmutableERC1155.sol/ImmutableERC1155.json"; string internal constant ERC20_ARTIFACT = "./foundry-out/ImmutableERC20FixedSupplyNoBurn.sol/ImmutableERC20FixedSupplyNoBurn.json"; + string internal constant ERC721_ARTIFACT = "./foundry-out/ImmutableERC721.sol/ImmutableERC721.json"; string internal constant ZONE_ARTIFACT = "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; @@ -50,6 +53,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { IImmutableSignedZoneV2Harness internal zone; IERC20 internal erc20Token; IImmutableERC1155 internal erc1155Token; + IImmutableERC721 internal erc721Token; function setUp() public { // operator allowlist @@ -60,6 +64,16 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // tokens erc20Token = IERC20(deployCode(ERC20_ARTIFACT, abi.encode("TestERC20", "ERC20", type(uint256).max, OWNER, OWNER))); + erc721Token = IImmutableERC721( + deployCode( + ERC721_ARTIFACT, + abi.encode( + OWNER, "TestERC721", "ERC721", "", "", address(operatorAllowlist), ROYALTY_FEE_RECEIVER, uint96(100) + ) + ) + ); + vm.prank(OWNER); + erc721Token.grantMinterRole(OWNER); erc1155Token = IImmutableERC1155( deployCode( ERC1155_ARTIFACT, @@ -92,6 +106,183 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { operatorAllowlist.addAddressesToAllowlist(allowlistAddress); } + function test_fulfillAdvancedOrder_withCompleteFulfilment() public { + // offer items + OfferItem[] memory offerItems = new OfferItem[](1); + offerItems[0] = OfferItem({ + itemType: ItemType.ERC721, + token: address(erc721Token), + identifierOrCriteria: uint256(50), + startAmount: uint256(1), + endAmount: uint256(1) + }); + + // consideration items + ConsiderationItem[] memory originalConsiderationItems = new ConsiderationItem[](1); + // original item + originalConsiderationItems[0] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(200_000_000_000_000_000_000), // 200^18 + endAmount: uint256(200_000_000_000_000_000_000), // 200^18 + recipient: payable(OFFERER) + }); + + ConsiderationItem[] memory considerationItems = new ConsiderationItem[](4); + considerationItems[0] = originalConsiderationItems[0]; + // protocol fee - 2% + considerationItems[1] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(4_000_000_000_000_000_000), + endAmount: uint256(4_000_000_000_000_000_000), + recipient: payable(PROTOCOL_FEE_RECEIVER) + }); + // royalty fee - 1% + considerationItems[2] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(2_000_000_000_000_000_000), + endAmount: uint256(2_000_000_000_000_000_000), + recipient: payable(ROYALTY_FEE_RECEIVER) + }); + // ecosystem fee - 3% + considerationItems[3] = ConsiderationItem({ + itemType: ItemType.ERC20, + token: address(erc20Token), + identifierOrCriteria: uint256(0), + startAmount: uint256(6_000_000_000_000_000_000), + endAmount: uint256(6_000_000_000_000_000_000), + recipient: payable(ECOSYSTEM_FEE_RECEIVER) + }); + + // order + OrderParameters memory orderParameters = OrderParameters({ + offerer: OFFERER, + zone: address(zone), + offer: offerItems, + consideration: considerationItems, + orderType: OrderType.FULL_RESTRICTED, + startTime: uint256(0), + endTime: uint256(5000), + zoneHash: bytes32(0), + salt: uint256(123), + conduitKey: bytes32(0), + totalOriginalConsiderationItems: uint256(1) + }); + + // order hash + bytes32 orderHash = seaport.getOrderHash( + OrderComponents({ + offerer: orderParameters.offerer, + zone: orderParameters.zone, + offer: orderParameters.offer, + consideration: originalConsiderationItems, + orderType: orderParameters.orderType, + startTime: orderParameters.startTime, + endTime: orderParameters.endTime, + zoneHash: orderParameters.zoneHash, + salt: orderParameters.salt, + conduitKey: orderParameters.conduitKey, + counter: seaport.getCounter(orderParameters.offerer) + }) + ); + + // order signature + bytes memory orderSignature; + { + (, uint256 offererPK) = makeAddrAndKey("offerer"); + bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); + (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); + orderSignature = abi.encodePacked(listingR, listingS, listingV); + } + + // extra data + bytes memory extraData; + { + (, uint256 signerPK) = makeAddrAndKey("signer"); + ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); + expectedReceivedItems[0] = ReceivedItem({ + itemType: considerationItems[0].itemType, + token: considerationItems[0].token, + identifier: considerationItems[0].identifierOrCriteria, + amount: considerationItems[0].startAmount, + recipient: considerationItems[0].recipient + }); + expectedReceivedItems[1] = ReceivedItem({ + itemType: considerationItems[1].itemType, + token: considerationItems[1].token, + identifier: considerationItems[1].identifierOrCriteria, + amount: considerationItems[1].startAmount, + recipient: considerationItems[1].recipient + }); + expectedReceivedItems[2] = ReceivedItem({ + itemType: considerationItems[2].itemType, + token: considerationItems[2].token, + identifier: considerationItems[2].identifierOrCriteria, + amount: considerationItems[2].startAmount, + recipient: considerationItems[2].recipient + }); + expectedReceivedItems[3] = ReceivedItem({ + itemType: considerationItems[3].itemType, + token: considerationItems[3].token, + identifier: considerationItems[3].identifierOrCriteria, + amount: considerationItems[3].startAmount, + recipient: considerationItems[3].recipient + }); + bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); + bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); + bytes32 eip712SignedOrderHash = + zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); + bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); + (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); + extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + } + + // advanced order + AdvancedOrder memory advancedOrder = AdvancedOrder({ + parameters: orderParameters, + numerator: uint120(1), + denominator: uint120(1), + signature: orderSignature, + extraData: extraData + }); + + // mints + vm.prank(OWNER); + erc20Token.transfer( + FULFILLER, + ( + considerationItems[0].startAmount + considerationItems[1].startAmount + + considerationItems[2].startAmount + considerationItems[3].startAmount + ) + ); + vm.prank(OWNER); + erc721Token.safeMint(OFFERER, offerItems[0].identifierOrCriteria); + + // approvals + vm.prank(OFFERER); + erc721Token.setApprovalForAll(address(seaport), true); + vm.prank(FULFILLER); + erc20Token.approve(address(seaport), type(uint256).max); + + // fulfillment + vm.prank(FULFILLER); + seaport.fulfillAdvancedOrder(advancedOrder, new CriteriaResolver[](0), bytes32(0), FULFILLER); + + // assertions + assertEq(erc721Token.balanceOf(OFFERER), 0); + assertEq(erc721Token.balanceOf(FULFILLER), offerItems[0].startAmount); + assertEq(erc20Token.balanceOf(OFFERER), considerationItems[0].startAmount); + assertEq(erc20Token.balanceOf(FULFILLER), 0); + assertEq(erc20Token.balanceOf(PROTOCOL_FEE_RECEIVER), considerationItems[1].startAmount); + assertEq(erc20Token.balanceOf(ROYALTY_FEE_RECEIVER), considerationItems[2].startAmount); + assertEq(erc20Token.balanceOf(ECOSYSTEM_FEE_RECEIVER), considerationItems[3].startAmount); + } + function test_fulfillAdvancedOrder_withPartialFill() public { // offer items OfferItem[] memory offerItems = new OfferItem[](1); diff --git a/test/trading/seaport/utils/IImmutableERC721.t.sol b/test/trading/seaport/utils/IImmutableERC721.t.sol new file mode 100644 index 00000000..c03f9a12 --- /dev/null +++ b/test/trading/seaport/utils/IImmutableERC721.t.sol @@ -0,0 +1,23 @@ +// Copyright Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache 2.0 + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.17; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + +/** + * @notice Interface for Immutable's ERC721 + */ +interface IImmutableERC721 is IERC721 { + /** @notice Allows minter to mint `tokenID` to `to` + * @param to the address to mint the token to + * @param tokenID the ID of the token to mint + */ + function safeMint(address to, uint256 tokenID) external; + + /** @notice Allows admin grant `user` `MINTER` role + * @param user The address to grant the `MINTER` role to + */ + function grantMinterRole(address user) external; +} From bdff9a889c1e191ef7541353c577a104be63ddc8 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 10:44:36 +1000 Subject: [PATCH 66/79] Remove unused import --- .../seaport/ImmutableSeaportSignedZoneV2Integration.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 9049a53b..e08fb8e7 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -23,7 +23,6 @@ import { } from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType, OrderType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore From 58b85d98da0ce69eb49b0e6ae3a06ef3a74b90f8 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 10:48:28 +1000 Subject: [PATCH 67/79] solhint fix --- contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol | 2 +- .../trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index eb40fff3..14cb651a 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -1,7 +1,7 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -// solhint-disable compiler-version +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol index 05045675..03e99179 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -1,7 +1,7 @@ // Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -// solhint-disable compiler-version +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; // solhint-disable-next-line no-global-import From 489731034eaa23f6236cf1b99cc2e2feef186b64 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 11:37:17 +1000 Subject: [PATCH 68/79] Add signing helper contract --- ...utableSeaportSignedZoneV2Integration.t.sol | 95 ++++++++++++------- .../seaport/utils/SigningTestHelper.t.sol | 30 ++++++ .../ImmutableSignedZoneV2TestHelper.t.sol | 22 +++-- 3 files changed, 103 insertions(+), 44 deletions(-) create mode 100644 test/trading/seaport/utils/SigningTestHelper.t.sol diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index e08fb8e7..5cc15cc5 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -9,6 +9,7 @@ import "forge-std/Test.sol"; import {IImmutableSignedZoneV2Harness} from "./zones/IImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; +import {SigningTestHelper} from "./utils/SigningTestHelper.t.sol"; import {IImmutableERC1155} from "./utils/IImmutableERC1155.t.sol"; import {IImmutableERC721} from "./utils/IImmutableERC721.t.sol"; import {IOperatorAllowlistUpgradeable} from "./utils/IOperatorAllowlistUpgradeable.t.sol"; @@ -27,7 +28,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase, private-vars-leading-underscore -contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { +contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper { // Foundry artifacts allow the test to deploy contracts separately that aren't compatible with // the solidity version compiler that the test and its dependencies resolve to. string internal constant OPERATOR_ALLOWLIST_ARTIFACT = @@ -40,10 +41,12 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { "./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json"; address internal immutable OWNER = makeAddr("owner"); - address internal immutable SIGNER = makeAddr("signer"); + address internal immutable SIGNER; + uint256 internal immutable SIGNER_PRIVATE_KEY; address internal immutable FULFILLER = makeAddr("fulfiller"); address internal immutable FULFILLER_TWO = makeAddr("fulfiller_two"); - address internal immutable OFFERER = makeAddr("offerer"); + address internal immutable OFFERER; + uint256 internal immutable OFFERER_PRIVATE_KEY; address internal immutable PROTOCOL_FEE_RECEIVER = makeAddr("protocol_fee_receiver"); address internal immutable ROYALTY_FEE_RECEIVER = makeAddr("royalty_fee_receiver"); address internal immutable ECOSYSTEM_FEE_RECEIVER = makeAddr("ecosystem_fee_receiver"); @@ -54,6 +57,11 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { IImmutableERC1155 internal erc1155Token; IImmutableERC721 internal erc721Token; + constructor() { + (SIGNER, SIGNER_PRIVATE_KEY) = makeAddrAndKey("signer"); + (OFFERER, OFFERER_PRIVATE_KEY) = makeAddrAndKey("offerer"); + } + function setUp() public { // operator allowlist IOperatorAllowlistUpgradeable operatorAllowlist = @@ -193,16 +201,13 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // order signature bytes memory orderSignature; { - (, uint256 offererPK) = makeAddrAndKey("offerer"); bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); - orderSignature = abi.encodePacked(listingR, listingS, listingV); + orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); } // extra data bytes memory extraData; { - (, uint256 signerPK) = makeAddrAndKey("signer"); ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); expectedReceivedItems[0] = ReceivedItem({ itemType: considerationItems[0].itemType, @@ -236,9 +241,15 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + extraData = abi.encodePacked( + bytes1(0), + FULFILLER, + uint64(4000), + _signCompact( + SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) + ), + context + ); } // advanced order @@ -370,16 +381,13 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // order signature bytes memory orderSignature; { - (, uint256 offererPK) = makeAddrAndKey("offerer"); bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); - orderSignature = abi.encodePacked(listingR, listingS, listingV); + orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); } // extra data bytes memory extraData; { - (, uint256 signerPK) = makeAddrAndKey("signer"); ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); expectedReceivedItems[0] = ReceivedItem({ itemType: considerationItems[0].itemType, @@ -413,9 +421,15 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + extraData = abi.encodePacked( + bytes1(0), + FULFILLER, + uint64(4000), + _signCompact( + SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) + ), + context + ); } // advanced order @@ -551,16 +565,13 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // order signature bytes memory orderSignature; { - (, uint256 offererPK) = makeAddrAndKey("offerer"); bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); - orderSignature = abi.encodePacked(listingR, listingS, listingV); + orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); } // extra data bytes memory extraData; { - (, uint256 signerPK) = makeAddrAndKey("signer"); ReceivedItem[] memory expectedReceivedItems = new ReceivedItem[](4); expectedReceivedItems[0] = ReceivedItem({ itemType: considerationItems[0].itemType, @@ -594,9 +605,15 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + extraData = abi.encodePacked( + bytes1(0), + FULFILLER, + uint64(4000), + _signCompact( + SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) + ), + context + ); } // advanced orders @@ -734,10 +751,8 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { // order signature bytes memory orderSignature; { - (, uint256 offererPK) = makeAddrAndKey("offerer"); bytes32 orderDigest = seaport.exposed_deriveEIP712Digest(seaport.exposed_domainSeparator(), orderHash); - (uint8 listingV, bytes32 listingR, bytes32 listingS) = vm.sign(offererPK, orderDigest); - orderSignature = abi.encodePacked(listingR, listingS, listingV); + orderSignature = _sign(OFFERER_PRIVATE_KEY, orderDigest); } // substandard 6 data expected received items @@ -775,24 +790,34 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test { bytes memory extraData1; bytes memory extraData2; { - (, uint256 signerPK) = makeAddrAndKey("signer"); bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, uint64(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData1 = abi.encodePacked(bytes1(0), FULFILLER, uint64(4000), signedOrderR, signedOrderS, context); + extraData1 = abi.encodePacked( + bytes1(0), + FULFILLER, + uint64(4000), + _signCompact( + SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) + ), + context + ); } { - (, uint256 signerPK) = makeAddrAndKey("signer"); bytes32 substandard6Data = zone.exposed_deriveReceivedItemsHash(expectedReceivedItems, 1, 1); bytes memory context = abi.encodePacked(bytes1(0x06), offerItems[0].startAmount, substandard6Data); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER_TWO, uint64(4000), orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 signedOrderR, bytes32 signedOrderS) = vm.sign(signerPK, signatureDigest); - extraData2 = abi.encodePacked(bytes1(0), FULFILLER_TWO, uint64(4000), signedOrderR, signedOrderS, context); + extraData2 = abi.encodePacked( + bytes1(0), + FULFILLER_TWO, + uint64(4000), + _signCompact( + SIGNER_PRIVATE_KEY, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash) + ), + context + ); } // advanced orders diff --git a/test/trading/seaport/utils/SigningTestHelper.t.sol b/test/trading/seaport/utils/SigningTestHelper.t.sol new file mode 100644 index 00000000..0f0aa25a --- /dev/null +++ b/test/trading/seaport/utils/SigningTestHelper.t.sol @@ -0,0 +1,30 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.17; + +// solhint-disable-next-line no-global-import +import "forge-std/Test.sol"; + +abstract contract SigningTestHelper is Test { + function _sign( + uint256 signerPrivateKey, + bytes32 signatureDigest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); + return abi.encodePacked(r, s, v); + } + + function _signCompact( + uint256 signerPrivateKey, + bytes32 signatureDigest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); + if (v != 27) { + // then left-most bit of s has to be flipped to 1. + s = s | bytes32(uint256(1) << 255); + } + return abi.encodePacked(r, s); + } +} diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol index 03e99179..a0314f59 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol @@ -9,13 +9,14 @@ import "forge-std/Test.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; -abstract contract ImmutableSignedZoneV2TestHelper is Test { +abstract contract ImmutableSignedZoneV2TestHelper is Test, SigningTestHelper { // solhint-disable private-vars-leading-underscore - address internal immutable OWNER = makeAddr("owner"); // 0x7c8999dC9a822c1f0Df42023113EDB4FDd543266 - address internal immutable FULFILLER = makeAddr("fulfiller"); // 0x71458637cD221877830A21F543E8b731e93C3627 - address internal immutable OFFERER = makeAddr("offerer"); // 0xD4A3ED913c988269BbB6caeCBEC568063B43435a - address internal immutable SIGNER = makeAddr("signer"); // 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2 + address internal immutable OWNER = makeAddr("owner"); + address internal immutable FULFILLER = makeAddr("fulfiller"); + address internal immutable OFFERER = makeAddr("offerer"); + address internal immutable SIGNER = makeAddr("signer"); // solhint-enable private-vars-leading-underscore function _newZone() internal returns (ImmutableSignedZoneV2) { @@ -44,10 +45,13 @@ abstract contract ImmutableSignedZoneV2TestHelper is Test { ) internal returns (bytes memory) { (, uint256 signerPK) = makeAddrAndKey("signer"); bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, expiration, orderHash, context); - bytes32 signatureDigest = ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash); - (, bytes32 r, bytes32 s) = vm.sign(signerPK, signatureDigest); - bytes memory extraData = abi.encodePacked(hex"00", FULFILLER, expiration, r, s, context); + bytes memory extraData = abi.encodePacked( + bytes1(0), + FULFILLER, + expiration, + _signCompact(signerPK, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash)), + context + ); return extraData; } - } From b56837c56be55927c377f8aa245b014b205da949 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 11:58:21 +1000 Subject: [PATCH 69/79] Refactor helper functions --- .../seaport/ImmutableSeaportHarness.t.sol | 6 +- .../seaport/utils/SigningTestHelper.t.sol | 10 +- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 171 ++++++++++++------ .../ImmutableSignedZoneV2TestHelper.t.sol | 57 ------ 4 files changed, 121 insertions(+), 123 deletions(-) delete mode 100644 test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol diff --git a/test/trading/seaport/ImmutableSeaportHarness.t.sol b/test/trading/seaport/ImmutableSeaportHarness.t.sol index 661bc61d..15de0c88 100644 --- a/test/trading/seaport/ImmutableSeaportHarness.t.sol +++ b/test/trading/seaport/ImmutableSeaportHarness.t.sol @@ -11,7 +11,11 @@ import {ImmutableSeaport} from "../../../contracts/trading/seaport/ImmutableSeap contract ImmutableSeaportHarness is ImmutableSeaport { constructor(address conduitController, address owner) ImmutableSeaport(conduitController, owner) {} - function exposed_deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) external pure returns (bytes32 value) { + function exposed_deriveEIP712Digest(bytes32 domainSeparator, bytes32 orderHash) + external + pure + returns (bytes32 value) + { return _deriveEIP712Digest(domainSeparator, orderHash); } diff --git a/test/trading/seaport/utils/SigningTestHelper.t.sol b/test/trading/seaport/utils/SigningTestHelper.t.sol index 0f0aa25a..33f56497 100644 --- a/test/trading/seaport/utils/SigningTestHelper.t.sol +++ b/test/trading/seaport/utils/SigningTestHelper.t.sol @@ -8,18 +8,12 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; abstract contract SigningTestHelper is Test { - function _sign( - uint256 signerPrivateKey, - bytes32 signatureDigest - ) internal pure returns (bytes memory) { + function _sign(uint256 signerPrivateKey, bytes32 signatureDigest) internal pure returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); return abi.encodePacked(r, s, v); } - function _signCompact( - uint256 signerPrivateKey, - bytes32 signatureDigest - ) internal pure returns (bytes memory) { + function _signCompact(uint256 signerPrivateKey, bytes32 signatureDigest) internal pure returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, signatureDigest); if (v != 27) { // then left-most bit of s has to be flipped to 1. diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index bb61eb30..5e10fd9c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -4,15 +4,18 @@ // solhint-disable-next-line compiler-version pragma solidity ^0.8.17; +// solhint-disable-next-line no-global-import +import "forge-std/Test.sol"; import {ReceivedItem, Schema, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; -import {ImmutableSignedZoneV2TestHelper} from "./ImmutableSignedZoneV2TestHelper.t.sol"; +import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; // solhint-disable func-name-mixedcase -contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { +contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { event SeaportCompatibleContractDeployed(); // SIP-5 event SignerAdded(address signer); // SIP-7 event SignerRemoved(address signer); // SIP-7 @@ -29,6 +32,18 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { uint256 public constant MAX_UINT_TYPE = type(uint256).max; + // solhint-disable private-vars-leading-underscore + address internal immutable OWNER = makeAddr("owner"); + address internal immutable FULFILLER = makeAddr("fulfiller"); + address internal immutable OFFERER = makeAddr("offerer"); + address internal immutable SIGNER; + uint256 internal immutable SIGNER_PRIVATE_KEY; + // solhint-enable private-vars-leading-underscore + + constructor() { + (SIGNER, SIGNER_PRIVATE_KEY) = makeAddrAndKey("signer"); + } + /* constructor */ function test_contructor_grantsAdminRoleToOwner() public { @@ -57,7 +72,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* addSigner - L */ function test_addSigner_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.expectRevert( "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" ); @@ -66,7 +81,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_addSigner_revertsIfSignerIsTheZeroAddress() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.expectRevert(abi.encodeWithSelector(SignerCannotBeZeroAddress.selector)); vm.prank(OWNER); zone.addSigner(address(0)); @@ -74,7 +89,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_addSigner_emitsSignerAddedEvent() public { address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.expectEmit(address(zone)); emit SignerAdded(signerToAdd); vm.prank(OWNER); @@ -83,7 +98,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_addSigner_revertsIfSignerAlreadyActive() public { address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.prank(OWNER); zone.addSigner(signerToAdd); vm.expectRevert(abi.encodeWithSelector(SignerAlreadyActive.selector, signerToAdd)); @@ -93,7 +108,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_addSigner_revertsIfSignerWasPreviouslyActive() public { address signerToAdd = makeAddr("signerToAdd"); - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.prank(OWNER); zone.addSigner(signerToAdd); vm.prank(OWNER); @@ -106,7 +121,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* removeSigner - L */ function test_removeSigner_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.expectRevert( "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" ); @@ -116,7 +131,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_removeSigner_revertsIfSignerNotActive() public { address signerToRemove = makeAddr("signerToRemove"); - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.expectRevert(abi.encodeWithSelector(SignerNotActive.selector, signerToRemove)); vm.prank(OWNER); zone.removeSigner(signerToRemove); @@ -124,7 +139,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { function test_removeSigner_emitsSignerRemovedEvent() public { address signerToRemove = makeAddr("signerToRemove"); - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.prank(OWNER); zone.addSigner(signerToRemove); vm.expectEmit(address(zone)); @@ -136,7 +151,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* updateAPIEndpoint - N */ function test_updateAPIEndpoint_revertsIfCalledByNonAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.prank(makeAddr("random")); vm.expectRevert( "AccessControl: account 0x42a3d6e125aad539ac15ed04e1478eb0a4dc1489 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" @@ -145,7 +160,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_updateAPIEndpoint_updatesAPIEndpointIfCalledByAdminRole() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); vm.prank(OWNER); string memory expectedApiEndpoint = "https://www.new-immutable.com"; zone.updateAPIEndpoint(expectedApiEndpoint); @@ -220,7 +235,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* supportsInterface - L */ function test_supportsInterface() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); assertTrue(zone.supportsInterface(0x01ffc9a7)); // ERC165 interface assertFalse(zone.supportsInterface(0xffffffff)); // ERC165 compliance assertTrue(zone.supportsInterface(0x3839be19)); // ZoneInterface @@ -231,7 +246,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* validateOrder - N */ function test_validateOrder_revertsIfEmptyExtraData() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, @@ -251,7 +266,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfExtraDataLengthIsLessThan93() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, @@ -273,7 +288,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfExtraDataVersionIsNotSupported() public { - ImmutableSignedZoneV2 zone = _newZone(); + ImmutableSignedZoneV2 zone = _newZone(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), fulfiller: FULFILLER, @@ -293,11 +308,12 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfSignatureHasExpired() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); + bytes memory extraData = + _buildExtraData(zone, SIGNER_PRIVATE_KEY, FULFILLER, expiration, orderHash, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -325,12 +341,13 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfActualFulfillerDoesNotMatchExpectedFulfiller() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); address randomFulfiller = makeAddr("random"); bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); + bytes memory extraData = + _buildExtraData(zone, SIGNER_PRIVATE_KEY, FULFILLER, expiration, orderHash, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -356,11 +373,12 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_revertsIfSignerIsNotActive() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); uint64 expiration = 100; - bytes memory extraData = _buildExtraData(zone, orderHash, expiration, new bytes(0)); + bytes memory extraData = + _buildExtraData(zone, SIGNER_PRIVATE_KEY, FULFILLER, expiration, orderHash, new bytes(0)); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -381,7 +399,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateOrder_returnsMagicValueOnSuccessfulValidation() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); vm.prank(OWNER); zone.addSigner(SIGNER); @@ -412,7 +430,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { bytes1(0x03), substandard3Data, bytes1(0x04), substandard4Data, bytes1(0x06), substandard6Data ); - bytes memory extraData = _buildExtraData(zone, orderHash, expiration, context); + bytes memory extraData = _buildExtraData(zone, SIGNER_PRIVATE_KEY, FULFILLER, expiration, orderHash, context); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9), @@ -432,7 +450,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _getSupportedSubstandards - L */ function test_getSupportedSubstandards() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); uint256[] memory supportedSubstandards = zone.exposed_getSupportedSubstandards(); assertEq(supportedSubstandards.length, 3); assertEq(supportedSubstandards[0], 3); @@ -443,7 +461,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveSignedOrderHash - N */ function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); address fulfiller = 0x71458637cD221877830A21F543E8b731e93C3627; uint64 expiration = 1234995; bytes32 orderHash = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); @@ -455,7 +473,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandards - L */ function test_validateSubstandards_emptyContext() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -474,7 +492,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_substandard3() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ @@ -506,7 +524,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_substandard4() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); @@ -535,7 +553,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_substandard6() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); SpentItem[] memory spentItems = new SpentItem[](1); spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); @@ -570,7 +588,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_multipleSubstandardsInCorrectOrder() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ @@ -607,7 +625,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_substandards3Then6() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); SpentItem[] memory spentItems = new SpentItem[](1); spentItems[0] = SpentItem({itemType: ItemType.ERC1155, token: address(0x5), identifier: 222, amount: 10}); @@ -644,7 +662,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_allSubstandards() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); SpentItem[] memory spentItems = new SpentItem[](1); spentItems[0] = SpentItem({itemType: ItemType.ERC1155, token: address(0x5), identifier: 222, amount: 10}); @@ -687,7 +705,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandards_revertsOnMultipleSubstandardsInIncorrectOrder() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ @@ -731,7 +749,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard3 */ function test_validateSubstandard3_returnsZeroLengthIfNotSubstandard3() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -751,7 +769,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -779,7 +797,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_revertsIfDerivedReceivedItemsHashNotEqualToHashInContext() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ @@ -815,7 +833,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard3_returns33OnSuccess() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); ReceivedItem memory receivedItem = ReceivedItem({ @@ -851,7 +869,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard4 - N */ function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -871,7 +889,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -899,7 +917,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_revertsIfDerivedOrderHashesIsNotEqualToHashesInContext() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); @@ -926,7 +944,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard4_returnsLengthOfSubstandardSegmentOnSuccess() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory orderHashes = new bytes32[](1); orderHashes[0] = bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9); @@ -959,7 +977,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _validateSubstandard6 - N */ function test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -979,7 +997,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard6_revertsIfContextLengthIsInvalid() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ZoneParameters memory zoneParameters = ZoneParameters({ orderHash: bytes32(0), @@ -1007,7 +1025,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard6_revertsIfDerivedReceivedItemsHashesIsNotEqualToHashesInContext() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); SpentItem[] memory spentItems = new SpentItem[](1); spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); @@ -1045,7 +1063,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_validateSubstandard6_returnsLengthOfSubstandardSegmentOnSuccess() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); SpentItem[] memory spentItems = new SpentItem[](1); spentItems[0] = SpentItem({itemType: ItemType.ERC721, token: address(0x2), identifier: 222, amount: 10}); @@ -1084,7 +1102,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveReceivedItemsHash - N */ function test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](0); @@ -1093,7 +1111,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_deriveReceivedItemsHash_returnsHashForValidReceivedItems() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](2); receivedItems[0] = ReceivedItem({ @@ -1117,7 +1135,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_deriveReceivedItemsHash_returnsHashForReceivedItemWithAVeryLargeAmount() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); ReceivedItem[] memory receivedItems = new ReceivedItem[](1); receivedItems[0] = ReceivedItem({ itemType: ItemType.ERC20, @@ -1135,7 +1153,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _bytes32ArrayIncludes - N */ function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsEmpty() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory emptySourceArray = new bytes32[](0); bytes32[] memory valuesArray = new bytes32[](2); @@ -1145,7 +1163,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsSmallerThanValuesArray() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory sourceArray = new bytes32[](1); bytes32[] memory valuesArray = new bytes32[](2); @@ -1155,7 +1173,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayDoesNotIncludeValuesArray() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory sourceArray = new bytes32[](2); sourceArray[0] = bytes32(uint256(1)); @@ -1169,7 +1187,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_bytes32ArrayIncludes_returnsTrueIfSourceArrayIncludesValuesArray() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory sourceArray = new bytes32[](2); sourceArray[0] = bytes32(uint256(1)); @@ -1183,7 +1201,7 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { } function test_bytes32ArrayIncludes_returnsTrueIfValuesArrayIsASubsetOfSourceArray() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32[] memory sourceArray = new bytes32[](4); sourceArray[0] = bytes32(uint256(1)); @@ -1201,14 +1219,14 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _domainSeparator - N */ function test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 domainSeparator = zone.exposed_domainSeparator(); assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); } function test_domainSeparator_returnsUpdatedDomainSeparatorIfChainIDIsDifferentFromValueSetOnDeployment() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 domainSeparatorCached = zone.exposed_domainSeparator(); vm.chainId(31338); @@ -1221,11 +1239,50 @@ contract ImmutableSignedZoneV2Test is ImmutableSignedZoneV2TestHelper { /* _deriveDomainSeparator - N */ function test_deriveDomainSeparator_returnsDomainSeparatorForChainID() public { - ImmutableSignedZoneV2Harness zone = _newZoneHarness(); + ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); bytes32 domainSeparator = zone.exposed_deriveDomainSeparator(); assertEq(domainSeparator, bytes32(0xafb48e1c246f21ba06352cb2c0ebe99b8adc2590dfc48fa547732df870835b42)); } + + /* helper functions */ + + function _newZone(address owner) internal returns (ImmutableSignedZoneV2) { + return new ImmutableSignedZoneV2( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + } + + function _newZoneHarness(address owner) internal returns (ImmutableSignedZoneV2Harness) { + return new ImmutableSignedZoneV2Harness( + "MyZoneName", + "https://www.immutable.com", + "https://www.immutable.com/docs", + owner + ); + } + + function _buildExtraData( + ImmutableSignedZoneV2Harness zone, + uint256 signerPrivateKey, + address fulfiller, + uint64 expiration, + bytes32 orderHash, + bytes memory context + ) internal 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)), + context + ); + return extraData; + } } // solhint-enable func-name-mixedcase diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol deleted file mode 100644 index a0314f59..00000000 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2TestHelper.t.sol +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2024 -// SPDX-License-Identifier: Apache-2 - -// solhint-disable-next-line compiler-version -pragma solidity ^0.8.17; - -// solhint-disable-next-line no-global-import -import "forge-std/Test.sol"; -import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; -import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; - -abstract contract ImmutableSignedZoneV2TestHelper is Test, SigningTestHelper { - // solhint-disable private-vars-leading-underscore - address internal immutable OWNER = makeAddr("owner"); - address internal immutable FULFILLER = makeAddr("fulfiller"); - address internal immutable OFFERER = makeAddr("offerer"); - address internal immutable SIGNER = makeAddr("signer"); - // solhint-enable private-vars-leading-underscore - - function _newZone() internal returns (ImmutableSignedZoneV2) { - return new ImmutableSignedZoneV2( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - OWNER - ); - } - - function _newZoneHarness() internal returns (ImmutableSignedZoneV2Harness) { - return new ImmutableSignedZoneV2Harness( - "MyZoneName", - "https://www.immutable.com", - "https://www.immutable.com/docs", - OWNER - ); - } - - function _buildExtraData( - ImmutableSignedZoneV2Harness zone, - bytes32 orderHash, - uint64 expiration, - bytes memory context - ) internal returns (bytes memory) { - (, uint256 signerPK) = makeAddrAndKey("signer"); - bytes32 eip712SignedOrderHash = zone.exposed_deriveSignedOrderHash(FULFILLER, expiration, orderHash, context); - bytes memory extraData = abi.encodePacked( - bytes1(0), - FULFILLER, - expiration, - _signCompact(signerPK, ECDSA.toTypedDataHash(zone.exposed_domainSeparator(), eip712SignedOrderHash)), - context - ); - return extraData; - } -} From bf9ddb64538ea29962ba52e223fe64a30c9f720c Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 13:17:06 +1000 Subject: [PATCH 70/79] Comment fix --- contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 14cb651a..0f6490f0 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -504,7 +504,7 @@ contract ImmutableSignedZoneV2 is } /** - * @dev helper function to check if every element of values exists in sourceArray + * @dev Helper function to check if every element of values exists in sourceArray * optimised for performance checking arrays sized 0-15 * * @param sourceArray source array From b6ea082c4f1a2d0fad236f4bb3ffe712e8f3703e Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 14:38:18 +1000 Subject: [PATCH 71/79] Refactor errors --- .../seaport/zones/ImmutableSignedZoneV2.sol | 21 ++----- .../zones/interfaces/SIP7EventsAndErrors.sol | 27 +++++++-- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 57 +++++++------------ 3 files changed, 48 insertions(+), 57 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 0f6490f0..f7fffb5f 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -388,15 +388,11 @@ contract ImmutableSignedZoneV2 is } if (context.length < 33) { - // TODO: Does size of error message have a gas impact? - revert InvalidExtraData( - "invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", - zoneParameters.orderHash - ); + revert InvalidExtraData("invalid substandard 3 data length", zoneParameters.orderHash); } if (_deriveReceivedItemsHash(zoneParameters.consideration, 1, 1) != bytes32(context[1:33])) { - revert SubstandardViolation(3, "invalid consideration hash", zoneParameters.orderHash); + revert Substandard3Violation(zoneParameters.orderHash); } return 33; @@ -420,10 +416,7 @@ contract ImmutableSignedZoneV2 is // substandard ID + array offset + array length if (context.length < 65) { - revert InvalidExtraData( - "invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", - zoneParameters.orderHash - ); + revert InvalidExtraData("invalid substandard 4 data length", zoneParameters.orderHash); } uint256 expectedOrderHashesSize = uint256(bytes32(context[33:65])); @@ -432,7 +425,7 @@ contract ImmutableSignedZoneV2 is // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes if (!_bytes32ArrayIncludes(zoneParameters.orderHashes, expectedOrderHashes)) { - revert SubstandardViolation(4, "invalid order hashes", zoneParameters.orderHash); + revert Substandard4Violation(zoneParameters.orderHashes, expectedOrderHashes, zoneParameters.orderHash); } return substandardIndexEnd + 1; @@ -455,9 +448,7 @@ contract ImmutableSignedZoneV2 is } if (context.length < 65) { - revert InvalidExtraData( - "invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", zoneParameters.orderHash - ); + revert InvalidExtraData("invalid substandard 6 data length", zoneParameters.orderHash); } uint256 originalFirstOfferItemAmount = uint256(bytes32(context[1:33])); @@ -468,7 +459,7 @@ contract ImmutableSignedZoneV2 is zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount ) != expectedReceivedItemsHash ) { - revert SubstandardViolation(6, "invalid consideration hash", zoneParameters.orderHash); + revert Substandard6Violation(zoneParameters.offer[0].amount, originalFirstOfferItemAmount, zoneParameters.orderHash); } return 65; diff --git a/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol index bd0caeb3..77534f11 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol @@ -52,13 +52,32 @@ interface SIP7EventsAndErrors { error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); /** - * @dev Revert with an error if a substandard validation fails + * @dev Revert with an error if supplied order extraData is invalid + * or improperly formatted. + */ + error InvalidExtraData(string reason, bytes32 orderHash); + + /** + * @dev Revert with an error if a substandard validation fails. + * This is a custom error that is not part of the SIP-7 spec. */ error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); /** - * @dev Revert with an error if supplied order extraData is invalid - * or improperly formatted. + * @dev Revert with an error if substandard 3 validation fails. + * This is a custom error that is not part of the SIP-7 spec. */ - error InvalidExtraData(string reason, bytes32 orderHash); + error Substandard3Violation(bytes32 orderHash); + + /** + * @dev Revert with an error if substandard 4 validation fails. + * This is a custom error that is not part of the SIP-7 spec. + */ + error Substandard4Violation(bytes32[] actualOrderHashes, bytes32[] expectedOrderHashes, bytes32 orderHash); + + /** + * @dev Revert with an error if substandard 6 validation fails. + * This is a custom error that is not part of the SIP-7 spec. + */ + error Substandard6Violation(uint256 actualSpentItemAmount, uint256 originalSpentItemAmount, bytes32 orderHash); } diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 5e10fd9c..4f2c97d5 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -12,23 +12,13 @@ import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {SIP6EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol"; +import {SIP7EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol"; // solhint-disable func-name-mixedcase -contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { +contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErrors, SIP7EventsAndErrors { event SeaportCompatibleContractDeployed(); // SIP-5 - event SignerAdded(address signer); // SIP-7 - event SignerRemoved(address signer); // SIP-7 - - error SignerCannotBeZeroAddress(); // SIP-7 - error SignerAlreadyActive(address signer); // SIP-7 - error SignerCannotBeReauthorized(address signer); // SIP-7 - error SignerNotActive(address signer); // SIP-7 - error InvalidExtraData(string reason, bytes32 orderHash); // SIP-7 - error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); // SIP-7 (custom) - error UnsupportedExtraDataVersion(uint8 version); // SIP-6 - error SignatureExpired(uint256 currentTimestamp, uint256 expiration, bytes32 orderHash); // SIP-7 - error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); // SIP-7 uint256 public constant MAX_UINT_TYPE = type(uint256).max; @@ -788,9 +778,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { vm.expectRevert( abi.encodeWithSelector( - InvalidExtraData.selector, - "invalid context, expecting substandard ID 3 followed by bytes32 consideration hash", - zoneParameters.orderHash + InvalidExtraData.selector, "invalid substandard 3 data length", zoneParameters.orderHash ) ); zone.exposed_validateSubstandard3(context, zoneParameters); @@ -824,11 +812,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { bytes memory context = abi.encodePacked(bytes1(0x03), bytes32(0)); - vm.expectRevert( - abi.encodeWithSelector( - SubstandardViolation.selector, 3, "invalid consideration hash", zoneParameters.orderHash - ) - ); + vm.expectRevert(abi.encodeWithSelector(Substandard3Violation.selector, zoneParameters.orderHash)); zone.exposed_validateSubstandard3(context, zoneParameters); } @@ -908,9 +892,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { vm.expectRevert( abi.encodeWithSelector( - InvalidExtraData.selector, - "invalid context, expecting substandard ID 4 followed by bytes32 array offset and bytes32 array length", - zoneParameters.orderHash + InvalidExtraData.selector, "invalid substandard 4 data length", zoneParameters.orderHash ) ); zone.exposed_validateSubstandard4(context, zoneParameters); @@ -935,10 +917,18 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { zoneHash: bytes32(0) }); - bytes memory context = abi.encodePacked(bytes1(0x04), bytes32(uint256(32)), bytes32(uint256(1)), bytes32(0x0)); + bytes32[] memory expectedOrderHashes = new bytes32[](1); + expectedOrderHashes[0] = bytes32(0x123456); + + bytes memory context = abi.encodePacked(bytes1(0x04), abi.encode(expectedOrderHashes)); vm.expectRevert( - abi.encodeWithSelector(SubstandardViolation.selector, 4, "invalid order hashes", zoneParameters.orderHash) + abi.encodeWithSelector( + Substandard4Violation.selector, + zoneParameters.orderHashes, + expectedOrderHashes, + zoneParameters.orderHash + ) ); zone.exposed_validateSubstandard4(context, zoneParameters); } @@ -962,12 +952,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { zoneHash: bytes32(0) }); - bytes memory context = abi.encodePacked( - bytes1(0x04), - bytes32(uint256(32)), - bytes32(uint256(1)), - bytes32(0x43592598d0419e49d268e9b553427fd7ba1dd091eaa3f6127161e44afb7b40f9) - ); + bytes memory context = abi.encodePacked(bytes1(0x04), abi.encode(orderHashes)); uint256 substandardLengthResult = zone.exposed_validateSubstandard4(context, zoneParameters); // bytes1 + bytes32 + bytes32 + bytes32 = 97 @@ -1016,9 +1001,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { vm.expectRevert( abi.encodeWithSelector( - InvalidExtraData.selector, - "invalid context, expecting substandard ID 6 followed by (uint256, bytes32)", - zoneParameters.orderHash + InvalidExtraData.selector, "invalid substandard 6 data length", zoneParameters.orderHash ) ); zone.exposed_validateSubstandard6(context, zoneParameters); @@ -1055,9 +1038,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper { bytes memory context = abi.encodePacked(bytes1(0x06), uint256(100), bytes32(uint256(0x123456))); vm.expectRevert( - abi.encodeWithSelector( - SubstandardViolation.selector, 6, "invalid consideration hash", zoneParameters.orderHash - ) + abi.encodeWithSelector(Substandard6Violation.selector, spentItems[0].amount, 100, zoneParameters.orderHash) ); zone.exposed_validateSubstandard6(context, zoneParameters); } From 3235226e096b46ad30f8acd0279635ea20afecbb Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 14:54:05 +1000 Subject: [PATCH 72/79] Refactor --- .../seaport/zones/ImmutableSignedZone.sol | 2 - .../seaport/zones/ImmutableSignedZoneV2.sol | 2 - .../zones/interfaces/SIP5EventsAndErrors.sol | 16 +++++++ .../zones/interfaces/SIP5Interface.sol | 11 ++--- .../zones/interfaces/SIP7Interface.sol | 13 ++++-- .../seaport/zones/ImmutableSignedZoneV2.t.sol | 45 ++++++++++--------- 6 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol diff --git a/contracts/trading/seaport/zones/ImmutableSignedZone.sol b/contracts/trading/seaport/zones/ImmutableSignedZone.sol index 52f71a3a..7a5a78b6 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZone.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZone.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; -import {SIP7EventsAndErrors} from "./interfaces/SIP7EventsAndErrors.sol"; import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -32,7 +31,6 @@ import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; */ contract ImmutableSignedZone is ERC165, - SIP7EventsAndErrors, SIP6EventsAndErrors, ZoneInterface, SIP5Interface, diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index f7fffb5f..5cbe8779 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; -import {SIP7EventsAndErrors} from "./interfaces/SIP7EventsAndErrors.sol"; import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; @@ -24,7 +23,6 @@ import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; */ contract ImmutableSignedZoneV2 is ERC165, - SIP7EventsAndErrors, SIP6EventsAndErrors, ZoneInterface, SIP5Interface, diff --git a/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol new file mode 100644 index 00000000..22e8a4bb --- /dev/null +++ b/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol @@ -0,0 +1,16 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +/** + * @notice SIP5EventsAndErrors contains errors and events + * related to zone interaction as specified in the SIP5. + */ +interface SIP5EventsAndErrors { + /** + * @dev An event that is emitted when a SIP-5 compatible contract is deployed. + */ + event SeaportCompatibleContractDeployed(); +} diff --git a/contracts/trading/seaport/zones/interfaces/SIP5Interface.sol b/contracts/trading/seaport/zones/interfaces/SIP5Interface.sol index 1e434266..cfc156fd 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP5Interface.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP5Interface.sol @@ -1,20 +1,17 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 + // solhint-disable compiler-version pragma solidity ^0.8.17; import {Schema} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {SIP5EventsAndErrors} from "./SIP5EventsAndErrors.sol"; /** * @dev SIP-5: Contract Metadata Interface for Seaport Contracts * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md */ -interface SIP5Interface { - /** - * @dev An event that is emitted when a SIP-5 compatible contract is deployed. - */ - event SeaportCompatibleContractDeployed(); - +interface SIP5Interface is SIP5EventsAndErrors { /** * @dev Returns Seaport metadata for this contract, returning the * contract name and supported schemas. diff --git a/contracts/trading/seaport/zones/interfaces/SIP7Interface.sol b/contracts/trading/seaport/zones/interfaces/SIP7Interface.sol index 79808fab..c09fad68 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP7Interface.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP7Interface.sol @@ -1,8 +1,11 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 + // solhint-disable compiler-version pragma solidity ^0.8.17; +import {SIP7EventsAndErrors} from "./SIP7EventsAndErrors.sol"; + /** * @title SIP7Interface * @author ryanio, Immutable @@ -11,13 +14,15 @@ pragma solidity ^0.8.17; * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md * */ -interface SIP7Interface { +interface SIP7Interface is SIP7EventsAndErrors { /** * @dev The struct for storing signer info. */ struct SignerInfo { - bool active; /// If the signer is currently active. - bool previouslyActive; /// If the signer has been active before. + /// If the signer is currently active. + bool active; + /// If the signer has been active before. + bool previouslyActive; } /** diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol index 4f2c97d5..a010d57c 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol @@ -12,14 +12,19 @@ import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {SIP5EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol"; import {SIP6EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol"; import {SIP7EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol"; // solhint-disable func-name-mixedcase -contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErrors, SIP7EventsAndErrors { - event SeaportCompatibleContractDeployed(); // SIP-5 - +contract ImmutableSignedZoneV2Test is + Test, + SigningTestHelper, + SIP5EventsAndErrors, + SIP6EventsAndErrors, + SIP7EventsAndErrors +{ uint256 public constant MAX_UINT_TYPE = type(uint256).max; // solhint-disable private-vars-leading-underscore @@ -59,7 +64,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro ); } - /* addSigner - L */ + /* addSigner */ function test_addSigner_revertsIfCalledByNonAdminRole() public { ImmutableSignedZoneV2 zone = _newZone(OWNER); @@ -108,7 +113,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro zone.addSigner(signerToAdd); } - /* removeSigner - L */ + /* removeSigner */ function test_removeSigner_revertsIfCalledByNonAdminRole() public { ImmutableSignedZoneV2 zone = _newZone(OWNER); @@ -138,7 +143,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro zone.removeSigner(signerToRemove); } - /* updateAPIEndpoint - N */ + /* updateAPIEndpoint */ function test_updateAPIEndpoint_revertsIfCalledByNonAdminRole() public { ImmutableSignedZoneV2 zone = _newZone(OWNER); @@ -159,7 +164,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(apiEndpoint, expectedApiEndpoint); } - /* getSeaportMetadata - L */ + /* getSeaportMetadata */ function test_getSeaportMetadata() public { string memory expectedZoneName = "MyZoneName"; @@ -193,7 +198,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(documentationURI, expectedDocumentationURI); } - /* sip7Information - L */ + /* sip7Information */ function test_sip7Information() public { string memory expectedApiEndpoint = "https://www.immutable.com"; @@ -222,7 +227,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(documentationURI, expectedDocumentationURI); } - /* supportsInterface - L */ + /* supportsInterface */ function test_supportsInterface() public { ImmutableSignedZoneV2 zone = _newZone(OWNER); @@ -233,7 +238,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertTrue(zone.supportsInterface(0x1a511c70)); // SIP-7 interface } - /* validateOrder - N */ + /* validateOrder */ function test_validateOrder_revertsIfEmptyExtraData() public { ImmutableSignedZoneV2 zone = _newZone(OWNER); @@ -437,7 +442,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(zone.validateOrder(zoneParameters), bytes4(0x17b1f942)); } - /* _getSupportedSubstandards - L */ + /* _getSupportedSubstandards */ function test_getSupportedSubstandards() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -448,7 +453,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(supportedSubstandards[2], 6); } - /* _deriveSignedOrderHash - N */ + /* _deriveSignedOrderHash */ function test_deriveSignedOrderHash_returnsHashOfSignedOrder() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -460,7 +465,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(derivedSignedOrderHash, 0x40c87207c5a0c362da24cb974859c70655de00fee9400f3a805ac360b90bd8c5); } - /* _validateSubstandards - L */ + /* _validateSubstandards */ function test_validateSubstandards_emptyContext() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -850,7 +855,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(substandardLengthResult, 33); } - /* _validateSubstandard4 - N */ + /* _validateSubstandard4 */ function test_validateSubstandard4_returnsZeroLengthIfNotSubstandard4() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -918,7 +923,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro }); bytes32[] memory expectedOrderHashes = new bytes32[](1); - expectedOrderHashes[0] = bytes32(0x123456); + expectedOrderHashes[0] = bytes32(0x17d4cf2b6c174a86b533210b50ba676a82e5ab1e2e89ea538f0a43a37f92fcbf); bytes memory context = abi.encodePacked(bytes1(0x04), abi.encode(expectedOrderHashes)); @@ -959,7 +964,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(substandardLengthResult, 97); } - /* _validateSubstandard6 - N */ + /* _validateSubstandard6 */ function test_validateSubstandard6_returnsZeroLengthIfNotSubstandard6() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -1080,7 +1085,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(substandardLengthResult, 65); } - /* _deriveReceivedItemsHash - N */ + /* _deriveReceivedItemsHash */ function test_deriveReceivedItemsHash_returnsHashIfNoReceivedItems() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -1131,7 +1136,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(receivedItemsHash, bytes32(0xdb99f7eb854f29cd6f8faedea38d7da25073ef9876653ff45ab5c10e51f8ce4f)); } - /* _bytes32ArrayIncludes - N */ + /* _bytes32ArrayIncludes */ function test_bytes32ArrayIncludes_returnsFalseIfSourceArrayIsEmpty() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -1197,7 +1202,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertTrue(includes); } - /* _domainSeparator - N */ + /* _domainSeparator */ function test_domainSeparator_returnsCachedDomainSeparatorWhenChainIDMatchesValueSetOnDeployment() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); @@ -1217,7 +1222,7 @@ contract ImmutableSignedZoneV2Test is Test, SigningTestHelper, SIP6EventsAndErro assertEq(domainSeparatorDerived, bytes32(0x835aabb0d2af048df195a75a990b42533471d4a4e82842cd54a892eaac463d74)); } - /* _deriveDomainSeparator - N */ + /* _deriveDomainSeparator */ function test_deriveDomainSeparator_returnsDomainSeparatorForChainID() public { ImmutableSignedZoneV2Harness zone = _newZoneHarness(OWNER); From a21cbaa7dcc1113e1a1cf90a30049abf13734677 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 14:57:04 +1000 Subject: [PATCH 73/79] Comments --- contracts/trading/seaport/zones/ImmutableSignedZone.sol | 5 +++-- .../seaport/zones/interfaces/SIP5EventsAndErrors.sol | 2 +- .../seaport/zones/interfaces/SIP6EventsAndErrors.sol | 7 ++++--- .../seaport/zones/interfaces/SIP7EventsAndErrors.sol | 7 ++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZone.sol b/contracts/trading/seaport/zones/ImmutableSignedZone.sol index 7a5a78b6..145d1acd 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZone.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZone.sol @@ -1,7 +1,8 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 -// solhint-disable compiler-version + // slither-disable-start missing-inheritance +// solhint-disable-next-line compiler-version pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; diff --git a/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol index 22e8a4bb..21bbf159 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.17; /** * @notice SIP5EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP5. + * related to zone interaction as specified in the SIP-5. */ interface SIP5EventsAndErrors { /** diff --git a/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol index 307518ed..0ffef2c3 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol @@ -1,15 +1,16 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 + // solhint-disable compiler-version pragma solidity ^0.8.17; /** * @notice SIP6EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP6. + * related to zone interaction as specified in the SIP-6. */ interface SIP6EventsAndErrors { /** - * @dev Revert with an error if SIP6 version is not supported + * @dev Revert with an error if SIP-6 version byte is not supported */ error UnsupportedExtraDataVersion(uint8 version); } diff --git a/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol index 77534f11..52c0877b 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol @@ -1,11 +1,12 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// Copyright (c) Immutable Pty Ltd 2018 - 2024 // SPDX-License-Identifier: Apache-2 + // solhint-disable compiler-version pragma solidity ^0.8.17; /** * @notice SIP7EventsAndErrors contains errors and events - * related to zone interaction as specified in the SIP7. + * related to zone interaction as specified in the SIP-7. */ interface SIP7EventsAndErrors { /** @@ -26,7 +27,7 @@ interface SIP7EventsAndErrors { /** * @dev Revert with an error if trying to remove a signer that is - * not active + * not active. */ error SignerNotActive(address signer); From fad825e4dad750277693cf24f388ac6572e80ea6 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Tue, 9 Apr 2024 15:15:27 +1000 Subject: [PATCH 74/79] Comments --- .../seaport/zones/ImmutableSignedZoneV2.sol | 74 ++++++++++--------- .../zones/interfaces/SIP6EventsAndErrors.sol | 2 +- ...utableSeaportSignedZoneV2Integration.t.sol | 7 +- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol index 5cbe8779..7a85d1b8 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol @@ -19,7 +19,7 @@ import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; * @author Immutable * @notice ImmutableSignedZoneV2 is a zone implementation based on the * SIP-7 standard https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md - * Implementing substandard 3, 4 and 6. + * implementing substandards 3, 4 and 6. */ contract ImmutableSignedZoneV2 is ERC165, @@ -82,7 +82,7 @@ contract ImmutableSignedZoneV2 is // Set the zone name. _ZONE_NAME = zoneName; - // set name hash + // Set name hash. _NAME_HASH = keccak256(bytes(zoneName)); // Set the API endpoint. @@ -95,7 +95,7 @@ contract ImmutableSignedZoneV2 is // Emit an event to signal a SIP-5 contract has been deployed. emit SeaportCompatibleContractDeployed(); - // Grant admin role to the specified owner + // Grant admin role to the specified owner. _grantRole(DEFAULT_ADMIN_ROLE, owner); } @@ -117,7 +117,7 @@ contract ImmutableSignedZoneV2 is // Revert if the signer was previously authorized. // Specified in SIP-7 to prevent compromised signer from being - // Cycled back into use. + // cycled back into use. if (_signers[signer].previouslyActive) { revert SignerCannotBeReauthorized(signer); } @@ -161,8 +161,8 @@ contract ImmutableSignedZoneV2 is * @dev Returns Seaport metadata for this contract, returning the * contract name and supported schemas. * - * @return name The contract name - * @return schemas The supported SIPs + * @return name The contract name. + * @return schemas The supported SIPs. */ function getSeaportMetadata() external @@ -182,7 +182,10 @@ contract ImmutableSignedZoneV2 is /** * @notice Returns signing information about the zone. * - * @return domainSeparator The domain separator used for signing. + * @return domainSeparator The domain separator used for signing. + * @return apiEndpoint The API endpoint to get signatures for orders. + * @return substandards The supported substandards. + * @return documentationURI The documentation URI. */ function sip7Information() external @@ -204,7 +207,7 @@ contract ImmutableSignedZoneV2 is } /** - * @notice ERC-165 interface support + * @notice ERC-165 interface support. * @param interfaceId The interface ID to check for support. */ function supportsInterface(bytes4 interfaceId) @@ -223,6 +226,8 @@ contract ImmutableSignedZoneV2 is * @dev This function is called by Seaport whenever any extraData is * provided by the caller. * + * @param zoneParameters The zone parameters containing data related to + * the fulfilment execution. * @return validOrderMagicValue A magic value indicating if the order is * currently valid. */ @@ -311,9 +316,9 @@ contract ImmutableSignedZoneV2 is } /** - * @dev get the supported substandards of the contract + * @dev Get the supported substandards of the contract. * - * @return substandards array of substandards supported + * @return substandards Array of substandards supported. * */ function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { @@ -346,10 +351,10 @@ contract ImmutableSignedZoneV2 is } /** - * @dev validate substandards 3, 4 and 6 based on context + * @dev Validate substandards 3, 4 and 6 based on context. * - * @param context bytes payload of context - * @param zoneParameters zone parameters + * @param context Bytes payload of context. + * @param zoneParameters The zone parameters. */ function _validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure { uint256 startIndex = 0; @@ -370,11 +375,11 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 3 + * @dev Validates substandard 3. * - * @param context bytes payload of context, 0 indexed to start of substandard segment - * @param zoneParameters zone parameters - * @return Length of substandard segment + * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param zoneParameters The zone parameters. + * @return Length of substandard segment. */ function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -397,11 +402,11 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 4 + * @dev Validates substandard 4. * - * @param context bytes payload of context, 0 indexed to start of substandard segment - * @param zoneParameters zone parameters - * @return Length of substandard segment + * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param zoneParameters The zone parameters. + * @return Length of substandard segment. */ function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -430,11 +435,11 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 6 + * @dev Validates substandard 6. * - * @param context bytes payload of context, 0 indexed to start of substandard segment - * @param zoneParameters zone parameters - * @return Length of substandard segment + * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param zoneParameters The zone parameters. + * @return Length of substandard segment. */ function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -457,18 +462,21 @@ contract ImmutableSignedZoneV2 is zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount ) != expectedReceivedItemsHash ) { - revert Substandard6Violation(zoneParameters.offer[0].amount, originalFirstOfferItemAmount, zoneParameters.orderHash); + revert Substandard6Violation( + zoneParameters.offer[0].amount, originalFirstOfferItemAmount, zoneParameters.orderHash + ); } return 65; } /** - * @dev Derive the received items hash based on received item array + * @dev Derive the received items hash based on received item array. * - * @param receivedItems actual received item array - * @param scalingFactorNumerator scaling factor numerator - * @param scalingFactorDenominator scaling factor denominator + * @param receivedItems Actual received item array. + * @param scalingFactorNumerator Scaling factor numerator. + * @param scalingFactorDenominator Scaling factor denominator. + * @return receivedItemsHash Hash of received items. */ function _deriveReceivedItemsHash( ReceivedItem[] calldata receivedItems, @@ -494,10 +502,10 @@ contract ImmutableSignedZoneV2 is /** * @dev Helper function to check if every element of values exists in sourceArray - * optimised for performance checking arrays sized 0-15 + * optimised for performance checking arrays sized 0-15. * - * @param sourceArray source array - * @param values values array + * @param sourceArray Source array. + * @param values Values array. */ function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) internal diff --git a/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol b/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol index 0ffef2c3..b4244c30 100644 --- a/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol +++ b/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol @@ -10,7 +10,7 @@ pragma solidity ^0.8.17; */ interface SIP6EventsAndErrors { /** - * @dev Revert with an error if SIP-6 version byte is not supported + * @dev Revert with an error if SIP-6 version byte is not supported. */ error UnsupportedExtraDataVersion(uint8 version); } diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 5cc15cc5..1fa109ff 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -432,7 +432,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper ); } - // advanced order + // advanced order, fill 1/100th of the order AdvancedOrder memory advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), @@ -616,7 +616,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper ); } - // advanced orders + // advanced order, fill 1/100th of the order AdvancedOrder memory advancedOrder = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), @@ -820,7 +820,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper ); } - // advanced orders + // advanced order, fill 1/2 of the order AdvancedOrder memory advancedOrder1 = AdvancedOrder({ parameters: orderParameters, numerator: uint120(50), @@ -829,6 +829,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper extraData: extraData1 }); + // advanced order, attempt to fill the whole order AdvancedOrder memory advancedOrder2 = AdvancedOrder({ parameters: orderParameters, numerator: uint120(1), From 0ce6af8b139f61e1171c3923826a4b3442dd45ef Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Wed, 10 Apr 2024 09:21:38 +1000 Subject: [PATCH 75/79] revert payment splitter changes --- test/payment-splitter/PaymentSplitter.t.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/payment-splitter/PaymentSplitter.t.sol b/test/payment-splitter/PaymentSplitter.t.sol index 4910aa30..4125cae9 100644 --- a/test/payment-splitter/PaymentSplitter.t.sol +++ b/test/payment-splitter/PaymentSplitter.t.sol @@ -165,7 +165,7 @@ contract PaymentSplitterTest is Test { mockToken1.mint(address(paymentSplitter), 100); mockToken2.mint(address(paymentSplitter), 100); - + vm.prank(fundsAdmin); vm.expectEmit(true, true, true, false, address(paymentSplitter)); @@ -203,7 +203,7 @@ contract PaymentSplitterTest is Test { mockToken1.mint(address(paymentSplitter), 100); mockToken2.mint(address(paymentSplitter), 100); - + vm.prank(fundsAdmin); paymentSplitter.releaseAll(); @@ -295,7 +295,7 @@ contract PaymentSplitterTest is Test { function testReceiveNativeTokenEvent() public { vm.deal(address(this), 100); - vm.expectEmit(true, true, false, false, address(paymentSplitter)); + vm.expectEmit(true, true, false, false, address(paymentSplitter)); emit PaymentReceived(address(this), 100); Address.sendValue(payable(address(paymentSplitter)), 100); } From 1d005306fb655deb6d0dcf385612f53a979cc994 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 11 Apr 2024 09:04:11 +1000 Subject: [PATCH 76/79] Reorganise seaport zones folder structure --- .../v1}/ImmutableSignedZone.sol | 4 +- .../{ => immutable-signed-zone/v1}/README.md | 0 .../v1/interfaces/SIP5Interface.sol | 26 ++++++++ .../v1/interfaces/SIP6EventsAndErrors.sol | 15 +++++ .../v1/interfaces/SIP7EventsAndErrors.sol | 64 +++++++++++++++++++ .../v1/interfaces/SIP7Interface.sol | 60 +++++++++++++++++ .../v2}/ImmutableSignedZoneV2.sol | 6 +- .../v2}/interfaces/SIP5EventsAndErrors.sol | 0 .../v2}/interfaces/SIP5Interface.sol | 0 .../v2}/interfaces/SIP6EventsAndErrors.sol | 0 .../v2/interfaces/SIP6Interface.sol | 14 ++++ .../v2}/interfaces/SIP7EventsAndErrors.sol | 0 .../v2}/interfaces/SIP7Interface.sol | 0 ...utableSeaportSignedZoneV2Integration.t.sol | 2 +- .../v1}/immutablesignedzone.test.ts | 10 +-- .../v2}/IImmutableSignedZoneV2Harness.t.sol | 2 +- .../v2}/ImmutableSignedZoneV2.t.sol | 10 +-- .../v2}/ImmutableSignedZoneV2Harness.t.sol | 2 +- 18 files changed, 198 insertions(+), 17 deletions(-) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v1}/ImmutableSignedZone.sol (99%) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v1}/README.md (100%) create mode 100644 contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP5Interface.sol create mode 100644 contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP6EventsAndErrors.sol create mode 100644 contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7EventsAndErrors.sol create mode 100644 contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7Interface.sol rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/ImmutableSignedZoneV2.sol (99%) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/interfaces/SIP5EventsAndErrors.sol (100%) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/interfaces/SIP5Interface.sol (100%) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/interfaces/SIP6EventsAndErrors.sol (100%) create mode 100644 contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6Interface.sol rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/interfaces/SIP7EventsAndErrors.sol (100%) rename contracts/trading/seaport/zones/{ => immutable-signed-zone/v2}/interfaces/SIP7Interface.sol (100%) rename test/trading/seaport/zones/{ => immutable-signed-zone/v1}/immutablesignedzone.test.ts (97%) rename test/trading/seaport/zones/{ => immutable-signed-zone/v2}/IImmutableSignedZoneV2Harness.t.sol (93%) rename test/trading/seaport/zones/{ => immutable-signed-zone/v2}/ImmutableSignedZoneV2.t.sol (98%) rename test/trading/seaport/zones/{ => immutable-signed-zone/v2}/ImmutableSignedZoneV2Harness.t.sol (95%) diff --git a/contracts/trading/seaport/zones/ImmutableSignedZone.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v1/ImmutableSignedZone.sol similarity index 99% rename from contracts/trading/seaport/zones/ImmutableSignedZone.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v1/ImmutableSignedZone.sol index 145d1acd..08d1ed17 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZone.sol +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v1/ImmutableSignedZone.sol @@ -1,4 +1,4 @@ -// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// Copyright (c) Immutable Pty Ltd 2018 - 2023 // SPDX-License-Identifier: Apache-2 // slither-disable-start missing-inheritance @@ -8,6 +8,7 @@ pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; +import {SIP7EventsAndErrors} from "./interfaces/SIP7EventsAndErrors.sol"; import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -32,6 +33,7 @@ import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; */ contract ImmutableSignedZone is ERC165, + SIP7EventsAndErrors, SIP6EventsAndErrors, ZoneInterface, SIP5Interface, diff --git a/contracts/trading/seaport/zones/README.md b/contracts/trading/seaport/zones/immutable-signed-zone/v1/README.md similarity index 100% rename from contracts/trading/seaport/zones/README.md rename to contracts/trading/seaport/zones/immutable-signed-zone/v1/README.md diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP5Interface.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP5Interface.sol new file mode 100644 index 00000000..1e434266 --- /dev/null +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP5Interface.sol @@ -0,0 +1,26 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +import {Schema} from "seaport-types/src/lib/ConsiderationStructs.sol"; + +/** + * @dev SIP-5: Contract Metadata Interface for Seaport Contracts + * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md + */ +interface SIP5Interface { + /** + * @dev An event that is emitted when a SIP-5 compatible contract is deployed. + */ + event SeaportCompatibleContractDeployed(); + + /** + * @dev Returns Seaport metadata for this contract, returning the + * contract name and supported schemas. + * + * @return name The contract name + * @return schemas The supported SIPs + */ + function getSeaportMetadata() external view returns (string memory name, Schema[] memory schemas); +} diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP6EventsAndErrors.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP6EventsAndErrors.sol new file mode 100644 index 00000000..307518ed --- /dev/null +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP6EventsAndErrors.sol @@ -0,0 +1,15 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +/** + * @notice SIP6EventsAndErrors contains errors and events + * related to zone interaction as specified in the SIP6. + */ +interface SIP6EventsAndErrors { + /** + * @dev Revert with an error if SIP6 version is not supported + */ + error UnsupportedExtraDataVersion(uint8 version); +} diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7EventsAndErrors.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7EventsAndErrors.sol new file mode 100644 index 00000000..bd0caeb3 --- /dev/null +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7EventsAndErrors.sol @@ -0,0 +1,64 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +/** + * @notice SIP7EventsAndErrors contains errors and events + * related to zone interaction as specified in the SIP7. + */ +interface SIP7EventsAndErrors { + /** + * @dev Emit an event when a new signer is added. + */ + event SignerAdded(address signer); + + /** + * @dev Emit an event when a signer is removed. + */ + event SignerRemoved(address signer); + + /** + * @dev Revert with an error if trying to add a signer that is + * already active. + */ + error SignerAlreadyActive(address signer); + + /** + * @dev Revert with an error if trying to remove a signer that is + * not active + */ + error SignerNotActive(address signer); + + /** + * @dev Revert with an error if a new signer is the zero address. + */ + error SignerCannotBeZeroAddress(); + + /** + * @dev Revert with an error if a removed signer is trying to be + * reauthorized. + */ + error SignerCannotBeReauthorized(address signer); + + /** + * @dev Revert with an error when the signature has expired. + */ + error SignatureExpired(uint256 currentTimestamp, uint256 expiration, bytes32 orderHash); + + /** + * @dev Revert with an error if the fulfiller does not match. + */ + error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash); + + /** + * @dev Revert with an error if a substandard validation fails + */ + error SubstandardViolation(uint256 substandardId, string reason, bytes32 orderHash); + + /** + * @dev Revert with an error if supplied order extraData is invalid + * or improperly formatted. + */ + error InvalidExtraData(string reason, bytes32 orderHash); +} diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7Interface.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7Interface.sol new file mode 100644 index 00000000..79808fab --- /dev/null +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v1/interfaces/SIP7Interface.sol @@ -0,0 +1,60 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2023 +// SPDX-License-Identifier: Apache-2 +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +/** + * @title SIP7Interface + * @author ryanio, Immutable + * @notice ImmutableSignedZone is an implementation of SIP-7 that requires orders + * to be signed by an approved signer. + * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md + * + */ +interface SIP7Interface { + /** + * @dev The struct for storing signer info. + */ + struct SignerInfo { + bool active; /// If the signer is currently active. + bool previouslyActive; /// If the signer has been active before. + } + + /** + * @notice Add a new signer to the zone. + * + * @param signer The new signer address to add. + */ + function addSigner(address signer) external; + + /** + * @notice Remove an active signer from the zone. + * + * @param signer The signer address to remove. + */ + function removeSigner(address signer) external; + + /** + * @notice Update the API endpoint returned by this zone. + * + * @param newApiEndpoint The new API endpoint. + */ + function updateAPIEndpoint(string calldata newApiEndpoint) external; + + /** + * @notice Returns signing information about the zone. + * + * @return domainSeparator The domain separator used for signing. + * @return apiEndpoint The API endpoint to get signatures for orders + * using this zone. + */ + function sip7Information() + external + view + returns ( + bytes32 domainSeparator, + string memory apiEndpoint, + uint256[] memory substandards, + string memory documentationURI + ); +} diff --git a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol similarity index 99% rename from contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol index 7a85d1b8..df2f93f4 100644 --- a/contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol @@ -6,9 +6,9 @@ pragma solidity ^0.8.17; import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; -import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; -import {SIP6EventsAndErrors} from "./interfaces/SIP6EventsAndErrors.sol"; import {SIP5Interface} from "./interfaces/SIP5Interface.sol"; +import {SIP6Interface} from "./interfaces/SIP6Interface.sol"; +import {SIP7Interface} from "./interfaces/SIP7Interface.sol"; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; @@ -23,9 +23,9 @@ import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol"; */ contract ImmutableSignedZoneV2 is ERC165, - SIP6EventsAndErrors, ZoneInterface, SIP5Interface, + SIP6Interface, SIP7Interface, AccessControlEnumerable { diff --git a/contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5EventsAndErrors.sol similarity index 100% rename from contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5EventsAndErrors.sol diff --git a/contracts/trading/seaport/zones/interfaces/SIP5Interface.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5Interface.sol similarity index 100% rename from contracts/trading/seaport/zones/interfaces/SIP5Interface.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5Interface.sol diff --git a/contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6EventsAndErrors.sol similarity index 100% rename from contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6EventsAndErrors.sol diff --git a/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6Interface.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6Interface.sol new file mode 100644 index 00000000..2bc63899 --- /dev/null +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6Interface.sol @@ -0,0 +1,14 @@ +// Copyright (c) Immutable Pty Ltd 2018 - 2024 +// SPDX-License-Identifier: Apache-2 + +// solhint-disable compiler-version +pragma solidity ^0.8.17; + +import {Schema} from "seaport-types/src/lib/ConsiderationStructs.sol"; +import {SIP6EventsAndErrors} from "./SIP6EventsAndErrors.sol"; + +/** + * @dev SIP-6: Multi-Zone ExtraData + * https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-6.md + */ +interface SIP6Interface is SIP6EventsAndErrors {} diff --git a/contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7EventsAndErrors.sol similarity index 100% rename from contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7EventsAndErrors.sol diff --git a/contracts/trading/seaport/zones/interfaces/SIP7Interface.sol b/contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7Interface.sol similarity index 100% rename from contracts/trading/seaport/zones/interfaces/SIP7Interface.sol rename to contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7Interface.sol diff --git a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol index 1fa109ff..6494fc8b 100644 --- a/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol +++ b/test/trading/seaport/ImmutableSeaportSignedZoneV2Integration.t.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.17; // solhint-disable-next-line no-global-import import "forge-std/Test.sol"; -import {IImmutableSignedZoneV2Harness} from "./zones/IImmutableSignedZoneV2Harness.t.sol"; +import {IImmutableSignedZoneV2Harness} from "./zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol"; import {ConduitController} from "../../../contracts/trading/seaport/conduit/ConduitController.sol"; import {ImmutableSeaportHarness} from "./ImmutableSeaportHarness.t.sol"; import {SigningTestHelper} from "./utils/SigningTestHelper.t.sol"; diff --git a/test/trading/seaport/zones/immutablesignedzone.test.ts b/test/trading/seaport/zones/immutable-signed-zone/v1/immutablesignedzone.test.ts similarity index 97% rename from test/trading/seaport/zones/immutablesignedzone.test.ts rename to test/trading/seaport/zones/immutable-signed-zone/v1/immutablesignedzone.test.ts index 1c9b024d..808e0b2a 100644 --- a/test/trading/seaport/zones/immutablesignedzone.test.ts +++ b/test/trading/seaport/zones/immutable-signed-zone/v1/immutablesignedzone.test.ts @@ -5,7 +5,7 @@ import { Wallet, constants } from "ethers"; import { keccak256 } from "ethers/lib/utils"; import { ethers } from "hardhat"; -import { ImmutableSignedZone__factory } from "../../../../typechain-types"; +import { ImmutableSignedZone__factory } from "../../../../../../typechain-types"; import { CONSIDERATION_EIP712_TYPE, @@ -15,13 +15,13 @@ import { autoMining, convertSignatureToEIP2098, getCurrentTimeStamp, -} from "../utils/signedZone"; +} from "../../../utils/signedZone"; -import type { ImmutableSignedZone } from "../../../../typechain-types"; +import type { ImmutableSignedZone } from "../../../../../../typechain-types"; import type { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import type { BytesLike } from "ethers"; -import { ReceivedItemStruct } from "../../../../typechain-types/contracts/trading/seaport/ImmutableSeaport"; -import { ZoneParametersStruct } from "../../../../typechain-types/contracts/trading/seaport/zones/ImmutableSignedZone"; +import { ReceivedItemStruct } from "../../../../../../typechain-types/contracts/trading/seaport/ImmutableSeaport"; +import { ZoneParametersStruct } from "../../../../../../typechain-types/contracts/trading/seaport/zones/immutable-signed-zone/v1/ImmutableSignedZone"; describe("ImmutableSignedZone", function () { let deployer: SignerWithAddress; diff --git a/test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol similarity index 93% rename from test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol rename to test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol index a994838b..eb5071f2 100644 --- a/test/trading/seaport/zones/IImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.17; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; -import {SIP7Interface} from "../../../../contracts/trading/seaport/zones/interfaces/SIP7Interface.sol"; +import {SIP7Interface} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7Interface.sol"; // solhint-disable func-name-mixedcase diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol similarity index 98% rename from test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol rename to test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol index a010d57c..abf094d8 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.t.sol @@ -8,13 +8,13 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {ReceivedItem, Schema, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; -import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; -import {SigningTestHelper} from "../utils/SigningTestHelper.t.sol"; +import {SigningTestHelper} from "../../../utils/SigningTestHelper.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {SIP5EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP5EventsAndErrors.sol"; -import {SIP6EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP6EventsAndErrors.sol"; -import {SIP7EventsAndErrors} from "../../../../contracts/trading/seaport/zones/interfaces/SIP7EventsAndErrors.sol"; +import {SIP5EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5EventsAndErrors.sol"; +import {SIP6EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6EventsAndErrors.sol"; +import {SIP7EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7EventsAndErrors.sol"; // solhint-disable func-name-mixedcase diff --git a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol similarity index 95% rename from test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol rename to test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol index 23f9ea2e..a31e44eb 100644 --- a/test/trading/seaport/zones/ImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.17; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; -import {ImmutableSignedZoneV2} from "../../../../contracts/trading/seaport/zones/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; // solhint-disable func-name-mixedcase From e73ff8731d0de8588e3a64fe0b1230f64d891948 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 11 Apr 2024 09:31:33 +1000 Subject: [PATCH 77/79] Add comments --- .../v2/ImmutableSignedZoneV2.sol | 102 ++++++++++-------- 1 file changed, 57 insertions(+), 45 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 df2f93f4..2d68d36b 100644 --- a/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol @@ -64,19 +64,18 @@ contract ImmutableSignedZoneV2 is /// Request and response payloads are defined in SIP-7. string private _sip7APIEndpoint; - /// @dev The documentationURI; + /// @dev The documentationURI. string private _documentationURI; /** * @notice Constructor to deploy the contract. * - * @param zoneName The name for the zone returned in - * getSeaportMetadata(). - * @param apiEndpoint The API endpoint where orders for this zone can be - * signed. - * Request and response payloads are defined in SIP-7. - * @param owner The address of the owner of this contract. Specified in the - * constructor to be CREATE2 / CREATE3 compatible. + * @param zoneName The name for the zone returned in getSeaportMetadata(). + * @param apiEndpoint The API endpoint where orders for this zone can be signed. + * Request and response payloads are defined in SIP-7. + * @param documentationURI The documentation URI. + * @param owner The address of the owner of this contract. Specified in the + * constructor to be CREATE2 / CREATE3 compatible. */ constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) { // Set the zone name. @@ -208,6 +207,7 @@ contract ImmutableSignedZoneV2 is /** * @notice ERC-165 interface support. + * * @param interfaceId The interface ID to check for support. */ function supportsInterface(bytes4 interfaceId) @@ -221,13 +221,13 @@ contract ImmutableSignedZoneV2 is } /** - * @notice Check if a given order including extraData is currently valid. + * @notice Validates a fulfilment execution. * * @dev This function is called by Seaport whenever any extraData is * provided by the caller. * - * @param zoneParameters The zone parameters containing data related to - * the fulfilment execution. + * @param zoneParameters The zone parameters containing data related to + the fulfilment execution. * @return validOrderMagicValue A magic value indicating if the order is * currently valid. */ @@ -255,23 +255,23 @@ contract ImmutableSignedZoneV2 is revert InvalidExtraData("extraData length must be at least 93 bytes", orderHash); } - // Revert if SIP6 version is not accepted (0) + // Revert if SIP-6 version is not accepted (0). if (uint8(extraData[0]) != _ACCEPTED_SIP6_VERSION) { revert UnsupportedExtraDataVersion(uint8(extraData[0])); } - // extraData bytes 1-21: expected fulfiller - // (zero address means not restricted) + // extraData bytes 1-21: expected fulfiller. + // (zero address means not restricted). address expectedFulfiller = address(bytes20(extraData[1:21])); - // extraData bytes 21-29: expiration timestamp (uint64) + // extraData bytes 21-29: expiration timestamp. uint64 expiration = uint64(bytes8(extraData[21:29])); - // extraData bytes 29-93: signature - // (strictly requires 64 byte compact sig, ERC2098) + // extraData bytes 29-93: signature. + // (strictly requires 64 byte compact sig, ERC2098). bytes calldata signature = extraData[29:93]; - // extraData bytes 93-end: context (optional, variable length) + // extraData bytes 93-end: context (optional, variable length). bytes calldata context = extraData[93:]; // Revert if expired. @@ -284,34 +284,34 @@ contract ImmutableSignedZoneV2 is // Put fulfiller on the stack for more efficient access. address actualFulfiller = zoneParameters.fulfiller; - // Revert unless - // Expected fulfiller is 0 address (any fulfiller) or - // Expected fulfiller is the same as actual fulfiller + // Revert unless: + // - expected fulfiller is 0 address (any fulfiller) OR + // - expected fulfiller is the same as actual fulfiller. if (expectedFulfiller != address(0) && expectedFulfiller != actualFulfiller) { revert InvalidFulfiller(expectedFulfiller, actualFulfiller, orderHash); } - // validate supported substandards + // Validate supported substandards. _validateSubstandards(context, zoneParameters); - // Derive the signedOrder hash + // Derive the signedOrder hash. bytes32 signedOrderHash = _deriveSignedOrderHash(expectedFulfiller, expiration, orderHash, context); // Derive the EIP-712 digest using the domain separator and signedOrder - // hash through openzepplin helper + // hash through openzepplin helper. bytes32 digest = ECDSA.toTypedDataHash(_domainSeparator(), signedOrderHash); // Recover the signer address from the digest and signature. - // Pass in R and VS from compact signature (ERC2098) + // Pass in R and VS from compact signature (ERC2098). address recoveredSigner = ECDSA.recover(digest, bytes32(signature[0:32]), bytes32(signature[32:64])); - // Revert if the signer is not active - // !This also reverts if the digest constructed on serverside is incorrect + // Revert if the signer is not active. + // This also reverts if the digest constructed on serverside is incorrect. if (!_signers[recoveredSigner].active) { revert SignerNotActive(recoveredSigner); } - // All validation completes and passes with no reverts, return valid + // All validation completes and passes with no reverts, return valid. validOrderMagicValue = ZoneInterface.validateOrder.selector; } @@ -319,7 +319,6 @@ contract ImmutableSignedZoneV2 is * @dev Get the supported substandards of the contract. * * @return substandards Array of substandards supported. - * */ function _getSupportedSubstandards() internal pure returns (uint256[] memory substandards) { // support substandards 3, 4 and 6 @@ -332,13 +331,11 @@ contract ImmutableSignedZoneV2 is /** * @dev Derive the signedOrder hash from the orderHash and expiration. * - * @param fulfiller The expected fulfiller address. - * @param expiration The signature expiration timestamp. - * @param orderHash The order hash. - * @param context The optional variable-length context. - * + * @param fulfiller The expected fulfiller address. + * @param expiration The signature expiration timestamp. + * @param orderHash The order hash. + * @param context The optional variable-length context. * @return signedOrderHash The signedOrder hash. - * */ function _deriveSignedOrderHash(address fulfiller, uint64 expiration, bytes32 orderHash, bytes calldata context) internal @@ -353,13 +350,16 @@ contract ImmutableSignedZoneV2 is /** * @dev Validate substandards 3, 4 and 6 based on context. * - * @param context Bytes payload of context. + * @param context Bytes payload of context. * @param zoneParameters The zone parameters. */ function _validateSubstandards(bytes calldata context, ZoneParameters calldata zoneParameters) internal pure { uint256 startIndex = 0; uint256 contextLength = context.length; + // 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; @@ -377,9 +377,9 @@ contract ImmutableSignedZoneV2 is /** * @dev Validates substandard 3. * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters. - * @return Length of substandard segment. + * @return Length of substandard segment. */ function _validateSubstandard3(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -404,9 +404,9 @@ contract ImmutableSignedZoneV2 is /** * @dev Validates substandard 4. * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters. - * @return Length of substandard segment. + * @return Length of substandard segment. */ function _validateSubstandard4(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -417,7 +417,7 @@ contract ImmutableSignedZoneV2 is return 0; } - // substandard ID + array offset + array length + // substandard ID + array offset + array length. if (context.length < 65) { revert InvalidExtraData("invalid substandard 4 data length", zoneParameters.orderHash); } @@ -426,7 +426,7 @@ contract ImmutableSignedZoneV2 is uint256 substandardIndexEnd = 64 + (expectedOrderHashesSize * 32); bytes32[] memory expectedOrderHashes = abi.decode(context[1:substandardIndexEnd + 1], (bytes32[])); - // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes + // revert if any order hashes in substandard data are not present in zoneParameters.orderHashes. if (!_bytes32ArrayIncludes(zoneParameters.orderHashes, expectedOrderHashes)) { revert Substandard4Violation(zoneParameters.orderHashes, expectedOrderHashes, zoneParameters.orderHash); } @@ -437,9 +437,9 @@ contract ImmutableSignedZoneV2 is /** * @dev Validates substandard 6. * - * @param context Bytes payload of context, 0 indexed to start of substandard segment. + * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters. - * @return Length of substandard segment. + * @return Length of substandard segment. */ function _validateSubstandard6(bytes calldata context, ZoneParameters calldata zoneParameters) internal @@ -454,9 +454,20 @@ contract ImmutableSignedZoneV2 is revert InvalidExtraData("invalid substandard 6 data length", zoneParameters.orderHash); } + // The first 32 bytes are the original first offer item amount. uint256 originalFirstOfferItemAmount = uint256(bytes32(context[1:33])); + // The next 32 bytes are the hash of the received items that were expected + // derived based on an assumption of full fulfilment (i.e. numerator = denominator = 1). bytes32 expectedReceivedItemsHash = bytes32(context[33:65]); + // To support partial fulfilment scenarios, we must scale the actual received item amounts + // to match the expected received items hash based on full fulfilment (i.e. numerator = denominator = 1). + // + // actualAmount = originalAmount * numerator / denominator + // originalAmount = actualAmount * denominator / numerator + // + // The numerator and denominator values are inferred from the actual and original (extracted + // from context) amounts of the first offer item. if ( _deriveReceivedItemsHash( zoneParameters.consideration, originalFirstOfferItemAmount, zoneParameters.offer[0].amount @@ -505,7 +516,8 @@ contract ImmutableSignedZoneV2 is * optimised for performance checking arrays sized 0-15. * * @param sourceArray Source array. - * @param values Values array. + * @param values Values array. + * @return True if all elements in values exist in sourceArray. */ function _bytes32ArrayIncludes(bytes32[] calldata sourceArray, bytes32[] memory values) internal From 26b132f0a519c791c97bef9552a16f1f46d32e63 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 11 Apr 2024 10:05:42 +1000 Subject: [PATCH 78/79] Format --- .../v2/IImmutableSignedZoneV2Harness.t.sol | 3 ++- .../v2/ImmutableSignedZoneV2.t.sol | 12 ++++++++---- .../v2/ImmutableSignedZoneV2Harness.t.sol | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol index eb5071f2..5b4dcd03 100644 --- a/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/IImmutableSignedZoneV2Harness.t.sol @@ -6,7 +6,8 @@ pragma solidity ^0.8.17; import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol"; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; -import {SIP7Interface} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7Interface.sol"; +import {SIP7Interface} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7Interface.sol"; // solhint-disable func-name-mixedcase 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 abf094d8..a822d45d 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 @@ -8,13 +8,17 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {ReceivedItem, Schema, SpentItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; import {ItemType} from "seaport-types/src/lib/ConsiderationEnums.sol"; -import {ImmutableSignedZoneV2} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; import {ImmutableSignedZoneV2Harness} from "./ImmutableSignedZoneV2Harness.t.sol"; import {SigningTestHelper} from "../../../utils/SigningTestHelper.t.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {SIP5EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5EventsAndErrors.sol"; -import {SIP6EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6EventsAndErrors.sol"; -import {SIP7EventsAndErrors} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7EventsAndErrors.sol"; +import {SIP5EventsAndErrors} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP5EventsAndErrors.sol"; +import {SIP6EventsAndErrors} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP6EventsAndErrors.sol"; +import {SIP7EventsAndErrors} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/SIP7EventsAndErrors.sol"; // solhint-disable func-name-mixedcase diff --git a/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol index a31e44eb..9aeaea26 100644 --- a/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol +++ b/test/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2Harness.t.sol @@ -5,7 +5,8 @@ pragma solidity ^0.8.17; import {ReceivedItem, ZoneParameters} from "seaport-types/src/lib/ConsiderationStructs.sol"; -import {ImmutableSignedZoneV2} from "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; +import {ImmutableSignedZoneV2} from + "../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol"; // solhint-disable func-name-mixedcase From eb5495af213b142c9edbca0605da13ed221ab8c7 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 11 Apr 2024 11:21:22 +1000 Subject: [PATCH 79/79] Add additional clarification for each substandard --- .../v2/ImmutableSignedZoneV2.sol | 16 +++++++++++++--- 1 file changed, 13 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 2d68d36b..b5d92925 100644 --- a/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol +++ b/contracts/trading/seaport/zones/immutable-signed-zone/v2/ImmutableSignedZoneV2.sol @@ -375,7 +375,12 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 3. + * @dev Validates substandard 3. This substandard is used to validate that the server's + * specified received items matches the actual received items. This substandard + * should be used when the server is able to accurately determine the received items + * for an exact fulfilment scenario. This substandard should NOT be used for fulfilments + * where the received items cannot be accurately known in advance, as is the case in + * best-efforts partial fulfilment scenarios. * * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters. @@ -402,7 +407,10 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 4. + * @dev Validates substandard 4. This substandard is used to validate that the server's + * specified orders that must be bundled with the fulfilment are present. This is useful + * for scenarios where the fulfiller desires a bundled fulfilment to revert if part of + * bundle is not available for fulfilment. * * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters. @@ -435,7 +443,9 @@ contract ImmutableSignedZoneV2 is } /** - * @dev Validates substandard 6. + * @dev Validates substandard 6. This substandard a variation on substandard 3 to support + * that supports fulfilments where server cannot accurately determine expected received + * items in advance, as is the case in best-efforts partial fulfilment scenarios. * * @param context Bytes payload of context, 0 indexed to start of substandard segment. * @param zoneParameters The zone parameters.