Skip to content

Commit

Permalink
allow register derivative with private license (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
kingster-will authored Jun 28, 2024
1 parent cf51c2c commit 3569f52
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 14 deletions.
4 changes: 3 additions & 1 deletion contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ interface ILicenseRegistry {
/// @param parentIpIds An array of addresses of the parent IPs.
/// @param licenseTemplate The address of the license template used.
/// @param licenseTermsIds An array of IDs of the license terms.
/// @param isUsingLicenseToken Whether the derivative IP is registered with license tokens.
function registerDerivativeIp(
address ipId,
address[] calldata parentIpIds,
address licenseTemplate,
uint256[] calldata licenseTermsIds
uint256[] calldata licenseTermsIds,
bool isUsingLicenseToken
) external;

/// @notice Checks if an IP is a derivative IP.
Expand Down
4 changes: 2 additions & 2 deletions contracts/modules/licensing/LicensingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ contract LicensingModule is
// Set the derivative IP as a derivative of the parent IPs.
// Set the expiration timestamp for the derivative IP by invoking the license template to calculate
// the earliest expiration time among all license terms.
LICENSE_REGISTRY.registerDerivativeIp(childIpId, parentIpIds, licenseTemplate, licenseTermsIds);
LICENSE_REGISTRY.registerDerivativeIp(childIpId, parentIpIds, licenseTemplate, licenseTermsIds, false);
// Process the payment for the minting fee.
(address commonRoyaltyPolicy, bytes[] memory royaltyDatas) = _payMintingFeeForAllParentIps(
childIpId,
Expand Down Expand Up @@ -325,7 +325,7 @@ contract LicensingModule is
// Set the derivative IP as a derivative of the parent IPs.
// Set the expiration timestamp for the derivative IP to match the earliest expiration time of
// all license terms.
LICENSE_REGISTRY.registerDerivativeIp(childIpId, parentIpIds, licenseTemplate, licenseTermsIds);
LICENSE_REGISTRY.registerDerivativeIp(childIpId, parentIpIds, licenseTemplate, licenseTermsIds, true);

// Confirm that the royalty policies defined in all license terms of the parent IPs are identical.
address commonRoyaltyPolicy = address(0);
Expand Down
24 changes: 19 additions & 5 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
/// @param parentIpIds An array of addresses of the parent IPs.
/// @param licenseTemplate The address of the license template used.
/// @param licenseTermsIds An array of IDs of the license terms.
/// @param isUsingLicenseToken Whether the derivative IP is registered with license tokens.
function registerDerivativeIp(
address childIpId,
address[] calldata parentIpIds,
address licenseTemplate,
uint256[] calldata licenseTermsIds
uint256[] calldata licenseTermsIds,
bool isUsingLicenseToken
) external onlyLicensingModule {
if (parentIpIds.length == 0) {
revert Errors.LicenseRegistry__NoParentIp();
Expand All @@ -226,7 +228,13 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
uint256 earliestExp = 0;
for (uint256 i = 0; i < parentIpIds.length; i++) {
earliestExp = ExpiringOps.getEarliestExpirationTime(earliestExp, _getExpireTime(parentIpIds[i]));
_verifyDerivativeFromParent(parentIpIds[i], childIpId, licenseTemplate, licenseTermsIds[i]);
_verifyDerivativeFromParent(
parentIpIds[i],
childIpId,
licenseTemplate,
licenseTermsIds[i],
isUsingLicenseToken
);
$.childIps[parentIpIds[i]].add(childIpId);
// determine if duplicate license terms
bool isNewParent = $.parentIps[childIpId].add(parentIpIds[i]);
Expand Down Expand Up @@ -415,11 +423,13 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
/// @param childIpId The address of the child IP
/// @param licenseTemplate The address of the license template where the license terms are created
/// @param licenseTermsId The license terms the child IP is registered with
/// @param isUsingLicenseToken Whether the child IP is registered with license tokens
function _verifyDerivativeFromParent(
address parentIpId,
address childIpId,
address licenseTemplate,
uint256 licenseTermsId
uint256 licenseTermsId,
bool isUsingLicenseToken
) internal view {
LicenseRegistryStorage storage $ = _getLicenseRegistryStorage();
if (DISPUTE_MODULE.isIpTagged(parentIpId)) {
Expand All @@ -433,10 +443,14 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
}
// childIp can only register with default license terms or the license terms attached to the parent IP
if ($.defaultLicenseTemplate != licenseTemplate || $.defaultLicenseTermsId != licenseTermsId) {
if ($.licenseTemplates[parentIpId] != licenseTemplate) {
address pLicenseTemplate = $.licenseTemplates[parentIpId];
if (
(isUsingLicenseToken && pLicenseTemplate != address(0) && pLicenseTemplate != licenseTemplate) ||
(!isUsingLicenseToken && pLicenseTemplate != licenseTemplate)
) {
revert Errors.LicenseRegistry__ParentIpUnmatchedLicenseTemplate(parentIpId, licenseTemplate);
}
if (!$.attachedLicenseTerms[parentIpId].contains(licenseTermsId)) {
if (!isUsingLicenseToken && !$.attachedLicenseTerms[parentIpId].contains(licenseTermsId)) {
revert Errors.LicenseRegistry__ParentIpHasNoLicenseTerms(parentIpId, licenseTermsId);
}
}
Expand Down
51 changes: 51 additions & 0 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,57 @@ contract LicensingModuleTest is BaseTest {
assertEq(licenseTermsId, termsId);
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_privateLicense() public {
uint256 termsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());

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

assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId1, address(pilTemplate), termsId), false);

assertEq(licenseToken.ownerOf(lcTokenId), ipOwner2);
assertEq(licenseToken.getLicenseTermsId(lcTokenId), termsId);
assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate));
assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1);
assertEq(licenseToken.totalMintedTokens(), 1);
assertEq(licenseToken.totalSupply(), 1);
assertEq(licenseToken.balanceOf(ipOwner2), 1);

uint256[] memory licenseTokens = new uint256[](1);
licenseTokens[0] = lcTokenId;
vm.prank(ipOwner2);
licensingModule.registerDerivativeWithLicenseTokens(ipId2, licenseTokens, "");

assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId2, address(pilTemplate), termsId), true);
assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId1, address(pilTemplate), termsId), false);

assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId2), 1);
assertEq(licenseRegistry.isDerivativeIp(ipId2), true);
assertEq(licenseRegistry.hasDerivativeIps(ipId2), false);
assertEq(licenseRegistry.hasDerivativeIps(ipId1), true);
assertEq(licenseRegistry.isDerivativeIp(ipId1), false);
assertEq(licenseRegistry.getDerivativeIpCount(ipId1), 1);
assertEq(licenseRegistry.getDerivativeIpCount(ipId2), 0);
assertEq(licenseRegistry.getDerivativeIp(ipId1, 0), ipId2);
assertEq(licenseRegistry.getParentIp(ipId2, 0), ipId1);
assertEq(licenseRegistry.getParentIpCount(ipId2), 1);
assertEq(licenseToken.totalSupply(), 0);
assertEq(licenseToken.totalMintedTokens(), 1);
vm.expectRevert(abi.encodeWithSelector(ERC721NonexistentToken.selector, lcTokenId));
licenseToken.ownerOf(lcTokenId);

(address licenseTemplate, uint256 licenseTermsId) = licenseRegistry.getAttachedLicenseTerms(ipId2, 0);
assertEq(licenseTemplate, address(pilTemplate));
assertEq(licenseTermsId, termsId);
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_revert_pause() public {
uint256 termsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
vm.prank(ipOwner1);
Expand Down
188 changes: 182 additions & 6 deletions test/foundry/registries/LicenseRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,13 @@ contract LicenseRegistryTest is BaseTest {
licenseTermsIds[0] = socialRemixTermsId;
vm.expectRevert(Errors.LicenseRegistry__NoParentIp.selector);
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp(ipAcct[2], parentIpIds, address(pilTemplate), licenseTermsIds);
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[2],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
}

// test getAttachedLicenseTerms
Expand Down Expand Up @@ -250,7 +256,107 @@ contract LicenseRegistryTest is BaseTest {
licenseTermsIds[0] = socialRemixTermsId;
licenseTermsIds[1] = socialRemixTermsId;
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp(ipAcct[3], parentIpIds, address(pilTemplate), licenseTermsIds);
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
// test registerDerivativeIp using licenseToken
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[5],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: true
});
}

function test_LicenseRegistry_registerDerivativeIp_unattachedLicenseTerm_usingLicenseToken() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
vm.prank(ipOwner[1]);
licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId);
vm.prank(ipOwner[2]);
licensingModule.attachLicenseTerms(ipAcct[2], address(pilTemplate), socialRemixTermsId);

uint256 commercialRemixTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix(100, 10, address(royaltyPolicyLAP), address(erc20))
);

address[] memory parentIpIds = new address[](2);
parentIpIds[0] = ipAcct[1];
parentIpIds[1] = ipAcct[2];
uint256[] memory licenseTermsIds = new uint256[](2);
licenseTermsIds[0] = commercialRemixTermsId;
licenseTermsIds[1] = commercialRemixTermsId;
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: true
});
}

function test_LicenseRegistry_registerDerivativeIp_parentNoLicenseTemplate_usingLicenseToken() public {
uint256 commercialRemixTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix(100, 10, address(royaltyPolicyLAP), address(erc20))
);

address[] memory parentIpIds = new address[](2);
parentIpIds[0] = ipAcct[1];
parentIpIds[1] = ipAcct[2];
uint256[] memory licenseTermsIds = new uint256[](2);
licenseTermsIds[0] = commercialRemixTermsId;
licenseTermsIds[1] = commercialRemixTermsId;
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: true
});
}

function test_LicenseRegistry_registerDerivativeIp_unmatchLicenseTemplate_usingLicenseToken() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
vm.prank(ipOwner[1]);
licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId);
vm.prank(ipOwner[2]);
licensingModule.attachLicenseTerms(ipAcct[2], address(pilTemplate), socialRemixTermsId);

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

address[] memory parentIpIds = new address[](2);
parentIpIds[0] = ipAcct[1];
parentIpIds[1] = ipAcct[2];
uint256[] memory licenseTermsIds = new uint256[](2);
licenseTermsIds[0] = mockTermsId;
licenseTermsIds[1] = mockTermsId;

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__ParentIpUnmatchedLicenseTemplate.selector,
ipAcct[1],
address(pilTemplate2)
)
);

vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate2),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: true
});
}

function test_LicenseRegistry_registerDerivativeIp_revert_DuplicateLicense() public {
Expand All @@ -273,7 +379,66 @@ contract LicenseRegistryTest is BaseTest {
)
);
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp(ipAcct[2], parentIpIds, address(pilTemplate), licenseTermsIds);
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[2],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
}

function test_LicenseRegistry_registerDerivativeIp_revert_UnattachedLicenseTemplate() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());

address[] memory parentIpIds = new address[](1);
parentIpIds[0] = ipAcct[1];
uint256[] memory licenseTermsIds = new uint256[](1);
licenseTermsIds[0] = socialRemixTermsId;

vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__ParentIpUnmatchedLicenseTemplate.selector,
ipAcct[1],
address(pilTemplate)
)
);
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[2],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
}

function test_LicenseRegistry_registerDerivativeIp_revert_UnattachedLicenseTermsId() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
vm.prank(ipOwner[1]);
licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId);
uint256 commercialRemixTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix(100, 10, address(royaltyPolicyLAP), address(erc20))
);
address[] memory parentIpIds = new address[](1);
parentIpIds[0] = ipAcct[1];
uint256[] memory licenseTermsIds = new uint256[](1);
licenseTermsIds[0] = commercialRemixTermsId;
vm.expectRevert(
abi.encodeWithSelector(
Errors.LicenseRegistry__ParentIpHasNoLicenseTerms.selector,
ipAcct[1],
commercialRemixTermsId
)
);
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[2],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
}

function test_LicenseRegistry_isExpiredNow() public {
Expand Down Expand Up @@ -333,7 +498,13 @@ contract LicenseRegistryTest is BaseTest {
licenseTermsIds[0] = termsId1;
licenseTermsIds[1] = termsId2;
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp(ipAcct[3], parentIpIds, address(pilTemplate), licenseTermsIds);
licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});

assertEq(licenseRegistry.getExpireTime(ipAcct[1]), block.timestamp + 100, "ipAcct[1] expire time is incorrect");
assertEq(licenseRegistry.getExpireTime(ipAcct[2]), 0, "ipAcct[2] expire time is incorrect");
Expand Down Expand Up @@ -371,8 +542,13 @@ contract LicenseRegistryTest is BaseTest {
licenseTermsIds[0] = termsId1;
licenseTermsIds[1] = termsId2;
vm.prank(address(licensingModule));
licenseRegistry.registerDerivativeIp(ipAcct[3], parentIpIds, address(pilTemplate), licenseTermsIds);

licenseRegistry.registerDerivativeIp({
childIpId: ipAcct[3],
parentIpIds: parentIpIds,
licenseTemplate: address(pilTemplate),
licenseTermsIds: licenseTermsIds,
isUsingLicenseToken: false
});
assertEq(licenseRegistry.getExpireTime(ipAcct[1]), block.timestamp + 500, "ipAcct[1] expire time is incorrect");
assertEq(licenseRegistry.getExpireTime(ipAcct[2]), 0, "ipAcct[2] expire time is incorrect");
assertEq(licenseRegistry.getExpireTime(ipAcct[3]), block.timestamp + 400, "ipAcct[3] expire time is incorrect");
Expand Down

0 comments on commit 3569f52

Please sign in to comment.