Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict Group from Having Derivative or Having Parents, or Minting License Tokens #354

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,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 @@ -664,6 +664,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 @@ -673,18 +691,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
Loading