Skip to content

Commit

Permalink
Ensure IP Cannot Be Registered as Derivative After Minting Private Li…
Browse files Browse the repository at this point in the history
…cense Token (#366)
  • Loading branch information
kingster-will authored Dec 18, 2024
1 parent 278571e commit a6f5935
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 2 deletions.
9 changes: 8 additions & 1 deletion contracts/LicenseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
LicenseTokenStorage storage $ = _getLicenseTokenStorage();
startLicenseTokenId = $.totalMintedTokens;
$.totalMintedTokens += amount;
$.licensorIpTotalTokens[licensorIpId] += amount;
if (!LICENSE_REGISTRY.isDefaultLicense(licenseTemplate, licenseTermsId)) {
$.licensorIpTotalTokens[licensorIpId] += amount;
}
for (uint256 i = 0; i < amount; i++) {
uint256 tokenId = startLicenseTokenId + i;
$.licenseTokenMetadatas[tokenId] = ltm;
Expand Down Expand Up @@ -170,6 +172,11 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
)
{
LicenseTokenStorage storage $ = _getLicenseTokenStorage();

if ($.licensorIpTotalTokens[childIpId] != 0) {
revert Errors.LicenseToken__ChildIPAlreadyHasBeenMintedLicenseTokens(childIpId);
}

licenseTemplate = $.licenseTokenMetadatas[tokenIds[0]].licenseTemplate;
licensorIpIds = new address[](tokenIds.length);
licenseTermsIds = new uint256[](tokenIds.length);
Expand Down
3 changes: 3 additions & 0 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ interface ILicenseRegistry {
/// @notice Returns the default license terms.
function getDefaultLicenseTerms() external view returns (address licenseTemplate, uint256 licenseTermsId);

/// @notice Checks if the license terms are the default license terms.
function isDefaultLicense(address licenseTemplate, uint256 licenseTermsId) external view returns (bool);

/// @notice Registers a new license template in the Story Protocol.
/// @param licenseTemplate The address of the license template to register.
function registerLicenseTemplate(address licenseTemplate) external;
Expand Down
6 changes: 6 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ library Errors {
address licenseTemplate,
uint256 licenseTermsId
);

/// @notice There are non-default license tokens have already been minted from the child Ip.
error LicenseToken__ChildIPAlreadyHasBeenMintedLicenseTokens(address childIpId);
////////////////////////////////////////////////////////////////////////////
// Licensing Module //
////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -385,6 +388,9 @@ library Errors {
/// @notice Derivative IP cannot add license terms.
error LicensingModule__DerivativesCannotAddLicenseTerms();

/// @notice there are non-default license tokens have already been minted from the child Ip.
error LicensingModule__DerivativeAlreadyHasBeenMintedLicenseTokens(address childIpId);

/// @notice IP list and license terms list length mismatch.
error LicensingModule__LicenseTermsLengthMismatch(uint256 ipLength, uint256 licenseTermsLength);

Expand Down
4 changes: 3 additions & 1 deletion contracts/modules/licensing/LicensingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ contract LicensingModule is
) {
revert Errors.LicensingModule__LicenseNotCompatibleForDerivative(childIpId);
}

if (LICENSE_NFT.getTotalTokensByLicensor(childIpId) != 0) {
revert Errors.LicensingModule__DerivativeAlreadyHasBeenMintedLicenseTokens(childIpId);
}
// Ensure none of the parent IPs have expired.
// Confirm that each parent IP has the license terms attached as specified by 'licenseTermsIds'
// or default license terms.
Expand Down
5 changes: 5 additions & 0 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
return ($.defaultLicenseTemplate, $.defaultLicenseTermsId);
}

/// @notice Checks if the license terms are the default license terms.
function isDefaultLicense(address licenseTemplate, uint256 licenseTermsId) external view returns (bool) {
LicenseRegistryStorage storage $ = _getLicenseRegistryStorage();
return $.defaultLicenseTemplate == licenseTemplate && $.defaultLicenseTermsId == licenseTermsId;
}
/// @notice Returns the license terms through which a child IP links to a parent IP.
/// @param childIpId The address of the child IP.
/// @param parentIpId The address of the parent IP.
Expand Down
97 changes: 97 additions & 0 deletions test/foundry/modules/licensing/LicensingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,59 @@ contract LicensingModuleTest is BaseTest {
licensingModule.registerDerivativeWithLicenseTokens(ipId3, licenseTokens, "", 100e6);
}

function test_LicensingModule_registerDerivativeWithLicenseTokens_revert_mintPrivateLicense() public {
uint256 commRemixTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10_000_000,
royaltyPolicy: address(royaltyPolicyLRP),
currencyToken: address(erc20)
})
);
uint256 commUseTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialUse({
mintingFee: 0,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLAP)
})
);

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

uint256 lcTokenId = licensingModule.mintLicenseTokens(
ipId1,
address(pilTemplate),
commRemixTermsId,
1,
ipOwner2,
"",
0,
0
);

vm.prank(ipOwner2);
licensingModule.mintLicenseTokens({
licensorIpId: ipId2,
licenseTemplate: address(pilTemplate),
licenseTermsId: commUseTermsId,
amount: 1,
receiver: ipOwner3,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});

uint256[] memory licenseTokens = new uint256[](1);
licenseTokens[0] = lcTokenId;

vm.expectRevert(
abi.encodeWithSelector(Errors.LicenseToken__ChildIPAlreadyHasBeenMintedLicenseTokens.selector, ipId2)
);
vm.prank(ipOwner2);
licensingModule.registerDerivativeWithLicenseTokens(ipId2, licenseTokens, "", 100e6);
}

function test_LicensingModule_registerDerivative_revert_royaltyPolicyMismatch() public {
PILTerms memory terms = PILFlavors.commercialRemix({
mintingFee: 0,
Expand Down Expand Up @@ -1749,6 +1802,50 @@ contract LicensingModuleTest is BaseTest {
licensingModule.registerDerivative(ipId3, parentIpIds, licenseTermsIds, address(pilTemplate), "", 0, 100e6, 0);
}

function test_LicensingModule_registerDerivative_revert_mintPrivateLicense() public {
uint256 commRemixTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialRemix({
mintingFee: 0,
commercialRevShare: 10_000_000,
royaltyPolicy: address(royaltyPolicyLRP),
currencyToken: address(erc20)
})
);
uint256 commUseTermsId = pilTemplate.registerLicenseTerms(
PILFlavors.commercialUse({
mintingFee: 0,
currencyToken: address(erc20),
royaltyPolicy: address(royaltyPolicyLAP)
})
);

vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), commRemixTermsId);
vm.prank(ipOwner2);
licensingModule.mintLicenseTokens({
licensorIpId: ipId2,
licenseTemplate: address(pilTemplate),
licenseTermsId: commUseTermsId,
amount: 1,
receiver: ipOwner3,
royaltyContext: "",
maxMintingFee: 0,
maxRevenueShare: 0
});

address[] memory parentIpIds = new address[](1);
parentIpIds[0] = ipId1;

uint256[] memory licenseTermsIds = new uint256[](1);
licenseTermsIds[0] = commRemixTermsId;

vm.expectRevert(
abi.encodeWithSelector(Errors.LicensingModule__DerivativeAlreadyHasBeenMintedLicenseTokens.selector, ipId2)
);
vm.prank(ipOwner2);
licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), "", 0, 100e6, 0);
}

function test_LicensingModule_registerDerivative_revert_CommercialUseOnlyLicense() public {
(, uint256 defaultTermsId) = licenseRegistry.getDefaultLicenseTerms();
uint256 commUseTermsId = pilTemplate.registerLicenseTerms(
Expand Down

0 comments on commit a6f5935

Please sign in to comment.