Skip to content

Commit

Permalink
Add Offchain URI Field to PILTerms (#94)
Browse files Browse the repository at this point in the history
* add offchain uri to PILTerms
  • Loading branch information
kingster-will authored Apr 14, 2024
1 parent c26eab5 commit 7343373
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 46 deletions.
5 changes: 5 additions & 0 deletions contracts/interfaces/modules/licensing/ILicenseTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/modules/licensing/IPILicenseTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,6 +42,7 @@ struct PILTerms {
bool derivativesReciprocal;
uint256 derivativeRevCelling;
address currency;
string uri;
}

/// @title IPILicenseTemplate
Expand Down
12 changes: 8 additions & 4 deletions contracts/lib/PILFlavors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ library PILFlavors {
derivativesApproval: false,
derivativesReciprocal: false,
derivativeRevCelling: 0,
currency: address(0)
currency: address(0),
uri: ""
});
}

Expand All @@ -138,7 +139,8 @@ library PILFlavors {
derivativesApproval: false,
derivativesReciprocal: true,
derivativeRevCelling: 0,
currency: address(0)
currency: address(0),
uri: ""
});
}

Expand All @@ -165,7 +167,8 @@ library PILFlavors {
derivativesApproval: false,
derivativesReciprocal: false,
derivativeRevCelling: 0,
currency: currencyToken
currency: currencyToken,
uri: ""
});
}

Expand Down Expand Up @@ -193,7 +196,8 @@ library PILFlavors {
derivativesApproval: false,
derivativesReciprocal: true,
derivativeRevCelling: 0,
currency: currencyToken
currency: currencyToken,
uri: ""
});
}
}
55 changes: 24 additions & 31 deletions contracts/modules/licensing/PILicenseTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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.
Expand All @@ -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)) {
Expand Down Expand Up @@ -220,17 +219,15 @@ 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);
}

/// @notice Checks if a license terms is transferable.
/// @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.
Expand Down Expand Up @@ -266,24 +263,27 @@ 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.
/// @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) {
PILicenseTemplateStorage storage $ = _getPILicenseTemplateStorage();
return $.licenseTermsCounter;
return _getPILicenseTemplateStorage().licenseTermsCounter;
}

/// @notice checks the contract whether supports the given interface.
Expand All @@ -298,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.
Expand All @@ -312,6 +311,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)
Expand Down Expand Up @@ -431,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;
Expand All @@ -456,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;
}
Expand Down
2 changes: 1 addition & 1 deletion test/foundry/access/AccessController.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,7 @@ contract AccessControllerTest is BaseTest {
bytes4(0),
AccessPermission.ALLOW
);

vm.stopPrank();
vm.expectRevert(abi.encodeWithSelector(PausableUpgradeable.EnforcedPause.selector));
vm.prank(owner);
Expand Down
3 changes: 2 additions & 1 deletion test/foundry/integration/big-bang/SingleNftCollection.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration {
derivativesApproval: false,
derivativesReciprocal: false,
derivativeRevCelling: 0,
currency: address(erc20)
currency: address(erc20),
uri: ""
})
);
}
Expand Down
7 changes: 7 additions & 0 deletions test/foundry/mocks/module/MockLicenseTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 "";
}
}
18 changes: 12 additions & 6 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
37 changes: 36 additions & 1 deletion test/foundry/modules/licensing/PILicenseTemplate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,41 @@ 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);
Expand All @@ -520,7 +555,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 */
}
}
6 changes: 4 additions & 2 deletions test/foundry/utils/LicensingHelper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ contract LicensingHelper {
derivativesApproval: false,
derivativesReciprocal: reciprocal,
derivativeRevCelling: 0,
currency: address(erc20)
currency: address(erc20),
uri: ""
});
}

Expand All @@ -124,7 +125,8 @@ contract LicensingHelper {
derivativesApproval: false,
derivativesReciprocal: reciprocal,
derivativeRevCelling: 0,
currency: address(0)
currency: address(0),
uri: ""
});
}

Expand Down

0 comments on commit 7343373

Please sign in to comment.