From b0fc4e899a1359181ce64b976d9d9c54d7543aeb Mon Sep 17 00:00:00 2001 From: Aggre Date: Mon, 5 Aug 2024 12:13:32 +0900 Subject: [PATCH 1/2] add `animation_url` property to the STokens metadata --- contracts/interface/ITokenURIDescriptor.sol | 17 ++++++++ contracts/src/s-token/STokensDescriptor.sol | 40 +++++++++++++++---- contracts/src/s-token/STokensManager.sol | 31 ++++++++++---- .../stoken/TokenURIDescriptorCopyTest.sol | 11 +++++ .../test/stoken/TokenURIDescriptorTest.sol | 11 +++++ yarn.lock | 2 +- 6 files changed, 97 insertions(+), 15 deletions(-) diff --git a/contracts/interface/ITokenURIDescriptor.sol b/contracts/interface/ITokenURIDescriptor.sol index ecf74b51..576eb51d 100644 --- a/contracts/interface/ITokenURIDescriptor.sol +++ b/contracts/interface/ITokenURIDescriptor.sol @@ -22,6 +22,23 @@ interface ITokenURIDescriptor { bytes32 _payload ) external view returns (string memory); + /* + * @dev get animation_url from custom descriptor + * @param _tokenId token id + * @param _owner owner address + * @param _positions staking position + * @param _rewards rewards + * @param _payload token payload + * @return string image information + */ + function animationUrl( + uint256 _tokenId, + address _owner, + ISTokensManager.StakingPositions memory _positions, + ISTokensManager.Rewards memory _rewards, + bytes32 _payload + ) external view returns (string memory); + /* * @dev get name from custom descriptor * @param _tokenId token id diff --git a/contracts/src/s-token/STokensDescriptor.sol b/contracts/src/s-token/STokensDescriptor.sol index 876f7bef..816771aa 100644 --- a/contracts/src/s-token/STokensDescriptor.sol +++ b/contracts/src/s-token/STokensDescriptor.sol @@ -170,14 +170,30 @@ contract STokensDescriptor { } function getTokenURI( - address _property, - uint256 _amount, - uint256 _cumulativeReward, - string memory _tokenUriImage, - string memory _tokenUriName, - string memory _tokenUriDescription, - bytes32 _payload + bytes memory _props ) internal pure returns (string memory) { + ( + address _property, + uint256 _amount, + uint256 _cumulativeReward, + string memory _tokenUriImage, + string memory _tokenUriName, + string memory _tokenUriDescription, + string memory _tokenUriAnimationUrl, + bytes32 _payload + ) = abi.decode( + _props, + ( + address, + uint256, + uint256, + string, + string, + string, + string, + bytes32 + ) + ); string memory amount = _amount.decimalString(18); string memory name = bytes(_tokenUriName).length == 0 ? string( @@ -244,6 +260,15 @@ contract STokensDescriptor { ) ) : _tokenUriImage; + string memory animationUrl = bytes(_tokenUriAnimationUrl).length == 0 + ? string("") + : string( + abi.encodePacked( + // solhint-disable-next-line quotes + '", "animation_url":"', + _tokenUriAnimationUrl + ) + ); return string( abi.encodePacked( @@ -259,6 +284,7 @@ contract STokensDescriptor { // solhint-disable-next-line quotes '", "image": "', image, + animationUrl, // solhint-disable-next-line quotes '", "attributes":', attributes, diff --git a/contracts/src/s-token/STokensManager.sol b/contracts/src/s-token/STokensManager.sol index e34d0c51..e1607805 100644 --- a/contracts/src/s-token/STokensManager.sol +++ b/contracts/src/s-token/STokensManager.sol @@ -305,6 +305,7 @@ contract STokensManager is string memory _tokenUriImage = tokenUriImage[_tokenId]; string memory _tokenUriName; string memory _tokenUriDescription; + string memory _tokenUriAnimationUrl; address descriptor = descriptorOfPropertyByPayload[_positions.property][ _payload ] == address(0) @@ -352,16 +353,32 @@ contract STokensManager is } catch { _tokenUriDescription = ""; } + try + ITokenURIDescriptor(descriptor).animationUrl( + _tokenId, + _owner, + _positions, + _rewardsArg, + _payload + ) + returns (string memory _animation_url) { + _tokenUriAnimationUrl = _animation_url; + } catch { + _tokenUriAnimationUrl = ""; + } } return getTokenURI( - _positions.property, - _positions.amount, - _positions.cumulativeReward, - _tokenUriImage, - _tokenUriName, - _tokenUriDescription, - _payload + abi.encode( + _positions.property, + _positions.amount, + _positions.cumulativeReward, + _tokenUriImage, + _tokenUriName, + _tokenUriDescription, + _tokenUriAnimationUrl, + _payload + ) ); } diff --git a/contracts/test/stoken/TokenURIDescriptorCopyTest.sol b/contracts/test/stoken/TokenURIDescriptorCopyTest.sol index 06a4d364..35264b71 100644 --- a/contracts/test/stoken/TokenURIDescriptorCopyTest.sol +++ b/contracts/test/stoken/TokenURIDescriptorCopyTest.sol @@ -9,6 +9,7 @@ contract TokenURIDescriptorCopyTest is ITokenURIDescriptor { string public newName = ""; string public newDescription = ""; string public newImage = "dummy-string"; + string public newAnimationUrl = "dummy-string"; function image( uint256, @@ -20,6 +21,16 @@ contract TokenURIDescriptorCopyTest is ITokenURIDescriptor { return newImage; } + function animationUrl( + uint256, + address, + ISTokensManager.StakingPositions memory, + ISTokensManager.Rewards memory, + bytes32 + ) external view override returns (string memory) { + return newAnimationUrl; + } + function onBeforeMint( uint256 _tokenId, address, diff --git a/contracts/test/stoken/TokenURIDescriptorTest.sol b/contracts/test/stoken/TokenURIDescriptorTest.sol index 6e7c728d..31bc402a 100644 --- a/contracts/test/stoken/TokenURIDescriptorTest.sol +++ b/contracts/test/stoken/TokenURIDescriptorTest.sol @@ -9,6 +9,7 @@ contract TokenURIDescriptorTest is ITokenURIDescriptor { string public newName = ""; string public newDescription = ""; string public newImage = "dummy-string"; + string public newAnimationUrl = "dummy-string"; function image( uint256, @@ -20,6 +21,16 @@ contract TokenURIDescriptorTest is ITokenURIDescriptor { return newImage; } + function animationUrl( + uint256, + address, + ISTokensManager.StakingPositions memory, + ISTokensManager.Rewards memory, + bytes32 + ) external view returns (string memory) { + return newAnimationUrl; + } + function onBeforeMint( uint256 _tokenId, address, diff --git a/yarn.lock b/yarn.lock index 68b70ac9..b2df9fd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2527,7 +2527,7 @@ version "0.2.0" resolved "https://codeload.github.com/trufflesuite/filecoin-signing-tools-js/tar.gz/2786fdb8a2b71bd1c7789c0701da41bb644a098f" dependencies: - axios "^0.20.0" + axios "0.26.1" base32-decode "^1.0.0" base32-encode "^1.1.1" bip32 "^2.0.5" From 36daea7f5f13ded0b99294234fd54aa970e17b38 Mon Sep 17 00:00:00 2001 From: Aggre Date: Mon, 5 Aug 2024 12:36:05 +0900 Subject: [PATCH 2/2] add test cases for STokensManager --- .../stoken/TokenURIDescriptorCopyTest.sol | 5 +- .../test/stoken/TokenURIDescriptorTest.sol | 6 ++- test/s-token/s-token-manager.ts | 46 +++++++++++++++++-- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/contracts/test/stoken/TokenURIDescriptorCopyTest.sol b/contracts/test/stoken/TokenURIDescriptorCopyTest.sol index 35264b71..5e366353 100644 --- a/contracts/test/stoken/TokenURIDescriptorCopyTest.sol +++ b/contracts/test/stoken/TokenURIDescriptorCopyTest.sol @@ -9,7 +9,6 @@ contract TokenURIDescriptorCopyTest is ITokenURIDescriptor { string public newName = ""; string public newDescription = ""; string public newImage = "dummy-string"; - string public newAnimationUrl = "dummy-string"; function image( uint256, @@ -27,8 +26,8 @@ contract TokenURIDescriptorCopyTest is ITokenURIDescriptor { ISTokensManager.StakingPositions memory, ISTokensManager.Rewards memory, bytes32 - ) external view override returns (string memory) { - return newAnimationUrl; + ) external pure override returns (string memory) { + return ""; } function onBeforeMint( diff --git a/contracts/test/stoken/TokenURIDescriptorTest.sol b/contracts/test/stoken/TokenURIDescriptorTest.sol index 31bc402a..d6a8f949 100644 --- a/contracts/test/stoken/TokenURIDescriptorTest.sol +++ b/contracts/test/stoken/TokenURIDescriptorTest.sol @@ -9,7 +9,7 @@ contract TokenURIDescriptorTest is ITokenURIDescriptor { string public newName = ""; string public newDescription = ""; string public newImage = "dummy-string"; - string public newAnimationUrl = "dummy-string"; + string public newAnimationUrl = "dummy-animation-string"; function image( uint256, @@ -61,6 +61,10 @@ contract TokenURIDescriptorTest is ITokenURIDescriptor { newImage = _imageURI; } + function _setAnimationUrl(string memory _animationUrl) public { + newAnimationUrl = _animationUrl; + } + function name( uint256, address, diff --git a/test/s-token/s-token-manager.ts b/test/s-token/s-token-manager.ts index 670eb13c..0ce26c70 100644 --- a/test/s-token/s-token-manager.ts +++ b/test/s-token/s-token-manager.ts @@ -33,6 +33,7 @@ type Details = { description: string image: string attributes: Attributes + animation_url?: string } contract('STokensManager', ([deployer, user]) => { @@ -117,7 +118,8 @@ contract('STokensManager', ([deployer, user]) => { amount: number | string, cumulativeReward: number, payload: string, - tokenUriImage = '' + tokenUriImage = '', + tokenUriAnimationUrl = '' ): void => { const uriInfo = tokenUri.split(',') expect(uriInfo.length).to.equal(2) @@ -127,9 +129,10 @@ contract('STokensManager', ([deployer, user]) => { name: string description: string image: string + animation_url: string attributes: Attributes } - const { name, description, image } = details + const { name, description, image, animation_url } = details checkName(name, property, amount, cumulativeReward) checkDescription(description, property) checkAttributes(details.attributes, property, amount, payload) @@ -137,6 +140,9 @@ contract('STokensManager', ([deployer, user]) => { tokenUriImage === '' ? checkImage(image, property) : checkTokenImageUri(image, tokenUriImage) + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + tokenUriAnimationUrl !== '' && + checkTokenAnimationUrl(animation_url, tokenUriAnimationUrl) } const getdetails = (tokenUri: string): Details => { @@ -148,6 +154,7 @@ contract('STokensManager', ([deployer, user]) => { name: string description: string image: string + animation_url?: string attributes: Attributes } return details @@ -187,6 +194,13 @@ contract('STokensManager', ([deployer, user]) => { expect(image).to.equal(tokenUriImage) } + const checkTokenAnimationUrl = ( + animationUrl: string, + tokenUriAnimationUrl: string + ): void => { + expect(animationUrl).to.equal(tokenUriAnimationUrl) + } + const checkAttributes = ( attributes: Attributes, property: string, @@ -273,7 +287,7 @@ contract('STokensManager', ([deployer, user]) => { 'ipfs://IPFS-CID' ) }) - it('get descriptor token uri; with custom name & description check', async () => { + it('get descriptor token uri; with custom name, animation_url & description check', async () => { // @ts-ignore await dev.lockup.depositToProperty( property.address, @@ -294,15 +308,37 @@ contract('STokensManager', ([deployer, user]) => { 10000, 0, web3.utils.keccak256('ADDITIONAL_BYTES'), - 'dummy-string' + 'dummy-string', + 'dummy-animation-string' ) // This checks for custom name & description await descriptor._setName('new-name') await descriptor._setDescription('new-description') + await descriptor._setAnimationUrl('new-animation') const recheckURI = await dev.sTokensManager.tokenURI(1) - const { name, description } = getdetails(recheckURI) + const { name, description, animation_url } = getdetails(recheckURI) expect(name).to.equal('new-name') expect(description).to.equal('new-description') + expect(animation_url).to.equal('new-animation') + }) + it('returns tokenURI without animation_url if it is not set', async () => { + // @ts-ignore + await dev.lockup.depositToProperty( + property.address, + '10000', + web3.utils.keccak256('ADDITIONAL_BYTES') + ) + await dev.sTokensManager.setTokenURIDescriptor( + property.address, + descriptor.address, + [web3.utils.keccak256('ADDITIONAL_BYTES')], + { from: user } + ) + await descriptor._setAnimationUrl('') + const { animation_url } = getdetails( + await dev.sTokensManager.tokenURI(1) + ) + expect(animation_url).to.equal(undefined) }) it('get token uri for legacy descriptors; without custom name & description', async () => { // @ts-ignore