Skip to content
This repository has been archived by the owner on Apr 30, 2024. It is now read-only.

Commit

Permalink
Limit the last node of a derivative chain to mint licenses (#106)
Browse files Browse the repository at this point in the history
* add contract and unit test adjustments to limit license minting for last node
* add comment and fix
  • Loading branch information
Spablob authored Feb 17, 2024
1 parent da87546 commit 14a0a9b
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 18 deletions.
1 change: 1 addition & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ library Errors {
error RoyaltyPolicyLAP__NotFullOwnership();
error RoyaltyPolicyLAP__UnlinkableToParents();
error RoyaltyPolicyLAP__TransferFailed();
error RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense();

error AncestorsVaultLAP__ZeroRoyaltyPolicyLAP();
error AncestorsVaultLAP__AlreadyClaimed();
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 @@ -130,8 +130,8 @@ contract LicensingModule is AccessControlled, ILicensingModule, BaseModule, Reen
}

/// Adds a policy to an ipId, which can be used to mint licenses.
/// Licnses are permissions for ipIds to be derivatives (children).
/// if policyId is not defined in LicenseRegistry, reverts.
/// Licenses are permissions for ipIds to be derivatives (children).
/// If policyId is not defined in LicenseRegistry, reverts.
/// Will revert if ipId already has the same policy
/// @param ipId to receive the policy
/// @param polId id of the policy data
Expand Down
27 changes: 20 additions & 7 deletions contracts/modules/royalty-module/policies/RoyaltyPolicyLAP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,26 @@ contract RoyaltyPolicyLAP is IRoyaltyPolicyLAP, Governable, ERC1155Holder, Reent
if (data.royaltyStack + newLicenseRoyalty > TOTAL_RNFT_SUPPLY)
revert Errors.RoyaltyPolicyLAP__AboveRoyaltyStackLimit();

// If the policy is already initialized, it means that the ipId setup is already done. If not, it means that
// the license for this royalty policy is being minted for the first time parentIpIds are zero given that only
// roots can call _initPolicy() for the first time in the function onLicenseMinting() while derivatives already
// called _initPolicy() when linking to their parents with onLinkToParents() call.
address[] memory rootParents = new address[](0);
bytes[] memory rootParentRoyalties = new bytes[](0);
if (data.splitClone == address(0)) _initPolicy(_ipId, rootParents, rootParentRoyalties, _externalData);
if (data.splitClone == address(0)) {
// If the policy is already initialized, it means that the ipId setup is already done. If not, it means that
// the license for this royalty policy is being minted for the first time parentIpIds are zero given that only
// roots can call _initPolicy() for the first time in the function onLicenseMinting() while derivatives already
// called _initPolicy() when linking to their parents with onLinkToParents() call.
address[] memory rootParents = new address[](0);
bytes[] memory rootParentRoyalties = new bytes[](0);
_initPolicy(_ipId, rootParents, rootParentRoyalties, _externalData);
} else {
InitParams memory params = abi.decode(_externalData, (InitParams));
// If the policy is already initialized and an ipId has the maximum number of ancestors
// it can not have any derivative and therefore is not allowed to mint any license
if (params.targetAncestors.length >= MAX_ANCESTORS) revert Errors.RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense();
// the check below ensures that the ancestors hash is the same as the one stored in the royalty data
// and that the targetAncestors passed in by the user matches the record stored in state on policy initialization
if (
keccak256(abi.encodePacked(params.targetAncestors, params.targetRoyaltyAmount)) !=
royaltyData[_ipId].ancestorsHash
) revert Errors.RoyaltyPolicyLAP__InvalidAncestorsHash();
}
}

/// @notice Executes royalty related logic on linking to parents
Expand Down
50 changes: 41 additions & 9 deletions test/foundry/modules/royalty/RoyaltyModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,46 @@ contract TestRoyaltyModule is BaseTest {
address licensor = address(3);
bytes memory licenseData = abi.encode(uint32(15));

address[] memory parents = new address[](2);
address[] memory targetAncestors1 = new address[](2);
uint32[] memory targetRoyaltyAmount1 = new uint32[](2);
uint32[] memory parentRoyalties1 = new uint32[](2);
bytes[] memory encodedLicenseData = new bytes[](2);

address[] memory nullParentAncestors1 = new address[](0);
address[] memory nullParentAncestors2 = new address[](0);
uint32[] memory nullParentAncestorsRoyalties1 = new uint32[](0);
uint32[] memory nullParentAncestorsRoyalties2 = new uint32[](0);

parents[0] = address(7);
parents[1] = address(8);
parentRoyalties1[0] = 7;
parentRoyalties1[1] = 8;
targetAncestors1[0] = address(7);
targetAncestors1[1] = address(8);
targetRoyaltyAmount1[0] = 7;
targetRoyaltyAmount1[1] = 8;
InitParams memory initParams = InitParams({
targetAncestors: targetAncestors1,
targetRoyaltyAmount: targetRoyaltyAmount1,
parentAncestors1: nullParentAncestors1,
parentAncestors2: nullParentAncestors2,
parentAncestorsRoyalties1: nullParentAncestorsRoyalties1,
parentAncestorsRoyalties2: nullParentAncestorsRoyalties2
});
for (uint32 i = 0; i < parentRoyalties1.length; i++) {
encodedLicenseData[i] = abi.encode(parentRoyalties1[i]);
}
bytes memory encodedBytes = abi.encode(initParams);

vm.startPrank(address(licensingModule));
royaltyModule.onLicenseMinting(licensor, address(royaltyPolicyLAP), licenseData, "");
royaltyModule.onLicenseMinting(licensor, address(royaltyPolicyLAP), licenseData, encodedBytes);
}

function test_RoyaltyModule_onLicenseMinting_Root() public {
address licensor = address(7);
bytes memory licenseData = abi.encode(uint32(15));

vm.startPrank(address(licensingModule));
royaltyModule.onLicenseMinting(licensor, address(royaltyPolicyLAP), licenseData, "");
vm.stopPrank();

vm.startPrank(u.admin);
royaltyModule.whitelistRoyaltyPolicy(address(royaltyPolicyLAP2), true);
vm.stopPrank();

// mint a license of another policy
address[] memory nullTargetAncestors = new address[](0);
uint32[] memory nullTargetRoyaltyAmount = new uint32[](0);
Expand All @@ -239,6 +263,14 @@ contract TestRoyaltyModule is BaseTest {
});
bytes memory nullBytes = abi.encode(nullInitParams);

vm.startPrank(address(licensingModule));
royaltyModule.onLicenseMinting(licensor, address(royaltyPolicyLAP), licenseData, nullBytes);
vm.stopPrank();

vm.startPrank(u.admin);
royaltyModule.whitelistRoyaltyPolicy(address(royaltyPolicyLAP2), true);
vm.stopPrank();

vm.startPrank(address(licensingModule));
royaltyModule.onLicenseMinting(licensor, address(royaltyPolicyLAP2), licenseData, nullBytes);
}
Expand Down
46 changes: 46 additions & 0 deletions test/foundry/modules/royalty/RoyaltyPolicyLAP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,52 @@ contract TestRoyaltyPolicyLAP is BaseTest {
royaltyPolicyLAP.onLicenseMinting(address(100), abi.encode(uint32(1001)), inputBytes);
}

function test_RoyaltyPolicyLAP_onLicenseMinting_revert_InvalidAncestors() public {
address[] memory targetAncestors = new address[](0);
uint32[] memory targetRoyaltyAmount = new uint32[](0);
address[] memory parentAncestors1 = new address[](0);
address[] memory parentAncestors2 = new address[](0);
uint32[] memory parentAncestorsRoyalties1 = new uint32[](0);
uint32[] memory parentAncestorsRoyalties2 = new uint32[](0);
InitParams memory initParams = InitParams({
targetAncestors: targetAncestors,
targetRoyaltyAmount: targetRoyaltyAmount,
parentAncestors1: parentAncestors1,
parentAncestors2: parentAncestors2,
parentAncestorsRoyalties1: parentAncestorsRoyalties1,
parentAncestorsRoyalties2: parentAncestorsRoyalties2
});
bytes memory inputBytes = abi.encode(initParams);

royaltyPolicyLAP.onLicenseMinting(address(100), abi.encode(uint32(0)), inputBytes);

address[] memory targetAncestors2 = new address[](2);
initParams = InitParams({
targetAncestors: targetAncestors2,
targetRoyaltyAmount: targetRoyaltyAmount,
parentAncestors1: parentAncestors1,
parentAncestors2: parentAncestors2,
parentAncestorsRoyalties1: parentAncestorsRoyalties1,
parentAncestorsRoyalties2: parentAncestorsRoyalties2
});
inputBytes = abi.encode(initParams);

vm.expectRevert(Errors.RoyaltyPolicyLAP__InvalidAncestorsHash.selector);
royaltyPolicyLAP.onLicenseMinting(address(100), abi.encode(uint32(0)), inputBytes);
}

function test_RoyaltyPolicyLAP_onLicenseMinting_revert_LastPositionNotAbleToMintLicense() public {
bytes[] memory encodedLicenseData = new bytes[](2);
for (uint32 i = 0; i < parentsIpIds100.length; i++) {
encodedLicenseData[i] = abi.encode(parentsIpIds100[i]);
}

royaltyPolicyLAP.onLinkToParents(address(100), parentsIpIds100, encodedLicenseData, MAX_ANCESTORS);

vm.expectRevert(Errors.RoyaltyPolicyLAP__LastPositionNotAbleToMintLicense.selector);
royaltyPolicyLAP.onLicenseMinting(address(100), abi.encode(uint32(0)), MAX_ANCESTORS);
}

function test_RoyaltyPolicyLAP_onLicenseMinting() public {
address[] memory targetAncestors = new address[](0);
uint32[] memory targetRoyaltyAmount = new uint32[](0);
Expand Down

0 comments on commit 14a0a9b

Please sign in to comment.