From 0b9e9e68d10ef3b384e7ca054b4748ed3a33ed71 Mon Sep 17 00:00:00 2001 From: Kingster Date: Sun, 14 Apr 2024 12:40:59 -0700 Subject: [PATCH 1/3] add uri to PILTerms --- .../modules/licensing/ILicenseTemplate.sol | 5 +++ .../modules/licensing/IPILicenseTemplate.sol | 2 + contracts/lib/PILFlavors.sol | 12 ++++-- .../modules/licensing/PILicenseTemplate.sol | 10 +++++ .../big-bang/SingleNftCollection.t.sol | 3 +- .../mocks/module/MockLicenseTemplate.sol | 7 ++++ .../modules/licensing/LicensingModule.t.sol | 18 ++++++--- .../modules/licensing/PILicenseTemplate.t.sol | 38 ++++++++++++++++++- test/foundry/utils/LicensingHelper.t.sol | 6 ++- 9 files changed, 87 insertions(+), 14 deletions(-) diff --git a/contracts/interfaces/modules/licensing/ILicenseTemplate.sol b/contracts/interfaces/modules/licensing/ILicenseTemplate.sol index 40cd596da..e6bf638f1 100644 --- a/contracts/interfaces/modules/licensing/ILicenseTemplate.sol +++ b/contracts/interfaces/modules/licensing/ILicenseTemplate.sol @@ -33,6 +33,11 @@ interface ILicenseTemplate is IERC165 { /// @return The metadata URI of the license template. function getMetadataURI() external view returns (string memory); + /// @notice Returns the URI of the license terms. + /// @param licenseTermsId The ID of the license terms. + /// @return The URI of the license terms. + function getLicenseTermsURI(uint256 licenseTermsId) external view returns (string memory); + /// @notice Returns the total number of registered license terms. /// @return The total number of registered license terms. function totalRegisteredLicenseTerms() external view returns (uint256); diff --git a/contracts/interfaces/modules/licensing/IPILicenseTemplate.sol b/contracts/interfaces/modules/licensing/IPILicenseTemplate.sol index 299a8edd7..0e6e0cb6e 100644 --- a/contracts/interfaces/modules/licensing/IPILicenseTemplate.sol +++ b/contracts/interfaces/modules/licensing/IPILicenseTemplate.sol @@ -24,6 +24,7 @@ import { ILicenseTemplate } from "../../../interfaces/modules/licensing/ILicense /// same terms or not. /// @param derivativeRevCelling The maximum revenue that can be generated from the derivative use of the work. /// @param currency The ERC20 token to be used to pay the minting fee. the token must be registered in story protocol. +/// @param uri The URI of the license terms, which can be used to fetch the offchain license terms. struct PILTerms { bool transferable; address royaltyPolicy; @@ -41,6 +42,7 @@ struct PILTerms { bool derivativesReciprocal; uint256 derivativeRevCelling; address currency; + string uri; } /// @title IPILicenseTemplate diff --git a/contracts/lib/PILFlavors.sol b/contracts/lib/PILFlavors.sol index a0f96a1af..108d51781 100644 --- a/contracts/lib/PILFlavors.sol +++ b/contracts/lib/PILFlavors.sol @@ -115,7 +115,8 @@ library PILFlavors { derivativesApproval: false, derivativesReciprocal: false, derivativeRevCelling: 0, - currency: address(0) + currency: address(0), + uri: "" }); } @@ -138,7 +139,8 @@ library PILFlavors { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0) + currency: address(0), + uri: "" }); } @@ -165,7 +167,8 @@ library PILFlavors { derivativesApproval: false, derivativesReciprocal: false, derivativeRevCelling: 0, - currency: currencyToken + currency: currencyToken, + uri: "" }); } @@ -193,7 +196,8 @@ library PILFlavors { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: currencyToken + currency: currencyToken, + uri: "" }); } } diff --git a/contracts/modules/licensing/PILicenseTemplate.sol b/contracts/modules/licensing/PILicenseTemplate.sol index f82faf0a1..e84a39d1f 100644 --- a/contracts/modules/licensing/PILicenseTemplate.sol +++ b/contracts/modules/licensing/PILicenseTemplate.sol @@ -279,6 +279,13 @@ contract PILicenseTemplate is return $.licenseTerms[selectedLicenseTermsId]; } + /// @notice Returns the URI of the license terms. + /// @param licenseTermsId The ID of the license terms. + /// @return The URI of the license terms. + function getLicenseTermsURI(uint256 licenseTermsId) external view returns (string memory) { + return _getPILicenseTemplateStorage().licenseTerms[licenseTermsId].uri; + } + /// @notice Returns the total number of registered license terms. /// @return The total number of registered license terms. function totalRegisteredLicenseTerms() external view returns (uint256) { @@ -312,6 +319,9 @@ contract PILicenseTemplate is '{"trait_type": "Currency", "value": "', terms.currency.toHexString(), '"},', + '{"trait_type": "URI", "value": "', + terms.uri, + '"},', // Skip transferable, it's already added in the common attributes by the LicenseRegistry. _policyCommercialTraitsToJson(terms), _policyDerivativeTraitsToJson(terms) diff --git a/test/foundry/integration/big-bang/SingleNftCollection.t.sol b/test/foundry/integration/big-bang/SingleNftCollection.t.sol index db5b4a608..067554a29 100644 --- a/test/foundry/integration/big-bang/SingleNftCollection.t.sol +++ b/test/foundry/integration/big-bang/SingleNftCollection.t.sol @@ -57,7 +57,8 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration { derivativesApproval: false, derivativesReciprocal: false, derivativeRevCelling: 0, - currency: address(erc20) + currency: address(erc20), + uri: "" }) ); } diff --git a/test/foundry/mocks/module/MockLicenseTemplate.sol b/test/foundry/mocks/module/MockLicenseTemplate.sol index 974bd2ecd..430fe2599 100644 --- a/test/foundry/mocks/module/MockLicenseTemplate.sol +++ b/test/foundry/mocks/module/MockLicenseTemplate.sol @@ -142,4 +142,11 @@ contract MockLicenseTemplate is BaseLicenseTemplateUpgradeable { function toJson(uint256 licenseTermsId) public view returns (string memory) { return ""; } + + /// @notice Returns the URI of the license terms. + /// @param licenseTermsId The ID of the license terms. + /// @return The URI of the license terms. + function getLicenseTermsURI(uint256 licenseTermsId) external view returns (string memory) { + return ""; + } } diff --git a/test/foundry/modules/licensing/LicensingModule.t.sol b/test/foundry/modules/licensing/LicensingModule.t.sol index 40c08d8bf..9762bfe21 100644 --- a/test/foundry/modules/licensing/LicensingModule.t.sol +++ b/test/foundry/modules/licensing/LicensingModule.t.sol @@ -186,7 +186,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); @@ -434,7 +435,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); @@ -758,7 +760,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); @@ -1055,7 +1058,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); @@ -1104,7 +1108,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); @@ -1150,7 +1155,8 @@ contract LicensingModuleTest is BaseTest { derivativesApproval: false, derivativesReciprocal: true, derivativeRevCelling: 0, - currency: address(0x123) + currency: address(0x123), + uri: "" }); uint256 termsId = pilTemplate.registerLicenseTerms(terms); diff --git a/test/foundry/modules/licensing/PILicenseTemplate.t.sol b/test/foundry/modules/licensing/PILicenseTemplate.t.sol index ff83ecaf6..ddaccba69 100644 --- a/test/foundry/modules/licensing/PILicenseTemplate.t.sol +++ b/test/foundry/modules/licensing/PILicenseTemplate.t.sol @@ -497,6 +497,42 @@ contract PILicenseTemplateTest is BaseTest { assertFalse(pilTemplate.isLicenseTransferable(nonTransferableTermsId)); } + function test_PILicenseTemplate_getLicenseURI() public { + PILTerms memory terms = PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(erc20), + royaltyPolicy: address(royaltyPolicyLAP) + }); + terms.uri = "license.url"; + uint256 termsId = pilTemplate.registerLicenseTerms(terms); + assertEq(pilTemplate.getLicenseTermsURI(termsId), "license.url"); + } + + function test_PILicenseTemplate_differentLicenseURI() public { + PILTerms memory terms = PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(erc20), + royaltyPolicy: address(royaltyPolicyLAP) + }); + terms.uri = "license.url"; + uint256 termsId = pilTemplate.registerLicenseTerms(terms); + assertEq(pilTemplate.getLicenseTermsURI(termsId), "license.url"); + + PILTerms memory terms1 = PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(erc20), + royaltyPolicy: address(royaltyPolicyLAP) + }); + terms1.uri = "another.license.url"; + uint256 termsId1 = pilTemplate.registerLicenseTerms(terms1); + + assertEq(pilTemplate.getLicenseTermsURI(termsId1), "another.license.url"); + assertEq(pilTemplate.getLicenseTermsURI(termsId), "license.url"); + assertEq(pilTemplate.getLicenseTermsId(terms1), termsId1); + assertEq(pilTemplate.getLicenseTermsId(terms), termsId); + } + + function test_PILicenseTemplate_getEarlierExpiredTime_WithEmptyLicenseTerms() public { uint256[] memory licenseTermsIds = new uint256[](0); assertEq(pilTemplate.getEarlierExpireTime(licenseTermsIds, block.timestamp), 0); @@ -520,7 +556,7 @@ contract PILicenseTemplateTest is BaseTest { function _DefaultToJson() internal pure returns (string memory) { /* solhint-disable */ return - '{"trait_type": "Expiration", "value": "never"},{"trait_type": "Currency", "value": "0x0000000000000000000000000000000000000000"},{"trait_type": "Commercial Use", "value": "false"},{"trait_type": "Commercial Attribution", "value": "false"},{"trait_type": "Commercial Revenue Share", "max_value": 1000, "value": 0},{"trait_type": "Commercial Revenue Celling", "value": 0},{"trait_type": "Commercializer Check", "value": "0x0000000000000000000000000000000000000000"},{"trait_type": "Derivatives Allowed", "value": "false"},{"trait_type": "Derivatives Attribution", "value": "false"},{"trait_type": "Derivatives Revenue Celling", "value": 0},{"trait_type": "Derivatives Approval", "value": "false"},{"trait_type": "Derivatives Reciprocal", "value": "false"},'; + '{"trait_type": "Expiration", "value": "never"},{"trait_type": "Currency", "value": "0x0000000000000000000000000000000000000000"},{"trait_type": "URI", "value": ""},{"trait_type": "Commercial Use", "value": "false"},{"trait_type": "Commercial Attribution", "value": "false"},{"trait_type": "Commercial Revenue Share", "max_value": 1000, "value": 0},{"trait_type": "Commercial Revenue Celling", "value": 0},{"trait_type": "Commercializer Check", "value": "0x0000000000000000000000000000000000000000"},{"trait_type": "Derivatives Allowed", "value": "false"},{"trait_type": "Derivatives Attribution", "value": "false"},{"trait_type": "Derivatives Revenue Celling", "value": 0},{"trait_type": "Derivatives Approval", "value": "false"},{"trait_type": "Derivatives Reciprocal", "value": "false"},'; /* solhint-enable */ } } diff --git a/test/foundry/utils/LicensingHelper.t.sol b/test/foundry/utils/LicensingHelper.t.sol index ba2e2abc3..8150accf5 100644 --- a/test/foundry/utils/LicensingHelper.t.sol +++ b/test/foundry/utils/LicensingHelper.t.sol @@ -98,7 +98,8 @@ contract LicensingHelper { derivativesApproval: false, derivativesReciprocal: reciprocal, derivativeRevCelling: 0, - currency: address(erc20) + currency: address(erc20), + uri: "" }); } @@ -124,7 +125,8 @@ contract LicensingHelper { derivativesApproval: false, derivativesReciprocal: reciprocal, derivativeRevCelling: 0, - currency: address(0) + currency: address(0), + uri: "" }); } From 578e73ed3ea0baeba326f1e16f34fea7d4f1a86f Mon Sep 17 00:00:00 2001 From: Kingster Date: Sun, 14 Apr 2024 12:45:34 -0700 Subject: [PATCH 2/3] fix lint --- test/foundry/access/AccessController.t.sol | 2 +- test/foundry/modules/licensing/PILicenseTemplate.t.sol | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/foundry/access/AccessController.t.sol b/test/foundry/access/AccessController.t.sol index 7e2b76b18..cae63d8b4 100644 --- a/test/foundry/access/AccessController.t.sol +++ b/test/foundry/access/AccessController.t.sol @@ -1646,7 +1646,7 @@ contract AccessControllerTest is BaseTest { bytes4(0), AccessPermission.ALLOW ); - + vm.stopPrank(); vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector)); vm.prank(owner); diff --git a/test/foundry/modules/licensing/PILicenseTemplate.t.sol b/test/foundry/modules/licensing/PILicenseTemplate.t.sol index ddaccba69..527d3e21a 100644 --- a/test/foundry/modules/licensing/PILicenseTemplate.t.sol +++ b/test/foundry/modules/licensing/PILicenseTemplate.t.sol @@ -532,7 +532,6 @@ contract PILicenseTemplateTest is BaseTest { assertEq(pilTemplate.getLicenseTermsId(terms), termsId); } - function test_PILicenseTemplate_getEarlierExpiredTime_WithEmptyLicenseTerms() public { uint256[] memory licenseTermsIds = new uint256[](0); assertEq(pilTemplate.getEarlierExpireTime(licenseTermsIds, block.timestamp), 0); From acfe889ca0cd3be5a2f21f00e7d95d7dae680163 Mon Sep 17 00:00:00 2001 From: Kingster Date: Sun, 14 Apr 2024 15:13:03 -0700 Subject: [PATCH 3/3] reduce build size --- .../modules/licensing/PILicenseTemplate.sol | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/contracts/modules/licensing/PILicenseTemplate.sol b/contracts/modules/licensing/PILicenseTemplate.sol index e84a39d1f..195f5b067 100644 --- a/contracts/modules/licensing/PILicenseTemplate.sol +++ b/contracts/modules/licensing/PILicenseTemplate.sol @@ -100,6 +100,7 @@ contract PILicenseTemplate is bytes32 hashedLicense = keccak256(abi.encode(terms)); PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); id = $.hashedLicenseTerms[hashedLicense]; + // license id start from 1 if (id != 0) { return id; } @@ -114,8 +115,7 @@ contract PILicenseTemplate is /// @param licenseTermsId The ID of the license terms. /// @return True if the license terms exists, false otherwise. function exists(uint256 licenseTermsId) external view override returns (bool) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - return licenseTermsId <= $.licenseTermsCounter; + return licenseTermsId <= _getPILicenseTemplateStorage().licenseTermsCounter; } /// @notice Verifies the minting of a license token. @@ -131,8 +131,7 @@ contract PILicenseTemplate is address licensorIpId, uint256 ) external override nonReentrant returns (bool) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - PILTerms memory terms = $.licenseTerms[licenseTermsId]; + PILTerms memory terms = _getPILicenseTemplateStorage().licenseTerms[licenseTermsId]; // If the policy defines no reciprocal derivatives are allowed (no derivatives of derivatives), // and we are mintingFromADerivative we don't allow minting if (LICENSE_REGISTRY.isDerivativeIp(licensorIpId)) { @@ -220,8 +219,7 @@ contract PILicenseTemplate is function getRoyaltyPolicy( uint256 licenseTermsId ) external view returns (address royaltyPolicy, bytes memory royaltyData, uint256 mintingFee, address currency) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - PILTerms memory terms = $.licenseTerms[licenseTermsId]; + PILTerms memory terms = _getPILicenseTemplateStorage().licenseTerms[licenseTermsId]; return (terms.royaltyPolicy, abi.encode(terms.commercialRevShare), terms.mintingFee, terms.currency); } @@ -229,8 +227,7 @@ contract PILicenseTemplate is /// @param licenseTermsId The ID of the license terms. /// @return True if the license terms is transferable, false otherwise. function isLicenseTransferable(uint256 licenseTermsId) external view override returns (bool) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - return $.licenseTerms[licenseTermsId].transferable; + return _getPILicenseTemplateStorage().licenseTerms[licenseTermsId].transferable; } /// @notice Returns the earliest expiration time among the given license terms. @@ -266,17 +263,14 @@ contract PILicenseTemplate is /// @param terms The PILTerms to get the ID for. /// @return selectedLicenseTermsId The ID of the given license terms. function getLicenseTermsId(PILTerms calldata terms) external view returns (uint256 selectedLicenseTermsId) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - bytes32 licenseTermsHash = keccak256(abi.encode(terms)); - return $.hashedLicenseTerms[licenseTermsHash]; + return _getPILicenseTemplateStorage().hashedLicenseTerms[keccak256(abi.encode(terms))]; } /// @notice Gets license terms of the given ID. /// @param selectedLicenseTermsId The ID of the license terms. /// @return terms The PILTerms associate with the given ID. function getLicenseTerms(uint256 selectedLicenseTermsId) external view returns (PILTerms memory terms) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - return $.licenseTerms[selectedLicenseTermsId]; + return _getPILicenseTemplateStorage().licenseTerms[selectedLicenseTermsId]; } /// @notice Returns the URI of the license terms. @@ -289,8 +283,7 @@ contract PILicenseTemplate is /// @notice Returns the total number of registered license terms. /// @return The total number of registered license terms. function totalRegisteredLicenseTerms() external view returns (uint256) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - return $.licenseTermsCounter; + return _getPILicenseTemplateStorage().licenseTermsCounter; } /// @notice checks the contract whether supports the given interface. @@ -305,8 +298,7 @@ contract PILicenseTemplate is /// @param licenseTermsId The ID of the license terms. /// @return The JSON string of the license terms, follow the OpenSea metadata standard. function toJson(uint256 licenseTermsId) public view returns (string memory) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - PILTerms memory terms = $.licenseTerms[licenseTermsId]; + PILTerms memory terms = _getPILicenseTemplateStorage().licenseTerms[licenseTermsId]; /* solhint-disable */ // Follows the OpenSea standard for JSON metadata. @@ -441,8 +433,7 @@ contract PILicenseTemplate is uint256 licenseTermsId, address licensee ) internal returns (bool) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - PILTerms memory terms = $.licenseTerms[licenseTermsId]; + PILTerms memory terms = _getPILicenseTemplateStorage().licenseTerms[licenseTermsId]; if (!terms.derivativesAllowed) { return false; @@ -466,28 +457,20 @@ contract PILicenseTemplate is /// @dev Verifies if the license terms are compatible. function _verifyCompatibleLicenseTerms(uint256[] calldata licenseTermsIds) internal view returns (bool) { - if (licenseTermsIds.length < 2) { - return true; - } + if (licenseTermsIds.length < 2) return true; PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); bool commercial = $.licenseTerms[licenseTermsIds[0]].commercialUse; bool derivativesReciprocal = $.licenseTerms[licenseTermsIds[0]].derivativesReciprocal; for (uint256 i = 1; i < licenseTermsIds.length; i++) { - PILTerms memory terms = $.licenseTerms[licenseTermsIds[i]]; - if (terms.commercialUse != commercial) { - return false; - } - if (terms.derivativesReciprocal != derivativesReciprocal) { - return false; - } + if ($.licenseTerms[licenseTermsIds[i]].commercialUse != commercial) return false; + if ($.licenseTerms[licenseTermsIds[i]].derivativesReciprocal != derivativesReciprocal) return false; } return true; } /// @dev Calculate and returns the expiration time based given start time and license terms. function _getExpireTime(uint256 licenseTermsId, uint256 start) internal view returns (uint) { - PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage(); - PILTerms memory terms = $.licenseTerms[licenseTermsId]; + PILTerms memory terms = _getPILicenseTemplateStorage().licenseTerms[licenseTermsId]; if (terms.expiration == 0) { return 0; }