Skip to content

Commit

Permalink
group cannot have parents and empty group cannot have derivative (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
kingster-will authored Dec 12, 2024
1 parent d7e5f37 commit 81380f6
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 9 deletions.
12 changes: 12 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ library Errors {
/// @notice Call failed.
error LicenseRegistry__CallFailed();

/// @notice Zero address provide for Group IP Asset Registry.
error LicenseRegistry__ZeroGroupIpRegistry();

/// @notice The empty group cannot be registered as parent IP.
error LicenseRegistry__ParentIpIsEmptyGroup(address groupId);

/// @notice The group cannot be registered as derivative/child IP.
error LicenseRegistry__GroupCannotHasParentIp(address groupId);

/// @notice The empty group cannot mint license token.
error LicenseRegistry__EmptyGroupCannotMintLicenseToken(address groupId);

////////////////////////////////////////////////////////////////////////////
// License Token //
////////////////////////////////////////////////////////////////////////////
Expand Down
24 changes: 23 additions & 1 deletion contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ILicenseTemplate } from "../interfaces/modules/licensing/ILicenseTempla
import { IPAccountStorageOps } from "../lib/IPAccountStorageOps.sol";
import { IIPAccount } from "../interfaces/IIPAccount.sol";
import { IPGraphACL } from "../access/IPGraphACL.sol";
import { IGroupIPAssetRegistry } from "../interfaces/registries/IGroupIPAssetRegistry.sol";

/// @title LicenseRegistry aka LNFT
/// @notice Registry of License NFTs, which represent licenses granted by IP ID licensors to create derivative IPs.
Expand All @@ -31,6 +32,8 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr

address public constant IP_GRAPH = address(0x0101);
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IGroupIPAssetRegistry public immutable GROUP_IP_ASSET_REGISTRY;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ILicensingModule public immutable LICENSING_MODULE;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IDisputeModule public immutable DISPUTE_MODULE;
Expand Down Expand Up @@ -81,10 +84,12 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address licensingModule, address disputeModule, address ipGraphAcl) {
constructor(address groupIpAssetRegistry, address licensingModule, address disputeModule, address ipGraphAcl) {
if (groupIpAssetRegistry == address(0)) revert Errors.LicenseRegistry__ZeroGroupIpRegistry();
if (licensingModule == address(0)) revert Errors.LicenseRegistry__ZeroLicensingModule();
if (disputeModule == address(0)) revert Errors.LicenseRegistry__ZeroDisputeModule();
if (ipGraphAcl == address(0)) revert Errors.LicenseRegistry__ZeroIPGraphACL();
GROUP_IP_ASSET_REGISTRY = IGroupIPAssetRegistry(groupIpAssetRegistry);
LICENSING_MODULE = ILicensingModule(licensingModule);
DISPUTE_MODULE = IDisputeModule(disputeModule);
IP_GRAPH_ACL = IPGraphACL(ipGraphAcl);
Expand Down Expand Up @@ -292,6 +297,12 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
uint256 licenseTermsId,
bool isMintedByIpOwner
) external view returns (Licensing.LicensingConfig memory) {
if (
GROUP_IP_ASSET_REGISTRY.isRegisteredGroup(licensorIpId) &&
GROUP_IP_ASSET_REGISTRY.totalMembers(licensorIpId) == 0
) {
revert Errors.LicenseRegistry__EmptyGroupCannotMintLicenseToken(licensorIpId);
}
LicenseRegistryStorage storage $ = _getLicenseRegistryStorage();
if (_isExpiredNow(licensorIpId)) {
revert Errors.LicenseRegistry__ParentIpExpired(licensorIpId);
Expand Down Expand Up @@ -558,6 +569,17 @@ contract LicenseRegistry is ILicenseRegistry, AccessManagedUpgradeable, UUPSUpgr
bool isUsingLicenseToken
) internal view {
LicenseRegistryStorage storage $ = _getLicenseRegistryStorage();
// group IP should not has parent IP
if (GROUP_IP_ASSET_REGISTRY.isRegisteredGroup(childIpId)) {
revert Errors.LicenseRegistry__GroupCannotHasParentIp(childIpId);
}
// revert if the parent IP is empty group IP
if (
GROUP_IP_ASSET_REGISTRY.isRegisteredGroup(parentIpId) &&
GROUP_IP_ASSET_REGISTRY.totalMembers(parentIpId) == 0
) {
revert Errors.LicenseRegistry__ParentIpIsEmptyGroup(parentIpId);
}
if (DISPUTE_MODULE.isIpTagged(parentIpId)) {
revert Errors.LicenseRegistry__ParentIpTagged(parentIpId);
}
Expand Down
1 change: 1 addition & 0 deletions script/foundry/utils/DeployHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ contract DeployHelper is Script, BroadcastManager, JsonDeploymentHandler, Storag
_predeploy(contractKey);
impl = address(
new LicenseRegistry(
address(ipAssetRegistry),
_getDeployedAddress(type(LicensingModule).name),
_getDeployedAddress(type(DisputeModule).name),
newDeployedIpGraphACL ? _getDeployedAddress(type(IPGraphACL).name) : address(ipGraphACL)
Expand Down
3 changes: 2 additions & 1 deletion test/foundry/mocks/module/LicenseRegistryHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { LicenseRegistry } from "../../../../contracts/registries/LicenseRegistr

contract LicenseRegistryHarness is LicenseRegistry {
constructor(
address _groupIpAssetRegistry,
address _erc721Registry,
address _erc1155Registry,
address _ipGraphAcl
) LicenseRegistry(_erc721Registry, _erc1155Registry, _ipGraphAcl) {}
) LicenseRegistry(_groupIpAssetRegistry, _erc721Registry, _erc1155Registry, _ipGraphAcl) {}

function setExpirationTime(address ipId, uint256 expireTime) external {
_setExpirationTime(ipId, expireTime);
Expand Down
111 changes: 105 additions & 6 deletions test/foundry/modules/grouping/GroupingModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,24 @@ contract GroupingModuleTest is BaseTest, ERC721Holder {
vm.prank(ipOwner2);
licensingModule.attachLicenseTerms(ipId2, address(pilTemplate), termsId);

Licensing.LicensingConfig memory licensingConfig = Licensing.LicensingConfig({
isSet: true,
mintingFee: 0,
licensingHook: address(0),
hookData: "",
commercialRevShare: 10 * 10 ** 6,
disabled: false,
expectMinimumGroupRewardShare: 0,
expectGroupRewardPool: address(evenSplitGroupPool)
});
vm.prank(ipOwner1);
licensingModule.setLicensingConfig(ipId1, address(pilTemplate), termsId, licensingConfig);

address[] memory ipIds = new address[](1);
ipIds[0] = ipId1;
vm.prank(alice);
groupingModule.addIp(groupId, ipIds);

vm.startPrank(ipOwner3);
address[] memory parentIpIds = new address[](1);
uint256[] memory licenseTermsIds = new uint256[](1);
Expand All @@ -779,18 +797,99 @@ contract GroupingModuleTest is BaseTest, ERC721Holder {
licensingModule.registerDerivative(ipId3, parentIpIds, licenseTermsIds, address(pilTemplate), "", 0, 100e6);
vm.stopPrank();

address[] memory ipIds = new address[](2);
ipIds[0] = ipId1;
ipIds[1] = ipId2;
ipIds = new address[](1);
ipIds[0] = ipId2;
vm.expectRevert(
abi.encodeWithSelector(Errors.GroupingModule__GroupFrozenDueToHasDerivativeIps.selector, groupId)
);
vm.prank(alice);
groupingModule.addIp(groupId, ipIds);

assertEq(ipAssetRegistry.totalMembers(groupId), 0);
assertEq(rewardPool.getTotalIps(groupId), 0);
assertEq(rewardPool.getIpAddedTime(groupId, ipId1), 0);
assertEq(ipAssetRegistry.totalMembers(groupId), 1);
assertEq(rewardPool.getTotalIps(groupId), 1);
assertEq(rewardPool.getIpAddedTime(groupId, ipId1), block.timestamp);
}

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

vm.startPrank(alice);
address groupId = groupingModule.registerGroup(address(rewardPool));
licensingModule.attachLicenseTerms(groupId, address(pilTemplate), termsId);
vm.stopPrank();

vm.prank(ipOwner1);
licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), termsId);
vm.prank(ipOwner2);
licensingModule.attachLicenseTerms(ipId2, address(pilTemplate), termsId);

vm.startPrank(ipOwner3);
address[] memory parentIpIds = new address[](1);
uint256[] memory licenseTermsIds = new uint256[](1);
parentIpIds[0] = groupId;
licenseTermsIds[0] = termsId;

vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__ParentIpIsEmptyGroup.selector, groupId));
licensingModule.registerDerivative(ipId3, parentIpIds, licenseTermsIds, address(pilTemplate), "", 0, 100e6);
vm.stopPrank();
}

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

vm.startPrank(alice);
address groupId = groupingModule.registerGroup(address(rewardPool));
licensingModule.attachLicenseTerms(groupId, address(pilTemplate), termsId);
vm.stopPrank();

vm.startPrank(ipOwner3);
vm.expectRevert(
abi.encodeWithSelector(Errors.LicenseRegistry__EmptyGroupCannotMintLicenseToken.selector, groupId)
);
licensingModule.mintLicenseTokens(groupId, address(pilTemplate), termsId, 1, ipOwner3, "", 0);
vm.stopPrank();
}

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

vm.startPrank(alice);
address groupId = groupingModule.registerGroup(address(rewardPool));
vm.stopPrank();

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

vm.startPrank(alice);
address[] memory parentIpIds = new address[](1);
uint256[] memory licenseTermsIds = new uint256[](1);
parentIpIds[0] = ipId1;
licenseTermsIds[0] = termsId;

vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__GroupCannotHasParentIp.selector, groupId));
licensingModule.registerDerivative(groupId, parentIpIds, licenseTermsIds, address(pilTemplate), "", 0, 100e6);
vm.stopPrank();
}

function test_GroupingModule_removeIp_revert_after_registerDerivative() public {
Expand Down
7 changes: 6 additions & 1 deletion test/foundry/utils/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ contract BaseTest is Test, DeployHelper, LicensingHelper {

ipAccountRegistry = IPAccountRegistry(ipAssetRegistry);
lrHarnessImpl = address(
new LicenseRegistryHarness(address(licensingModule), address(disputeModule), address(ipGraphACL))
new LicenseRegistryHarness(
address(ipAssetRegistry),
address(licensingModule),
address(disputeModule),
address(ipGraphACL)
)
);

mockArbitrationPolicy = new MockArbitrationPolicy(address(disputeModule), address(USDC), ARBITRATION_PRICE);
Expand Down

0 comments on commit 81380f6

Please sign in to comment.