diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000..ae93109 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "linux-x64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/src/sips/lib/SIP15Decoder.sol b/src/sips/lib/SIP15Decoder.sol new file mode 100644 index 0000000..b0a43c3 --- /dev/null +++ b/src/sips/lib/SIP15Decoder.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {BaseSIPDecoder} from 'shipyard-core/src/sips/lib/BaseSIPDecoder.sol'; +import {Substandard5Comparison} from './SIP15Encoder.sol'; + +library SIP15Decoder { + /** + * @notice Read the SIP15 substandard version byte from the extraData field of a SIP15 encoded bytes array. + * @param extraData bytes calldata + * @return substandard the single byte substandard number 0-5 + */ + function decodeSubstandardVersion(bytes calldata extraData) internal pure returns (bytes1 substandard) { + return BaseSIPDecoder.decodeSubstandardVersion(extraData); + } + /** + * @notice decodes data encoding with substandard1Efficient encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) + */ + + function decodeSubstandard1Efficient(bytes calldata extraData) + internal + pure + returns (uint8, address, uint256, bytes32, bytes32) + { + return _decodeSingleTraitsWithOffset(extraData, 1); + } + + /** + * @notice decodes data encoding with substandard1 encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) + */ + function decodeSubstandard1(bytes calldata extraData) + internal + pure + returns (uint8, address, uint256, bytes32, bytes32) + { + return _decodeSingleTraitsWithOffset(extraData, 1); + } + + /** + * @notice decodes data encoding with substandard2 encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) + */ + function decodeSubstandard2(bytes calldata extraData) + internal + pure + returns (uint8, address, uint256, bytes32, bytes32) + { + return _decodeSingleTraitsWithOffset(extraData, 1); + } + + /** + * @notice decodes data encoding with substandard3 encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) + */ + function decodeSubstandard3(bytes calldata extraData) + internal + pure + returns (uint8, address, uint256, bytes32, bytes32) + { + return _decodeSingleTraitsWithOffset(extraData, 1); + } + + /** + * @notice decodes data encoding with substandard4 encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return (uint8 comparisonEnum, address token, uint256[] identifiers, bytes32 traitValue, bytes32 traitKey) + */ + function decodeSubstandard4(bytes calldata extraData) + internal + pure + returns (uint8, address, uint256[] memory, bytes32, bytes32) + { + return abi.decode(extraData[1:], (uint8, address, uint256[], bytes32, bytes32)); + } + + /** + * @notice decodes data encoding with substandard1 encoder + * @param extraData the encoded bytes with substandard and the extra data + * @return Substandard5Comparison + */ + function decodeSubstandard5(bytes calldata extraData) internal pure returns (Substandard5Comparison memory) { + return abi.decode(extraData[1:], (Substandard5Comparison)); + } + + function _decodeSingleTraitsWithOffset( + bytes calldata extraData, + uint256 sip15DataStartRelativeOffset + ) + internal + pure + returns (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) + { + return abi.decode(extraData[sip15DataStartRelativeOffset:], (uint8, address, uint256, bytes32, bytes32)); + } +} diff --git a/src/sips/lib/SIP15Encoder.sol b/src/sips/lib/SIP15Encoder.sol new file mode 100644 index 0000000..1cab67f --- /dev/null +++ b/src/sips/lib/SIP15Encoder.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {ReceivedItem} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import {ItemType} from 'seaport-types/src/lib/ConsiderationEnums.sol'; +import {ZoneParameters, Schema} from 'seaport-types/src/lib/ConsiderationStructs.sol'; + +struct Substandard5Comparison { + uint8[] comparisonEnums; + address token; + address traits; + uint256 identifier; + bytes32[] traitValues; + bytes32[] traitKeys; +} + +library SIP15Encoder { + /** + * @notice Generate a zone hash for an SIP15 contract, + * @param encodedData the SIP15 encoded extra data + * @return bytes32 hashed encoded data + */ + function generateZoneHash(bytes memory encodedData) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(encodedData)); + } + + /** + * @notice Encode extraData for SIP15-substandard-1 Efficient, which specifies the + * first consideration item, comparison "equal to", single trait key, zero trait value + * @param zoneParameters the orderParams of the order to be encoded + * @param traitKey the bytes32 encoded trait key for checking a trait on an ERC7496 token + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard1Efficient( + ZoneParameters memory zoneParameters, + bytes32 traitKey + ) internal pure returns (bytes memory) { + // Get the token address from the first consideration item + address token = zoneParameters.consideration[0].token; + + // Get the id from the first consideration item + uint256 id = zoneParameters.consideration[0].identifier; + return abi.encodePacked(uint8(0x00), abi.encode(0, token, id, bytes32(0), traitKey)); + } + + /** + * @notice Encode extraData for SIP15-substandard-1, which specifies the + * token address and id from first offer item + * @param zoneParameters the zone parameters with the offer to be used for the token and identifier + * @param comparisonEnum the comparison enum 0 - 5 + * @param traitKey the bytes32 encoded trait key for checking a trait on an ERC7496 token + * @param traitValue the expected value of the trait. + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard1( + ZoneParameters memory zoneParameters, + uint8 comparisonEnum, + bytes32 traitValue, + bytes32 traitKey + ) internal pure returns (bytes memory) { + // Get the token address from the first offer item + address token = zoneParameters.offer[0].token; + + // Get the id from the first offer item + uint256 id = zoneParameters.offer[0].identifier; + return abi.encodePacked(uint8(0x01), abi.encode(comparisonEnum, token, id, traitValue, traitKey)); + } + + /** + * @notice Encode extraData for SIP15-substandard-2, which specifies + * the token and identifier from the first consideration item as well as a comparison enum, trait key and expected trait value + * @param zoneParameters the zoneParameters of the order whose first consideration will be used for the token and identifier + * @param comparisonEnum The comparison enum 0 - 5 + * @param traitValue The expecta value of the trait + * @param traitKey the bytes32 encoded trait key for checking a trait on an ERC7496 token + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard2( + ZoneParameters memory zoneParameters, + uint8 comparisonEnum, + bytes32 traitValue, + bytes32 traitKey + ) internal pure returns (bytes memory) { + // Get the token address from the first consideration item + address token = zoneParameters.consideration[0].token; + + // Get the id from the first consideration item + uint256 identifier = zoneParameters.consideration[0].identifier; + return abi.encodePacked(uint8(0x02), abi.encode(comparisonEnum, token, identifier, traitValue, traitKey)); + } + + /** + * @notice Encode extraData for SIP15-substandard-3, + * which specifies a single comparison enum, token, identifier, traitValue and traitKey + * @param comparisonEnum the comparison enum 0 - 5 + * @param token the address of the collection + * @param identifier the tokenId of the token to be checked + * @param traitKey the bytes32 encoded trait key for checking a trait on an ERC7496 token + * @param traitValue the expected value of the trait. + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard3( + uint8 comparisonEnum, + address token, + uint256 identifier, + bytes32 traitValue, + bytes32 traitKey + ) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(0x03), abi.encode(comparisonEnum, token, identifier, traitValue, traitKey)); + } + + /** + * @notice Encode extraData for SIP15-substandard-4, which specifies a single comparison + * enum and token and multiple identifiers, single trait key and trait value. + * each comparison is against a single identifier and a single traitValue with a single tratKey. + * @param comparisonEnum the comparison enum 0 - 5 + * @param token the address of the collection + * @param identifiers the tokenId of the token to be checked + * @param traitKey the bytes32 encoded trait key for checking a trait on an ERC7496 token + * @param traitValue the expected value of the trait. + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard4( + uint8 comparisonEnum, + address token, + uint256[] memory identifiers, + bytes32 traitValue, + bytes32 traitKey + ) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(0x04), abi.encode(comparisonEnum, token, identifiers, traitValue, traitKey)); + } + + /** + * @notice Encode extraData for SIP15-substandard-5, which specifies a single tokenIdentifier + * @param comparisonStruct the struct of comparison data with the following values: + * - uint8[] comparisonEnums the array of comparison enums for each trait key and expected value, must be the same length as the trait keys and values. + * - address token the address of the token whose traits will be checked + * - address traits the address that contains the ERC7496 traits. this is useful if you have an erc712 with traits availible at a + * different address leave as address(0) if the traits can be retreived from the same address as the token + * - uint256 identifier the identifier of the token to be checked; + * - bytes32[] traitValues the array of expected trait values + * - bytes32[] traitKeys the encoded trait keys ; + * @return bytes the encoded extra data with added substandard + */ + function encodeSubstandard5(Substandard5Comparison memory comparisonStruct) internal pure returns (bytes memory) { + return abi.encodePacked(uint8(0x05), abi.encode(comparisonStruct)); + } +} diff --git a/test/sips/lib/CreateZoneParams.sol b/test/sips/lib/CreateZoneParams.sol new file mode 100644 index 0000000..887c686 --- /dev/null +++ b/test/sips/lib/CreateZoneParams.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.24; + +import {ZoneParameters} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import {SIP15Encoder, Substandard5Comparison} from '../../src/sips/SIP15Encoder.sol'; +import { + ConsiderationItemLib, + OfferItemLib, + OrderComponentsLib, + OrderParametersLib, + OrderLib, + SeaportArrays +} from 'seaport-sol/src/lib/SeaportStructLib.sol'; +import {Test, console} from 'forge-std/Test.sol'; +import { + AdvancedOrder, + ConsiderationItem, + CriteriaResolver, + OrderParameters, + ItemType, + OfferItem, + Order, + SpentItem, + ReceivedItem, + OrderComponents, + OrderType +} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import {ConsiderationInterface} from 'seaport-types/src/interfaces/ConsiderationInterface.sol'; + +contract CreateZoneParams is Test { + using OfferItemLib for OfferItem; + using OfferItemLib for OfferItem[]; + using ConsiderationItemLib for ConsiderationItem; + using ConsiderationItemLib for ConsiderationItem[]; + using OrderComponentsLib for OrderComponents; + using OrderParametersLib for OrderParameters; + using OrderLib for Order; + using OrderLib for Order[]; + + string constant SINGLE_721 = 'single 721'; + string constant SINGLE_721_Order = '721 order'; + + struct Context { + ConsiderationInterface seaport; + FuzzInputs fuzzInputs; + } + + struct FuzzInputs { + uint256 tokenId; + uint256 tokenId2; + uint128 amount; + address token; + address token2; + address erc20; + address offerer; + address recipient; + bytes32 zoneHash; + uint256 salt; + address fulfiller; + address seaport; + bytes32 traitKey; + bytes32 traitValue; + uint8 comparisonEnum; + } + + function _createZoneParams(Context memory context) internal view returns (ZoneParameters memory zoneParameters) { + // Avoid weird overflow issues. + context.fuzzInputs.amount = uint128(bound(context.fuzzInputs.amount, 1, 0xffffffffffffffff)); + context.fuzzInputs.tokenId = bound(context.fuzzInputs.tokenId, 0, 0xfffffffff); + //create offer item array from fuzz inputs + OfferItem[] memory offerItemArray = _createOfferArray(context.fuzzInputs); + //create consideration item array from fuzz inputs + ConsiderationItem[] memory considerationItemArray = _createConsiderationArray(context.fuzzInputs); + //create order components from fuzz inputs + OrderComponents memory orderComponents = + _buildOrderComponents(context.fuzzInputs, offerItemArray, considerationItemArray); + //create order + Order memory order = OrderLib.empty().withParameters(orderComponents.toOrderParameters()); + + //create advanced order + AdvancedOrder memory advancedOrder = order.toAdvancedOrder(1, 1, bytes('')); + + CriteriaResolver[] memory criteriaResolvers = new CriteriaResolver[](0); + //create zone parameters + zoneParameters = _getZoneParameters(advancedOrder, context.fuzzInputs.fulfiller, criteriaResolvers); + } + + function _createOfferArray(FuzzInputs memory _fuzzInputs) internal view returns (OfferItem[] memory _offerItems) { + _offerItems = SeaportArrays.OfferItems( + OfferItemLib.fromDefault(SINGLE_721).withToken(address(_fuzzInputs.token)).withIdentifierOrCriteria( + _fuzzInputs.tokenId + ), + OfferItemLib.fromDefault(SINGLE_721).withToken(address(_fuzzInputs.token2)).withIdentifierOrCriteria( + _fuzzInputs.tokenId % 7 + ) + ); + } + + function _createConsiderationArray(FuzzInputs memory _fuzzInputs) + internal + view + returns (ConsiderationItem[] memory _considerationItemArray) + { + ConsiderationItem memory erc721ConsiderationItem = ConsiderationItemLib.fromDefault(SINGLE_721) + .withIdentifierOrCriteria(_fuzzInputs.tokenId).withToken(_fuzzInputs.token).withStartAmount(1).withEndAmount(1) + .withRecipient(_fuzzInputs.recipient); + + // Create a native consideration item. + ConsiderationItem memory nativeConsiderationItem = ConsiderationItemLib.empty().withItemType(ItemType.NATIVE) + .withIdentifierOrCriteria(0).withStartAmount(_fuzzInputs.amount).withEndAmount(_fuzzInputs.amount).withRecipient( + _fuzzInputs.recipient + ); + + // Create a ERC20 consideration item. + ConsiderationItem memory erc20ConsiderationItemOne = ConsiderationItemLib.empty().withItemType(ItemType.ERC20) + .withToken(_fuzzInputs.erc20).withIdentifierOrCriteria(0).withStartAmount(_fuzzInputs.amount).withEndAmount( + _fuzzInputs.amount + ).withRecipient(_fuzzInputs.recipient); + // create consideration array + _considerationItemArray = + SeaportArrays.ConsiderationItems(erc721ConsiderationItem, nativeConsiderationItem, erc20ConsiderationItemOne); + } + + function _buildOrderComponents( + FuzzInputs memory _fuzzInputs, + OfferItem[] memory offerItemArray, + ConsiderationItem[] memory considerationItemArray + ) internal view returns (OrderComponents memory _orderComponents) { + // Create the offer and consideration item arrays. + OfferItem[] memory _offerItemArray = offerItemArray; + ConsiderationItem[] memory _considerationItemArray = considerationItemArray; + + // Build the OrderComponents for the prime offerer's order. + _orderComponents = OrderComponentsLib.fromDefault(SINGLE_721_Order).withOffer(_offerItemArray).withConsideration( + _considerationItemArray + ).withZone(address(1)).withOfferer(_fuzzInputs.offerer).withZone(address(2)).withOrderType( + OrderType.FULL_RESTRICTED + ).withZoneHash(_fuzzInputs.zoneHash); + } + + function _getZoneParameters( + AdvancedOrder memory advancedOrder, + address fulfiller, + CriteriaResolver[] memory criteriaResolvers + ) internal view returns (ZoneParameters memory zoneParameters) { + // Get orderParameters from advancedOrder + OrderParameters memory orderParameters = advancedOrder.parameters; + + // crate arbitrary orderHash + bytes32 orderHash = keccak256(abi.encode(advancedOrder)); + + (SpentItem[] memory spentItems, ReceivedItem[] memory receivedItems) = + orderParameters.getSpentAndReceivedItems(advancedOrder.numerator, advancedOrder.denominator, 0, criteriaResolvers); + // Store orderHash in orderHashes array to pass into zoneParameters + bytes32[] memory orderHashes = new bytes32[](1); + orderHashes[0] = orderHash; + + // Create ZoneParameters and add to zoneParameters array + zoneParameters = ZoneParameters({ + orderHash: orderHash, + fulfiller: fulfiller, + offerer: orderParameters.offerer, + offer: spentItems, + consideration: receivedItems, + extraData: advancedOrder.extraData, + orderHashes: orderHashes, + startTime: orderParameters.startTime, + endTime: orderParameters.endTime, + zoneHash: orderParameters.zoneHash + }); + } +} diff --git a/test/sips/lib/SIP15Decoder.t.sol b/test/sips/lib/SIP15Decoder.t.sol new file mode 100644 index 0000000..57816ba --- /dev/null +++ b/test/sips/lib/SIP15Decoder.t.sol @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.24; + +import {Test, console} from 'forge-std/Test.sol'; +import {SIP15Encoder, Substandard5Comparison} from '../../src/sips/SIP15Encoder.sol'; +import {SIP15Decoder} from '../../src/sips/SIP15Decoder.sol'; +import {ZoneParameters, Schema} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import { + ConsiderationItemLib, + FulfillmentComponentLib, + FulfillmentLib, + OfferItemLib, + ZoneParametersLib, + OrderComponentsLib, + OrderParametersLib, + AdvancedOrderLib, + OrderLib, + SeaportArrays +} from 'seaport-sol/src/lib/SeaportStructLib.sol'; + +import { + AdvancedOrder, + ConsiderationItem, + CriteriaResolver, + Fulfillment, + FulfillmentComponent, + OrderParameters, + ItemType, + OfferItem, + Order, + SpentItem, + ReceivedItem, + OrderComponents, + OrderType +} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import {ConsiderationInterface} from 'seaport-types/src/interfaces/ConsiderationInterface.sol'; +import {CreateZoneParams} from './CreateZoneParams.sol'; + +contract SIP15Decoder_Unit_test is CreateZoneParams { + using OfferItemLib for OfferItem; + using OfferItemLib for OfferItem[]; + using ConsiderationItemLib for ConsiderationItem; + using ConsiderationItemLib for ConsiderationItem[]; + using OrderComponentsLib for OrderComponents; + using OrderParametersLib for OrderParameters; + using OrderLib for Order; + using OrderLib for Order[]; + + function setUp() public { + // create a default offerItem for a single 721; + // note that it does not have token or identifier set + OfferItemLib.empty().withItemType(ItemType.ERC721).withStartAmount(1).withEndAmount(1).saveDefault(SINGLE_721); + + ConsiderationItemLib.empty().withItemType(ItemType.ERC721).withStartAmount(1).withEndAmount(1).saveDefault( + SINGLE_721 + ); + + OrderComponentsLib.empty().withOrderType(OrderType.FULL_RESTRICTED).withStartTime(block.timestamp).withEndTime( + block.timestamp + 10 + ).withSalt(0).saveDefault(SINGLE_721_Order); + } + + function test_DecodeSubstandard1EfficientFuzz(Context memory context) public { + ZoneParameters memory zoneParams = _createZoneParams(context); + bytes memory encodedData = SIP15Encoder.encodeSubstandard1Efficient(zoneParams, context.fuzzInputs.traitKey); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + this.decodeSubstandard1Efficient(encodedData); + assertEq(comparisonEnum, 0); + assertEq(traitKey, context.fuzzInputs.traitKey); + assertEq(traitValue, bytes32(0)); + assertEq(token, zoneParams.consideration[0].token); + assertEq(id, zoneParams.consideration[0].identifier); + } + + function test_DecodeSubstandard1Fuzz(Context memory context) public { + ZoneParameters memory zoneParams = _createZoneParams(context); + bytes memory encodedData = SIP15Encoder.encodeSubstandard1( + zoneParams, context.fuzzInputs.comparisonEnum, context.fuzzInputs.traitValue, context.fuzzInputs.traitKey + ); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + this.decodeSubstandard1(encodedData); + assertEq(comparisonEnum, context.fuzzInputs.comparisonEnum); + assertEq(traitKey, context.fuzzInputs.traitKey); + assertEq(traitValue, context.fuzzInputs.traitValue); + assertEq(token, zoneParams.offer[0].token); + assertEq(id, zoneParams.offer[0].identifier); + } + + function test_DecodeSubstandard2Fuzz(Context memory context) public { + ZoneParameters memory zoneParams = _createZoneParams(context); + bytes memory encodedData = SIP15Encoder.encodeSubstandard2( + zoneParams, context.fuzzInputs.comparisonEnum, context.fuzzInputs.traitValue, context.fuzzInputs.traitKey + ); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + this.decodeSubstandard2(encodedData); + assertEq(comparisonEnum, context.fuzzInputs.comparisonEnum); + assertEq(traitKey, context.fuzzInputs.traitKey); + assertEq(traitValue, context.fuzzInputs.traitValue); + assertEq(token, zoneParams.consideration[0].token); + assertEq(id, zoneParams.consideration[0].identifier); + } + + function test_DecodeSubstandard3Fuzz(Context memory context) public { + bytes memory encodedData = SIP15Encoder.encodeSubstandard3( + context.fuzzInputs.comparisonEnum, + context.fuzzInputs.token, + context.fuzzInputs.tokenId, + context.fuzzInputs.traitValue, + context.fuzzInputs.traitKey + ); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + this.decodeSubstandard3(encodedData); + assertEq(comparisonEnum, context.fuzzInputs.comparisonEnum); + assertEq(traitKey, context.fuzzInputs.traitKey); + assertEq(traitValue, context.fuzzInputs.traitValue); + assertEq(token, context.fuzzInputs.token); + assertEq(id, context.fuzzInputs.tokenId); + } + + function test_DecodeSubstandard4Fuzz(Context memory context) public { + uint256[] memory tokenIds = new uint256[](2); + tokenIds[0] = context.fuzzInputs.tokenId; + tokenIds[1] = context.fuzzInputs.tokenId2; + + bytes memory encodedData = SIP15Encoder.encodeSubstandard4( + context.fuzzInputs.comparisonEnum, + context.fuzzInputs.token, + tokenIds, + context.fuzzInputs.traitValue, + context.fuzzInputs.traitKey + ); + (uint8 comparisonEnum, address token, uint256[] memory ids, bytes32 traitValue, bytes32 traitKey) = + this.decodeSubstandard4(encodedData); + assertEq(comparisonEnum, context.fuzzInputs.comparisonEnum); + assertEq(traitKey, context.fuzzInputs.traitKey); + assertEq(traitValue, context.fuzzInputs.traitValue); + assertEq(token, context.fuzzInputs.token); + assertEq(ids[0], context.fuzzInputs.tokenId); + assertEq(ids[1], context.fuzzInputs.tokenId2); + } + + function test_DecodeSubstandard5Fuzz(Context memory context) public { + uint8[] memory _compEnums = new uint8[](2); + _compEnums[0] = context.fuzzInputs.comparisonEnum; + _compEnums[1] = 70; + bytes32[] memory _traitValues = new bytes32[](2); + _traitValues[0] = context.fuzzInputs.traitValue; + _traitValues[1] = bytes32(uint256(70)); + + bytes32[] memory _traitKeys = new bytes32[](2); + _traitKeys[0] = context.fuzzInputs.traitKey; + _traitKeys[1] = bytes32(uint256(421)); + + Substandard5Comparison memory comparison = Substandard5Comparison({ + comparisonEnums: _compEnums, + token: context.fuzzInputs.token, + traits: context.fuzzInputs.token2, + identifier: context.fuzzInputs.tokenId, + traitValues: _traitValues, + traitKeys: _traitKeys + }); + + bytes memory encodedData = SIP15Encoder.encodeSubstandard5(comparison); + + (Substandard5Comparison memory substandard5Comparison) = this.decodeSubstandard5(encodedData); + assertEq(substandard5Comparison.comparisonEnums[0], context.fuzzInputs.comparisonEnum); + assertEq(substandard5Comparison.comparisonEnums[1], 70); + assertEq(substandard5Comparison.traitKeys[0], context.fuzzInputs.traitKey); + assertEq(substandard5Comparison.traitValues[0], context.fuzzInputs.traitValue); + assertEq(substandard5Comparison.traitKeys[1], bytes32(uint256(421))); + assertEq(substandard5Comparison.traitValues[1], bytes32(uint256(70))); + assertEq(substandard5Comparison.token, context.fuzzInputs.token); + assertEq(substandard5Comparison.identifier, context.fuzzInputs.tokenId); + assertEq(substandard5Comparison.traits, context.fuzzInputs.token2); + } + + function decodeSubstandard1Efficient(bytes calldata encodedData) + external + pure + returns (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) + { + return SIP15Decoder.decodeSubstandard1Efficient(encodedData); + } + + function decodeSubstandard1(bytes calldata encodedData) + external + pure + returns (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) + { + return SIP15Decoder.decodeSubstandard1(encodedData); + } + + function decodeSubstandard2(bytes calldata encodedData) + external + pure + returns (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) + { + return SIP15Decoder.decodeSubstandard2(encodedData); + } + + function decodeSubstandard3(bytes calldata encodedData) + external + pure + returns (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) + { + return SIP15Decoder.decodeSubstandard3(encodedData); + } + + function decodeSubstandard4(bytes calldata encodedData) + external + pure + returns (uint8 comparisonEnum, address token, uint256[] memory ids, bytes32 traitValue, bytes32 traitKey) + { + return SIP15Decoder.decodeSubstandard4(encodedData); + } + + function decodeSubstandard5(bytes calldata encodedData) + external + pure + returns (Substandard5Comparison memory substandard5Comparison) + { + return SIP15Decoder.decodeSubstandard5(encodedData); + } +} diff --git a/test/sips/lib/SIP15Encoder.t.sol b/test/sips/lib/SIP15Encoder.t.sol new file mode 100644 index 0000000..a0f76eb --- /dev/null +++ b/test/sips/lib/SIP15Encoder.t.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.24; + +import {SIP15Encoder, Substandard5Comparison} from '../../src/sips/SIP15Encoder.sol'; +import {ZoneParameters} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import { + ConsiderationItemLib, + OfferItemLib, + OrderComponentsLib, + OrderParametersLib, + OrderLib +} from 'seaport-sol/src/lib/SeaportStructLib.sol'; + +import { + ConsiderationItem, + OrderParameters, + ItemType, + OfferItem, + Order, + OrderComponents, + OrderType +} from 'seaport-types/src/lib/ConsiderationStructs.sol'; +import {ConsiderationInterface} from 'seaport-types/src/interfaces/ConsiderationInterface.sol'; +import {CreateZoneParams} from './CreateZoneParams.sol'; + +contract SIP15Encoder_Unit_test is CreateZoneParams { + using OfferItemLib for OfferItem; + using OfferItemLib for OfferItem[]; + using ConsiderationItemLib for ConsiderationItem; + using ConsiderationItemLib for ConsiderationItem[]; + using OrderComponentsLib for OrderComponents; + using OrderParametersLib for OrderParameters; + using OrderLib for Order; + using OrderLib for Order[]; + + function setUp() public { + // create a default offerItem for a single 721; + // note that it does not have token or identifier set + OfferItemLib.empty().withItemType(ItemType.ERC721).withStartAmount(1).withEndAmount(1).saveDefault(SINGLE_721); + + ConsiderationItemLib.empty().withItemType(ItemType.ERC721).withStartAmount(1).withEndAmount(1).saveDefault( + SINGLE_721 + ); + + OrderComponentsLib.empty().withOrderType(OrderType.FULL_RESTRICTED).withStartTime(block.timestamp).withEndTime( + block.timestamp + 10 + ).withSalt(0).saveDefault(SINGLE_721_Order); + } + + function test_EncodeSubstandard1EfficientFuzz(Context memory context) public view { + ZoneParameters memory zoneParams = _createZoneParams(context); + this.encodeSubstandard1Efficient(zoneParams, context.fuzzInputs.traitKey); + } + + function test_EncodeSubstandard1(Context memory context) public view { + ZoneParameters memory zoneParams = _createZoneParams(context); + this.encodeSubstandard1( + zoneParams, context.fuzzInputs.comparisonEnum, context.fuzzInputs.traitValue, context.fuzzInputs.traitKey + ); + } + + function test_EncodeSubstandard2(Context memory context) public view { + ZoneParameters memory zoneParams = _createZoneParams(context); + this.encodeSubstandard1( + zoneParams, context.fuzzInputs.comparisonEnum, context.fuzzInputs.traitValue, context.fuzzInputs.traitKey + ); + } + + function test_EncodeSubstandard3(Context memory context) public view { + this.encodeSubstandard3( + context.fuzzInputs.comparisonEnum, + context.fuzzInputs.token, + context.fuzzInputs.tokenId, + context.fuzzInputs.traitValue, + context.fuzzInputs.traitKey + ); + } + + function test_EncodeSubstandarad5(Context memory context) public view { + this.encodeSubstandard5( + context.fuzzInputs.comparisonEnum, + context.fuzzInputs.token, + context.fuzzInputs.token2, + context.fuzzInputs.tokenId, + context.fuzzInputs.traitValue, + context.fuzzInputs.traitKey + ); + } + + function encodeSubstandard1Efficient(ZoneParameters calldata zoneParams, bytes32 _traitKey) public view { + bytes memory encodedData = SIP15Encoder.encodeSubstandard1Efficient(zoneParams, _traitKey); + uint8 substandard = uint8(this.decodeSubstandardVersion(encodedData, 0)); + + bytes memory trimmedData = this.trimSubstandard(encodedData); + + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + abi.decode(trimmedData, (uint8, address, uint256, bytes32, bytes32)); + assertEq(substandard, 1); + assertEq(comparisonEnum, 0); + assertEq(traitKey, _traitKey); + assertEq(traitValue, bytes32(0)); + assertEq(token, zoneParams.consideration[0].token); + assertEq(id, zoneParams.consideration[0].identifier); + } + + function encodeSubstandard1( + ZoneParameters calldata zoneParams, + uint8 _comparisonEnum, + bytes32 _traitValue, + bytes32 _traitKey + ) public view { + bytes memory encodedData = SIP15Encoder.encodeSubstandard1(zoneParams, _comparisonEnum, _traitValue, _traitKey); + uint8 substandard = uint8(this.decodeSubstandardVersion(encodedData, 0)); + + bytes memory trimmedData = this.trimSubstandard(encodedData); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + abi.decode(trimmedData, (uint8, address, uint256, bytes32, bytes32)); + + assertEq(substandard, 1); + assertEq(comparisonEnum, _comparisonEnum); + assertEq(traitKey, _traitKey); + assertEq(traitValue, _traitValue); + assertEq(token, zoneParams.offer[0].token); + assertEq(id, zoneParams.offer[0].identifier); + } + + function encodeSubstandard2( + ZoneParameters calldata zoneParams, + uint8 _comparisonEnum, + bytes32 _traitValue, + bytes32 _traitKey + ) public view { + bytes memory encodedData = SIP15Encoder.encodeSubstandard1(zoneParams, _comparisonEnum, _traitValue, _traitKey); + uint8 substandard = uint8(this.decodeSubstandardVersion(encodedData, 0)); + + bytes memory trimmedData = this.trimSubstandard(encodedData); + (uint8 comparisonEnum, address token, uint256 id, bytes32 traitValue, bytes32 traitKey) = + abi.decode(trimmedData, (uint8, address, uint256, bytes32, bytes32)); + + assertEq(substandard, 1); + assertEq(comparisonEnum, _comparisonEnum); + assertEq(traitKey, _traitKey); + assertEq(traitValue, _traitValue); + assertEq(token, zoneParams.consideration[0].token); + assertEq(id, zoneParams.consideration[0].identifier); + } + + function encodeSubstandard3( + uint8 _comparisonEnum, + address _token, + uint256 _identifier, + bytes32 _traitValue, + bytes32 _traitKey + ) public view { + bytes memory encodedData = + SIP15Encoder.encodeSubstandard3(_comparisonEnum, _token, _identifier, _traitValue, _traitKey); + uint8 substandard = uint8(this.decodeSubstandardVersion(encodedData, 0)); + + bytes memory trimmedData = this.trimSubstandard(encodedData); + (uint8 comparisonEnum, address token, uint256 identifier, bytes32 traitValue, bytes32 traitKey) = + abi.decode(trimmedData, (uint8, address, uint256, bytes32, bytes32)); + + assertEq(substandard, 3); + assertEq(comparisonEnum, _comparisonEnum); + assertEq(traitKey, _traitKey); + assertEq(traitValue, _traitValue); + assertEq(token, _token); + assertEq(identifier, _identifier); + } + + function encodeSubstandard5( + uint8 _comparisonEnum, + address _token, + address _traits, + uint256 _identifier, + bytes32 _traitValue, + bytes32 _traitKey + ) public view { + uint8[] memory _compEnums = new uint8[](2); + _compEnums[0] = _comparisonEnum; + _compEnums[1] = 70; + bytes32[] memory _traitValues = new bytes32[](2); + _traitValues[0] = _traitValue; + _traitValues[1] = bytes32(uint256(70)); + + bytes32[] memory _traitKeys = new bytes32[](2); + _traitKeys[0] = _traitKey; + _traitKeys[1] = bytes32(uint256(421)); + + Substandard5Comparison memory comparison = Substandard5Comparison({ + comparisonEnums: _compEnums, + token: _token, + traits: _traits, + identifier: _identifier, + traitValues: _traitValues, + traitKeys: _traitKeys + }); + bytes memory encodedData = SIP15Encoder.encodeSubstandard5(comparison); + uint8 substandard = uint8(this.decodeSubstandardVersion(encodedData, 0)); + bytes memory trimmedData = this.trimSubstandard(encodedData); + (Substandard5Comparison memory returnComp) = abi.decode(trimmedData, (Substandard5Comparison)); + + assertEq(substandard, 5); + assertEq(returnComp.comparisonEnums[0], _comparisonEnum); + assertEq(returnComp.traitKeys[0], _traitKey); + assertEq(returnComp.traitValues[0], _traitValue); + assertEq(returnComp.token, _token); + assertEq(returnComp.identifier, _identifier); + } + + function trimSubstandard(bytes calldata dataToTrim) external pure returns (bytes memory data) { + data = dataToTrim[1:]; + } + + function decodeSubstandardVersion( + bytes calldata extraData, + uint256 sipDataStartRelativeOffset + ) external pure returns (bytes1 versionByte) { + assembly { + versionByte := shr(248, calldataload(add(extraData.offset, sipDataStartRelativeOffset))) + versionByte := or(versionByte, iszero(versionByte)) + versionByte := shl(248, versionByte) + } + } + //use fuzz inputs to create some zone params to test the encoder. +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +