Skip to content

Commit

Permalink
Ensure Private License Token's License Template Matches Attached Lice…
Browse files Browse the repository at this point in the history
…nse Terms (#407)
  • Loading branch information
kingster-will authored Feb 21, 2025
1 parent 1ffda0a commit 853ab33
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
6 changes: 6 additions & 0 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ interface ILicenseRegistry {
/// @param newLicenseTermsId The ID of the new default license terms.
function setDefaultLicenseTerms(address newLicenseTemplate, uint256 newLicenseTermsId) external;

/// @notice set license template for an IP, if the IP has no license template.
/// @param ipId The address of the IP to which the license template is attached.
/// @param licenseTemplate The address of the license template.
/// @dev This function can only be called by the LicensingModule.
function initializeLicenseTemplate(address ipId, address licenseTemplate) external;

/// @notice Returns the default license terms.
function getDefaultLicenseTerms() external view returns (address licenseTemplate, uint256 licenseTermsId);

Expand Down
5 changes: 4 additions & 1 deletion contracts/modules/licensing/LicensingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -685,13 +685,16 @@ contract LicensingModule is
bytes calldata royaltyContext,
uint256 maxMintingFee
) private returns (uint256 paidMintingFee) {
bool isMintedByOwner = _hasPermission(licensorIpId);
Licensing.LicensingConfig memory lsc = LICENSE_REGISTRY.verifyMintLicenseToken(
licensorIpId,
licenseTemplate,
licenseTermsId,
_hasPermission(licensorIpId)
isMintedByOwner
);

if (isMintedByOwner) LICENSE_REGISTRY.initializeLicenseTemplate(licensorIpId, licenseTemplate);

if (ILicenseTemplate(licenseTemplate).allowDerivativeRegistration(licenseTermsId)) {
uint256 ancestors = LICENSE_REGISTRY.getAncestorsCount(licensorIpId);
if (ancestors >= MAX_ANCESTOR) {
Expand Down
22 changes: 22 additions & 0 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,20 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
if (earliestExp != 0) _setExpirationTime(childIpId, earliestExp);
}

/// @notice set license template for an IP, if the IP has no license template set.
/// @param ipId The address of the IP to which the license template is attached.
/// @param licenseTemplate The address of the license template.
/// @dev This function can only be called by the LicensingModule.
function initializeLicenseTemplate(address ipId, address licenseTemplate) external onlyLicensingModule {
LicenseRegistryStorage storage $ = _getLicenseRegistryStorage();
if (licenseTemplate == address(0)) revert Errors.LicenseRegistry__ZeroLicenseTemplate();
// check if registered license template
if (!$.registeredLicenseTemplates[licenseTemplate]) {
revert Errors.LicenseRegistry__UnregisteredLicenseTemplate(licenseTemplate);
}
if ($.licenseTemplates[ipId] == address(0)) $.licenseTemplates[ipId] = licenseTemplate;
}

/// @notice Verifies the minting of a license token.
/// @param licensorIpId The address of the licensor IP.
/// @param licenseTemplate The address of the license template where the license terms are defined.
Expand All @@ -329,6 +343,14 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
if (!_exists(licenseTemplate, licenseTermsId)) {
revert Errors.LicenseRegistry__LicenseTermsNotExists(licenseTemplate, licenseTermsId);
}

if ($.licenseTemplates[licensorIpId] != address(0) && licenseTemplate != $.licenseTemplates[licensorIpId]) {
revert Errors.LicenseRegistry__UnmatchedLicenseTemplate(
licensorIpId,
$.licenseTemplates[licensorIpId],
licenseTemplate
);
}
} else if (!_hasIpAttachedLicenseTerms(licensorIpId, licenseTemplate, licenseTermsId)) {
revert Errors.LicenseRegistry__LicensorIpHasNoLicenseTerms(licensorIpId, licenseTemplate, licenseTermsId);
}
Expand Down
145 changes: 145 additions & 0 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,151 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseTermsId, termsId);
}

function test_LicensingModule_mintLicenseToken_revert_privateLicenseTemplateIsDifferentFromExisting() public {
uint256 termsId1 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLRP)
})
);
vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), termsId1);

MockLicenseTemplate pilTemplate2 = new MockLicenseTemplate();
vm.prank(admin);
licenseRegistry.registerLicenseTemplate(address(pilTemplate2));
uint256 termsId2 = pilTemplate2.registerLicenseTerms();

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__UnmatchedLicenseTemplate.selector,
ipId1,
address(pilTemplate),
address(pilTemplate2)
)
);
vm.prank(ipOwner1);
uint256 lcTokenId = licensingModule.mintLicenseTokens({
licensorIpId: ipId1,
licenseTemplate: address(pilTemplate2),
licenseTermsId: termsId2,
amount: 1,
receiver: ipOwner2,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});
}

function test_LicensingModule_attachLicenseTerms_revert_templateDifferentFromPrivateLicenseTemplate() public {
uint256 termsId1 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLRP)
})
);
vm.prank(ipOwner1);
uint256 lcTokenId = licensingModule.mintLicenseTokens({
licensorIpId: ipId1,
licenseTemplate: address(pilTemplate),
licenseTermsId: termsId1,
amount: 1,
receiver: ipOwner2,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});

MockLicenseTemplate pilTemplate2 = new MockLicenseTemplate();
vm.prank(admin);
licenseRegistry.registerLicenseTemplate(address(pilTemplate2));
uint256 termsId2 = pilTemplate2.registerLicenseTerms();

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__UnmatchedLicenseTemplate.selector,
ipId1,
address(pilTemplate),
address(pilTemplate2)
)
);
vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate2), termsId2);
}

function test_LicensingModule_attachLicenseTerms_templateSameWithPrivateLicenseTemplate() public {
uint256 termsId1 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLRP)
})
);
vm.prank(ipOwner1);
uint256 lcTokenId = licensingModule.mintLicenseTokens({
licensorIpId: ipId1,
licenseTemplate: address(pilTemplate),
licenseTermsId: termsId1,
amount: 1,
receiver: ipOwner2,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});

uint256 termsId2 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLAP)
})
);

vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), termsId2);
}

function test_LicensingModule_mintLicenseToken_PrivateLicenseTemplateSameWithExisting() public {
uint256 termsId1 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLRP)
})
);

uint256 termsId2 = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLAP)
})
);

vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), termsId1);

vm.prank(ipOwner1);
uint256 lcTokenId = licensingModule.mintLicenseTokens({
licensorIpId: ipId1,
licenseTemplate: address(pilTemplate),
licenseTermsId: termsId2,
amount: 1,
receiver: ipOwner2,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_revert_pause() public {
uint256 termsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
vm.prank(ipOwner1);
Expand Down

1 comment on commit 853ab33

@Haxatron
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified. Now:

  • If an IP has no license template, IP owner can mint private license token which will register the template.

  • minting private license with license template different from the license template is not possible

Please sign in to comment.