diff --git a/src/SablierV2NFTDescriptor.sol b/src/SablierV2NFTDescriptor.sol index 5601cc2e1..0d3486ed2 100644 --- a/src/SablierV2NFTDescriptor.sol +++ b/src/SablierV2NFTDescriptor.sol @@ -90,9 +90,10 @@ contract SablierV2NFTDescriptor is ISablierV2NFTDescriptor { generateDescription({ streamingModel: vars.streamingModel, assetSymbol: vars.assetSymbol, - streamId: streamId.toString(), sablierAddress: vars.sablierAddress, - assetAddress: vars.asset.toHexString() + assetAddress: vars.asset.toHexString(), + streamId: streamId.toString(), + isTransferable: vars.sablier.isTransferable(streamId) }), '","external_url":"https://sablier.com","name":"', generateName({ streamingModel: vars.streamingModel, streamId: streamId.toString() }), @@ -250,14 +251,21 @@ contract SablierV2NFTDescriptor is ISablierV2NFTDescriptor { function generateDescription( string memory streamingModel, string memory assetSymbol, - string memory streamId, string memory sablierAddress, - string memory assetAddress + string memory assetAddress, + string memory streamId, + bool isTransferable ) internal pure returns (string memory) { + // Depending on the transferability of the NFT, declare the relevant information. + string memory info = isTransferable + ? + unicode"⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient." + : unicode"❕INFO: This NFT is non-transferable. It cannot be sold or transferred to another account."; + return string.concat( "This NFT represents a payment stream in a Sablier V2 ", streamingModel, @@ -274,7 +282,7 @@ contract SablierV2NFTDescriptor is ISablierV2NFTDescriptor { " Address: ", assetAddress, "\\n\\n", - unicode"⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient." + info ); } diff --git a/test/mocks/NFTDescriptorMock.sol b/test/mocks/NFTDescriptorMock.sol index 6d93fb7d8..47bcf5aee 100644 --- a/test/mocks/NFTDescriptorMock.sol +++ b/test/mocks/NFTDescriptorMock.sol @@ -54,15 +54,16 @@ contract NFTDescriptorMock is SablierV2NFTDescriptor { function generateDescription_( string memory streamingModel, string memory assetSymbol, - string memory streamId, string memory sablierAddress, - string memory assetAddress + string memory assetAddress, + string memory streamId, + bool isTransferable ) external pure returns (string memory) { - return generateDescription(streamingModel, assetSymbol, streamId, sablierAddress, assetAddress); + return generateDescription(streamingModel, assetSymbol, sablierAddress, assetAddress, streamId, isTransferable); } function generateName_( diff --git a/test/unit/concrete/nft-descriptor/generateDescription.t.sol b/test/unit/concrete/nft-descriptor/generateDescription.t.sol index 8a295e59e..58664a40f 100644 --- a/test/unit/concrete/nft-descriptor/generateDescription.t.sol +++ b/test/unit/concrete/nft-descriptor/generateDescription.t.sol @@ -5,11 +5,13 @@ pragma solidity >=0.8.22 <0.9.0; import { NFTDescriptor_Unit_Concrete_Test } from "./NFTDescriptor.t.sol"; contract GenerateDescription_Unit_Concrete_Test is NFTDescriptor_Unit_Concrete_Test { - string internal constant DISCLAIMER = + string internal constant INFO_NON_TRANSFERABLE = + unicode"❕INFO: This NFT is non-transferable. It cannot be sold or transferred to another account."; + string internal constant INFO_TRANSFERABLE = unicode"⚠️ WARNING: Transferring the NFT makes the new owner the recipient of the stream. The funds are not automatically withdrawn for the previous recipient."; function test_GenerateDescription_Empty() external { - string memory actualDescription = nftDescriptorMock.generateDescription_("", "", "", "", ""); + string memory actualDescription = nftDescriptorMock.generateDescription_("", "", "", "", "", true); string memory expectedDescription = string.concat( "This NFT represents a payment stream in a Sablier V2 ", " contract. The owner of this NFT can withdraw the streamed assets, which are denominated in ", @@ -20,18 +22,50 @@ contract GenerateDescription_Unit_Concrete_Test is NFTDescriptor_Unit_Concrete_T "\\n- ", " Address: ", "\\n\\n", - DISCLAIMER + INFO_TRANSFERABLE ); assertEq(actualDescription, expectedDescription, "metadata description"); } - function test_GenerateDescription() external { + function test_GenerateDescription_NonTransferable() external { string memory actualDescription = nftDescriptorMock.generateDescription_( "Lockup Linear", dai.symbol(), + "0x78B190C1E493752f85E02b00a0C98851A5638A30", + "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", + "42", + false + ); + string memory expectedDescription = string.concat( + "This NFT represents a payment stream in a Sablier V2 ", + "Lockup Linear", + " contract. The owner of this NFT can withdraw the streamed assets, which are denominated in ", + dai.symbol(), + ".\\n\\n", + "- Stream ID: ", "42", + "\\n- ", + "Lockup Linear", + " Address: ", "0x78B190C1E493752f85E02b00a0C98851A5638A30", - "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2" + "\\n- ", + "DAI", + " Address: ", + "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", + "\\n\\n", + INFO_NON_TRANSFERABLE + ); + assertEq(actualDescription, expectedDescription, "metadata description"); + } + + function test_GenerateDescription() external { + string memory actualDescription = nftDescriptorMock.generateDescription_( + "Lockup Linear", + dai.symbol(), + "0x78B190C1E493752f85E02b00a0C98851A5638A30", + "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", + "42", + true ); string memory expectedDescription = string.concat( "This NFT represents a payment stream in a Sablier V2 ", @@ -50,7 +84,7 @@ contract GenerateDescription_Unit_Concrete_Test is NFTDescriptor_Unit_Concrete_T " Address: ", "0xFEbD67A34821d1607a57DD31aae5f246D7dE2ca2", "\\n\\n", - DISCLAIMER + INFO_TRANSFERABLE ); assertEq(actualDescription, expectedDescription, "metadata description"); }