Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Offchain URI Field to PILTerms #94

Merged
merged 3 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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
Loading