diff --git a/contracts/LicenseToken.sol b/contracts/LicenseToken.sol index 525ea150..dd16ffd3 100644 --- a/contracts/LicenseToken.sol +++ b/contracts/LicenseToken.sol @@ -220,7 +220,14 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag return _getLicenseTokenStorage().licenseTokenMetadatas[tokenId].expiresAt; } + /// @notice Returns the canonical protocol-wide DisputeModule + /// @return The DisputeModule instance + function disputeModule() external view returns (IDisputeModule) { + return _getLicenseTokenStorage().disputeModule; + } + /// @notice Returns the canonical protocol-wide LicensingModule + /// @return The LicensingModule instance function licensingModule() external view returns (ILicensingModule) { return _getLicenseTokenStorage().licensingModule; } diff --git a/contracts/interfaces/ILicenseToken.sol b/contracts/interfaces/ILicenseToken.sol index ca905f06..235fffbb 100644 --- a/contracts/interfaces/ILicenseToken.sol +++ b/contracts/interfaces/ILicenseToken.sol @@ -4,6 +4,9 @@ pragma solidity 0.8.23; import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; +import { IDisputeModule } from "./modules/dispute/IDisputeModule.sol"; +import { ILicensingModule } from "./modules/licensing/ILicensingModule.sol"; + /// @title ILicenseToken /// @notice Interface for the License Token (ERC721) NFT collection that manages License Tokens representing /// License Terms. @@ -92,6 +95,14 @@ interface ILicenseToken is IERC721Metadata, IERC721Enumerable { /// @return A `LicenseTokenMetadata` struct containing the metadata of the specified License Token. function getLicenseTokenMetadata(uint256 tokenId) external view returns (LicenseTokenMetadata memory); + /// @notice Returns the canonical protocol-wide DisputeModule + /// @return The DisputeModule instance + function disputeModule() external view returns (IDisputeModule); + + /// @notice Returns the canonical protocol-wide LicensingModule + /// @return The LicensingModule instance + function licensingModule() external view returns (ILicensingModule); + /// @notice Validates License Tokens for registering a derivative IP. /// @dev This function checks if the License Tokens are valid for the derivative IP registration process. /// for example, whether token is expired. diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index c563a4ec..62e94118 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -138,7 +138,7 @@ library Errors { error LicenseRegistry__DerivativeAlreadyRegistered(address childIpId); error LicenseRegistry__ParentIpTagged(address ipId); error LicenseRegistry__DerivativeIsParent(address ipId); - error LicenseRegistry__ParentIpUnmachedLicenseTemplate(address ipId, address licenseTemplate); + error LicenseRegistry__ParentIpUnmatchedLicenseTemplate(address ipId, address licenseTemplate); error LicenseRegistry__IndexOutOfBounds(address ipId, uint256 index, uint256 length); error LicenseRegistry__LicenseTermsAlreadyAttached(address ipId, address licenseTemplate, uint256 licenseTermsId); error LicenseRegistry__UnmatchedLicenseTemplate(address ipId, address licenseTemplate, address newLicenseTemplate); diff --git a/contracts/registries/LicenseRegistry.sol b/contracts/registries/LicenseRegistry.sol index bc9dda7c..e12ac47b 100644 --- a/contracts/registries/LicenseRegistry.sol +++ b/contracts/registries/LicenseRegistry.sol @@ -459,7 +459,7 @@ 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) { - revert Errors.LicenseRegistry__ParentIpUnmachedLicenseTemplate(parentIpId, licenseTemplate); + revert Errors.LicenseRegistry__ParentIpUnmatchedLicenseTemplate(parentIpId, licenseTemplate); } if (!$.attachedLicenseTerms[parentIpId].contains(licenseTermsId)) { revert Errors.LicenseRegistry__ParentIpHasNoLicenseTerms(parentIpId, licenseTermsId); diff --git a/test/foundry/IPAccount.t.sol b/test/foundry/IPAccount.t.sol index 57a8ce6f..5a8c26e3 100644 --- a/test/foundry/IPAccount.t.sol +++ b/test/foundry/IPAccount.t.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; +import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol"; + import { IIPAccount } from "../../contracts/interfaces/IIPAccount.sol"; import { Errors } from "../../contracts/lib/Errors.sol"; @@ -41,9 +43,7 @@ contract IPAccountTest is BaseTest { assertEq(predictedAccount, deployedAccount); } - // TODO: Fix this test, "vm.addr(2)" hits error AccessController__BothCallerAndRecipientAreNotRegisteredModule - // but we want to test for "AccessController__PermissionDenied" for vm.addr(2) (which is not a module or IPAccount) - /*function test_IPAccount_TokenAndOwnership() public { + function test_IPAccount_TokenAndOwnership() public { address owner = vm.addr(1); uint256 tokenId = 100; @@ -63,20 +63,20 @@ contract IPAccountTest is BaseTest { abi.encodeWithSelector( Errors.AccessController__PermissionDenied.selector, address(ipAccount), - vm.addr(2), + address(module), address(0), bytes4(0) ) ); - ipAccount.isValidSigner(vm.addr(2), ""); + ipAccount.isValidSigner(address(module), ""); assertEq(ipAccount.isValidSigner(owner, ""), IERC6551Account.isValidSigner.selector); // Transfer token to new owner and make sure account owner changes - address newOwner = vm.addr(2); + address newOwner = address(module); vm.prank(owner); - mockNFT.safeTransferFrom(owner, newOwner, tokenId); + mockNFT.transferFrom(owner, newOwner, tokenId); assertEq(ipAccount.isValidSigner(newOwner, ""), IERC6551Account.isValidSigner.selector); - }*/ + } function test_IPAccount_OwnerExecutionPass() public { address owner = vm.addr(1); diff --git a/test/foundry/IPAccount.tree b/test/foundry/IPAccount.tree deleted file mode 100644 index 55d180b5..00000000 --- a/test/foundry/IPAccount.tree +++ /dev/null @@ -1,23 +0,0 @@ -IPAccount -├── when caller is not IPAccount NFT owner -│ ├── when caller registers IPAccount -│ │ └── it should revert -│ └── when caller transfers IPAccount -│ └── it should revert -└── when caller is IPAccount NFT owner - ├── when owner registers IPAccount - │ ├── it should create valid IPAccount - │ └── it should succeed - ├── when owner transfers IPAccount - │ ├── it should make owner no longer IPAccount NFT owner - │ └── it should succeed - └── when owner calls execute - ├── given the abi data is empty - │ └── it should revert - ├── given the abi data is invalid - │ └── it should revert - ├── given the abi encoded module is invalid - │ └── it should revert - └── given the abi encoded module is valid - ├── it should emit an event - └── it should succeed \ No newline at end of file diff --git a/test/foundry/IPAccountImpl.btt.t.sol b/test/foundry/IPAccountImpl.btt.t.sol new file mode 100644 index 00000000..d9a4c3f9 --- /dev/null +++ b/test/foundry/IPAccountImpl.btt.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.23; + +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +// import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import { IERC6551Account } from "erc6551/interfaces/IERC6551Account.sol"; + +import { IIPAccount } from "../../contracts/interfaces/IIPAccount.sol"; +import { IIPAccountStorage } from "../../contracts/interfaces/IIPAccountStorage.sol"; +import { Errors } from "../../contracts/lib/Errors.sol"; + +import { MockModule } from "./mocks/module/MockModule.sol"; +import { BaseTest } from "./utils/BaseTest.t.sol"; + +contract IPAccountImplBTT is BaseTest { + IIPAccount private ipAcct; + uint256 private chainId = block.chainid; + uint256 private tokenId = 55555; + + address private ipOwner; + address private signer; + address private to; + bytes private data; + + bytes private mockModuleDataSuccess = abi.encodeWithSignature("executeSuccessfully(string)", "success"); + bytes private mockModuleDataRevert = abi.encodeWithSignature("executeRevert()"); + + function setUp() public override { + super.setUp(); + + ipOwner = u.alice; + mockNFT.mintId(ipOwner, tokenId); + ipAcct = IIPAccount(payable(ipAssetRegistry.registerIpAccount(chainId, address(mockNFT), tokenId))); + } + + function test_IPAccountImpl_supportsInterface() public { + assertTrue(ipAcct.supportsInterface(type(IIPAccount).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IIPAccountStorage).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IERC6551Account).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IERC721Receiver).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IERC1155Receiver).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IERC1155Receiver).interfaceId)); + assertTrue(ipAcct.supportsInterface(type(IERC165).interfaceId)); + } + + function test_IPAccountImpl_token() public { + (uint256 chainId_, address tokenAddress_, uint256 tokenId_) = ipAcct.token(); + assertEq(chainId_, chainId); + assertEq(tokenAddress_, address(mockNFT)); + assertEq(tokenId_, tokenId); + } + + modifier whenDataLenIsGT0AndLT4() { + data = "123"; + _; + } + + function test_IPAccountImpl_revert_isValidSigner_whenDataLenIsGT0AndLT4() public whenDataLenIsGT0AndLT4 { + signer = ipOwner; + vm.prank(signer); + vm.expectRevert(Errors.IPAccount__InvalidCalldata.selector); + assertEq(ipAcct.isValidSigner(signer, data), bytes4(0)); + } + + modifier whenDataLenIsZeroOrGTE4(bytes memory data_) { + require(data_.length == 0 || data_.length >= 4, "data length must be 0 or >= 4"); + data = data_; + _; + } + + modifier whenSignerIsNotOwner() { + signer = u.bob; + vm.startPrank(signer); + _; + } + + function test_IPAccountImpl_revert_isValidSigner_inAccessControllerFail() + public + whenDataLenIsZeroOrGTE4("") + whenSignerIsNotOwner + { + vm.expectRevert( + abi.encodeWithSelector( + Errors.AccessController__BothCallerAndRecipientAreNotRegisteredModule.selector, + address(signer), + bytes4(0) + ) + ); + ipAcct.isValidSigner(signer, data); + } + + modifier whenSignerIsOwner() { + signer = ipOwner; + vm.startPrank(signer); + _; + } + + function test_IPAccountImpl_isValidSigner_inAccessControllerSucceed() + public + whenDataLenIsZeroOrGTE4("") + whenSignerIsOwner + { + assertEq(ipAcct.isValidSigner(signer, data), IERC6551Account.isValidSigner.selector); + } + + modifier toIsRegisteredModule() { + vm.stopPrank(); + vm.startPrank(u.admin); + MockModule mockModule = new MockModule(address(ipAssetRegistry), address(moduleRegistry), "MockModule"); + moduleRegistry.registerModule(mockModule.name(), address(mockModule)); + to = address(mockModule); + vm.startPrank(signer); // back to the original prank + _; + } + + function test_IPAccountImpl_execute_revert_invalidSigner() + public + whenDataLenIsZeroOrGTE4(mockModuleDataSuccess) + whenSignerIsNotOwner + toIsRegisteredModule + { + vm.expectRevert( + abi.encodeWithSelector( + Errors.AccessController__PermissionDenied.selector, + address(ipAcct), + signer, + to, + MockModule.executeSuccessfully.selector + ) + ); + ipAcct.execute(to, 0, data); + } + + function test_IPAccountImpl_execute_resultSuccess() + public + whenDataLenIsZeroOrGTE4(mockModuleDataSuccess) + whenSignerIsOwner + toIsRegisteredModule + { + uint256 expectedState = ipAcct.state() + 1; + + vm.expectEmit(address(ipAcct)); + emit IIPAccount.Executed(to, 0, data, expectedState); + bytes memory result = ipAcct.execute(to, 0, data); + + assertEq(abi.decode(result, (string)), "success"); + assertEq(ipAcct.state(), expectedState); + } + + function test_IPAccountImpl_execute_resultRevert() + public + whenDataLenIsZeroOrGTE4(mockModuleDataRevert) + whenSignerIsOwner + toIsRegisteredModule + { + uint256 expectedState = ipAcct.state(); + + vm.expectRevert("MockModule: executeRevert"); + ipAcct.execute(to, 0, data); + + assertEq(ipAcct.state(), expectedState); + } + + function test_IPAccountImpl_receiveERC721() public { + assertEq( + IERC721Receiver(address(ipAcct)).onERC721Received(ipOwner, address(0), 111, ""), + IERC721Receiver.onERC721Received.selector + ); + } + + function test_IPAccountImpl_receiveERC1155() public { + assertEq( + IERC1155Receiver(address(ipAcct)).onERC1155Received(ipOwner, address(0), 111, 1, ""), + IERC1155Receiver.onERC1155Received.selector + ); + + uint256[] memory ids = new uint256[](1); + uint256[] memory values = new uint256[](1); + + assertEq( + IERC1155Receiver(address(ipAcct)).onERC1155BatchReceived(ipOwner, address(0), ids, values, ""), + IERC1155Receiver.onERC1155BatchReceived.selector + ); + } +} diff --git a/test/foundry/IPAccountImpl.tree b/test/foundry/IPAccountImpl.tree new file mode 100644 index 00000000..d3c258f1 --- /dev/null +++ b/test/foundry/IPAccountImpl.tree @@ -0,0 +1,48 @@ +IPAccountImpl.sol +# when checking supported interfaces +## it should support IIPAccount interface +## it should support IIPAccountStorage interface +## it should support IERC6551Account interface +## it should support IERC1155Receiver interface +## it should support IERC721Receiver interface +## it should support IERC165 interface +# when checking identifier of IP +## it should return expected chain ID +## it should return expected token contract +## it should return expected token ID +# when checking if signer is valid +## when the data length is greater than zero and less than four +### it should revert +## when the data length is zero or greater than or equal to four +### when checking permission via access controller fails +#### it should revert +### when checking permission via access controller succeeds +#### it should true +#### it sohuld return IERC6551Account.isValidSigner.selector +# when executing +## when signer is invalid in access controlelr +### it should revert +## when signer is valid in access controller +### when call fails +#### it should revert +### when call succeeds +#### it should increment `state` +#### it should emit an event +#### it should return result +# when executing with signature +## given the signer is zero address +### it should revert +## given the deadline is in the past +### it should revert +## given the EIP1976 signature is invalid now +### it should revert +## given the EIP1976 signature is valid +### it should call `_execute` +### it should emit an event +# when receiving ERC721 +## it should return onERC721Received selector +# when receiving ERC1155 +## given batch received +### it should return onERC1155BatchReceived selector +## given single received +### it should return onERC1155Received selector \ No newline at end of file diff --git a/test/foundry/LicenseToken.t.sol b/test/foundry/LicenseToken.t.sol new file mode 100644 index 00000000..ff1a2474 --- /dev/null +++ b/test/foundry/LicenseToken.t.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.23; + +// contract +import { Errors } from "../../contracts/lib/Errors.sol"; +import { PILFlavors } from "../../contracts/lib/PILFlavors.sol"; +import { PILTerms } from "../../contracts/interfaces/modules/licensing/IPILicenseTemplate.sol"; +import { LicenseToken } from "../../contracts/LicenseToken.sol"; + +// test +import { BaseTest } from "./utils/BaseTest.t.sol"; + +contract LicenseTokenTest is BaseTest { + mapping(uint256 => address) internal ipAcct; + mapping(uint256 => address) internal ipOwner; + mapping(uint256 => uint256) internal tokenIds; + + uint256 internal commTermsId; + + function setUp() public override { + super.setUp(); + + ipOwner[1] = u.alice; + ipOwner[2] = u.bob; + + tokenIds[1] = mockNFT.mint(ipOwner[1]); + tokenIds[2] = mockNFT.mint(ipOwner[2]); + + ipAcct[1] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[1]); + vm.label(ipAcct[1], "IPAccount1"); + + commTermsId = registerSelectedPILicenseTerms( + "commercial_use", + PILFlavors.commercialUse({ + mintingFee: 1 ether, + currencyToken: address(USDC), + royaltyPolicy: address(royaltyPolicyLAP) + }) + ); + } + + modifier whenCallerIsProtocolManager() { + vm.startPrank(u.admin); + _; + } + + function test_LicenseToken_setDisputeModule() public whenCallerIsProtocolManager { + licenseToken.setDisputeModule(address(1)); + assertEq(address(licenseToken.disputeModule()), address(1)); + } + + function test_LicenseToken_revert_setDisputeModule_zeroDisputeModule() public whenCallerIsProtocolManager { + vm.expectRevert(Errors.LicenseToken__ZeroDisputeModule.selector); + licenseToken.setDisputeModule(address(0)); + } + + function test_LicenseToken_setLicensingModule() public whenCallerIsProtocolManager { + licenseToken.setLicensingModule(address(1)); + assertEq(address(licenseToken.licensingModule()), address(1)); + } + + function test_LicenseToken_revert_setLicensingModule_zeroLicensingModule() public whenCallerIsProtocolManager { + vm.expectRevert(Errors.LicenseToken__ZeroLicensingModule.selector); + licenseToken.setLicensingModule(address(0)); + } + + function test_LicenseToken_setLicensingImageUrl() public whenCallerIsProtocolManager { + vm.expectEmit(address(licenseToken)); + emit LicenseToken.BatchMetadataUpdate(1, 0); + licenseToken.setLicensingImageUrl("new_url"); + } + + function test_LicenseToken_isLicenseTokenRevoked() public { + uint256 mintAmount = 10; + + vm.prank(address(licensingModule)); + uint256 startLicenseTokenId = licenseToken.mintLicenseTokens({ + licensorIpId: ipAcct[1], + licenseTemplate: address(pilTemplate), + licenseTermsId: commTermsId, + amount: mintAmount, + minter: ipOwner[1], + receiver: ipOwner[1] + }); + + for (uint256 i = 0; i < mintAmount; i++) { + assertFalse(licenseToken.isLicenseTokenRevoked(startLicenseTokenId + i)); + } + + _disputeIp(u.bob, ipAcct[1]); + + // After ipAcct[1] is disputed, expect all license tokens with licensor = ipAcct[1] to be revoked + for (uint256 i = 0; i < mintAmount; i++) { + assertTrue(licenseToken.isLicenseTokenRevoked(startLicenseTokenId + i)); + } + } + + function test_LicenseToken_revert_transfer_revokedLicense() public { + vm.prank(address(licensingModule)); + uint256 licenseTokenId = licenseToken.mintLicenseTokens({ + licensorIpId: ipAcct[1], + licenseTemplate: address(pilTemplate), + licenseTermsId: commTermsId, + amount: 1, + minter: ipOwner[1], + receiver: ipOwner[1] + }); + + vm.prank(ipOwner[1]); + licenseToken.transferFrom(ipOwner[1], ipOwner[2], licenseTokenId); + assertEq(licenseToken.balanceOf(ipOwner[1]), 0); + assertEq(licenseToken.balanceOf(ipOwner[2]), 1); + + // make all license tokens with "licensor = ipAcct[1]" revoked + _disputeIp(u.bob, ipAcct[1]); + + vm.prank(ipOwner[2]); + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseToken__RevokedLicense.selector, licenseTokenId)); + licenseToken.transferFrom(ipOwner[2], ipOwner[1], licenseTokenId); + assertEq(licenseToken.balanceOf(ipOwner[1]), 0); + assertEq(licenseToken.balanceOf(ipOwner[2]), 1); + } + + function test_LicenseToken_revert_transfer_notTransferable() public { + uint256 licenseTermsId = pilTemplate.registerLicenseTerms( + PILTerms({ + transferable: false, + royaltyPolicy: address(0), + mintingFee: 0, + expiration: 0, + commercialUse: false, + commercialAttribution: false, + commercializerChecker: address(0), + commercializerCheckerData: "", + commercialRevShare: 0, + commercialRevCelling: 0, + derivativesAllowed: true, + derivativesAttribution: true, + derivativesApproval: false, + derivativesReciprocal: true, + derivativeRevCelling: 0, + currency: address(USDC), + uri: "" + }) + ); + + vm.prank(address(licensingModule)); + uint256 licenseTokenId = licenseToken.mintLicenseTokens({ + licensorIpId: ipAcct[1], + licenseTemplate: address(pilTemplate), + licenseTermsId: licenseTermsId, + amount: 1, + minter: ipOwner[1], + receiver: ipOwner[1] + }); + + vm.expectRevert(Errors.LicenseToken__NotTransferable.selector); + vm.prank(ipOwner[1]); + licenseToken.transferFrom(ipOwner[1], ipOwner[2], licenseTokenId); + assertEq(licenseToken.balanceOf(ipOwner[1]), 1); + assertEq(licenseToken.balanceOf(ipOwner[2]), 0); + } +} diff --git a/test/foundry/integration/flows/disputes/Disputes.t.sol b/test/foundry/integration/flows/disputes/Disputes.t.sol index e75638fb..2c351dcf 100644 --- a/test/foundry/integration/flows/disputes/Disputes.t.sol +++ b/test/foundry/integration/flows/disputes/Disputes.t.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.23; // external import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // contract import { Errors } from "../../../../../contracts/lib/Errors.sol"; @@ -66,7 +65,7 @@ contract Flows_Integration_Disputes is BaseIntegration { }); } - function test_Integration_Disputes_revert_cannotRegisterDerivativeFromDisputedIpParent() public { + function test_Integration_Disputes_revert_cannotRegisterDerivativeWithTokensFromDisputedIpParent() public { assertEq(licenseToken.balanceOf(u.carl), 0); vm.prank(u.carl); @@ -90,6 +89,26 @@ contract Flows_Integration_Disputes is BaseIntegration { licensingModule.registerDerivativeWithLicenseTokens(ipAcct[3], licenseIds, ""); } + function test_Integration_Disputes_revert_cannotRegisterDerivativeFromDisputedIpParent() public { + _disputeIp(u.bob, ipAcct[1]); + + address[] memory parentIpIds = new address[](1); + parentIpIds[0] = ipAcct[1]; + + uint256[] memory licenseTermsIds = new uint256[](1); + licenseTermsIds[0] = ncSocialRemixTermsId; + + vm.prank(u.carl); + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__ParentIpTagged.selector, ipAcct[1])); + licensingModule.registerDerivative({ + childIpId: ipAcct[3], + parentIpIds: parentIpIds, + licenseTermsIds: licenseTermsIds, + licenseTemplate: address(pilTemplate), + royaltyContext: "" + }); + } + function test_Integration_Disputes_transferLicenseAfterIpDispute() public { assertEq(licenseToken.balanceOf(u.carl), 0); @@ -131,14 +150,4 @@ contract Flows_Integration_Disputes is BaseIntegration { }); assertEq(licenseToken.balanceOf(u.carl), 1); } - - function _disputeIp(address disputeInitiator, address ipAddrToDispute) internal returns (uint256 disputeId) { - vm.startPrank(disputeInitiator); - IERC20(USDC).approve(address(arbitrationPolicySP), ARBITRATION_PRICE); - disputeId = disputeModule.raiseDispute(ipAddrToDispute, string("urlExample"), "PLAGIARISM", ""); - vm.stopPrank(); - - vm.prank(u.relayer); // admin is a judge - disputeModule.setDisputeJudgement(disputeId, true, ""); - } } diff --git a/test/foundry/integration/flows/licensing/LicensingIntegration.t.sol b/test/foundry/integration/flows/licensing/LicensingIntegration.t.sol index 9c9eb11d..d9e25bb8 100644 --- a/test/foundry/integration/flows/licensing/LicensingIntegration.t.sol +++ b/test/foundry/integration/flows/licensing/LicensingIntegration.t.sol @@ -1,344 +1,144 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.23; -// external -import { Test } from "forge-std/Test.sol"; -import { ERC6551Registry } from "erc6551/ERC6551Registry.sol"; -import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { AccessManager } from "@openzeppelin/contracts/access/manager/AccessManager.sol"; -import { CREATE3 } from "@solady/src/utils/CREATE3.sol"; - // contracts -import { AccessController } from "../../../../../contracts/access/AccessController.sol"; -import { IPAccountImpl } from "../../../../../contracts/IPAccountImpl.sol"; -import { IPAssetRegistry } from "../../../../../contracts/registries/IPAssetRegistry.sol"; -import { ModuleRegistry } from "../../../../../contracts/registries/ModuleRegistry.sol"; -import { LicenseRegistry } from "../../../../../contracts/registries/LicenseRegistry.sol"; -import { RoyaltyModule } from "../../../../../contracts/modules/royalty/RoyaltyModule.sol"; -import { RoyaltyPolicyLAP } from "../../../../../contracts/modules/royalty/policies/RoyaltyPolicyLAP.sol"; -import { DisputeModule } from "../../../../../contracts/modules/dispute/DisputeModule.sol"; -import { LicensingModule } from "../../../../../contracts/modules/licensing/LicensingModule.sol"; -import { ArbitrationPolicySP } from "../../../../../contracts/modules/dispute/policies/ArbitrationPolicySP.sol"; -import { IpRoyaltyVault } from "../../../../../contracts/modules/royalty/policies/IpRoyaltyVault.sol"; -import { LicenseToken } from "../../../../../contracts/LicenseToken.sol"; -import { DISPUTE_MODULE_KEY, LICENSING_MODULE_KEY, ROYALTY_MODULE_KEY } from "contracts/lib/modules/Module.sol"; -import { PILicenseTemplate } from "../../../../../contracts/modules/licensing/PILicenseTemplate.sol"; import { PILFlavors } from "../../../../../contracts/lib/PILFlavors.sol"; -import { MockERC20 } from "../../../mocks/token/MockERC20.sol"; -import { MockERC721 } from "../../../mocks/token/MockERC721.sol"; -import { TestProxyHelper } from "../../../utils/TestProxyHelper.sol"; +import { Errors } from "../../../../../contracts/lib/Errors.sol"; +import { PILicenseTemplate } from "../../../../../contracts/modules/licensing/PILicenseTemplate.sol"; -contract e2e is Test { - MockERC20 erc20; - MockERC721 mockNft; - uint256 internal constant ARBITRATION_PRICE = 1000; - - address admin; - address alice; - address bob; - address charlie; - address dave; - address eve; - - AccessManager protocolAccessManager; - AccessController accessController; - ModuleRegistry moduleRegistry; - ERC6551Registry erc6551Registry; - IPAccountImpl ipAccountImpl; - IPAssetRegistry ipAssetRegistry; - LicenseRegistry licenseRegistry; - LicenseToken licenseToken; - RoyaltyModule royaltyModule; - DisputeModule disputeModule; - LicensingModule licensingModule; - PILicenseTemplate piLicenseTemplate; - RoyaltyPolicyLAP royaltyPolicyLAP; - - address ipId1; - address ipId2; - address ipId3; - address ipId6; - address ipId7; +// test +import { BaseIntegration } from "../../BaseIntegration.t.sol"; +import { TestProxyHelper } from "../../../utils/TestProxyHelper.sol"; +import { MockERC20 } from "../../../mocks/token/MockERC20.sol"; +contract LicensingIntegrationTest is BaseIntegration { error ERC721NonexistentToken(uint256 tokenId); - function setUp() public { - admin = vm.addr(1); - alice = vm.addr(2); - bob = vm.addr(3); - charlie = vm.addr(4); - dave = vm.addr(5); - eve = vm.addr(6); - - erc20 = new MockERC20(); - mockNft = new MockERC721("ape"); - protocolAccessManager = new AccessManager(admin); - - // Deploy contracts - - bytes32 ipAccountImplSalt = keccak256( - abi.encode(type(IPAccountImpl).creationCode, address(this), block.timestamp) - ); - address ipAccountImplAddr = CREATE3.getDeployed(ipAccountImplSalt); - - address impl = address(new AccessController()); - accessController = AccessController( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(AccessController.initialize, address(protocolAccessManager)) - ) - ); - - impl = address(new ModuleRegistry()); - moduleRegistry = ModuleRegistry( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(AccessController.initialize, address(protocolAccessManager)) - ) - ); - - erc6551Registry = new ERC6551Registry(); - impl = address(new IPAssetRegistry(address(erc6551Registry), ipAccountImplAddr)); - ipAssetRegistry = IPAssetRegistry( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(IPAssetRegistry.initialize, address(protocolAccessManager)) - ) - ); - - impl = address(new LicenseRegistry()); - licenseRegistry = LicenseRegistry( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(LicenseRegistry.initialize, (address(protocolAccessManager))) - ) - ); + mapping(uint256 => uint256 tokenId) private tokenIds; + mapping(uint256 tokenId => address ipId) private ipAcct; - bytes memory ipAccountImplCode = abi.encodePacked( - type(IPAccountImpl).creationCode, - abi.encode( - address(accessController), - address(ipAssetRegistry), - address(licenseRegistry), - address(moduleRegistry) - ) - ); - this.create3Deploy(ipAccountImplSalt, ipAccountImplCode, 0); - ipAccountImpl = IPAccountImpl(payable(ipAccountImplAddr)); - - impl = address(new LicenseToken()); - licenseToken = LicenseToken( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(LicenseToken.initialize, (address(protocolAccessManager), "image_url")) - ) - ); - - impl = address( - new DisputeModule(address(accessController), address(ipAssetRegistry), address(licenseRegistry)) - ); + PILicenseTemplate private anotherPILTemplate; - disputeModule = DisputeModule( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(DisputeModule.initialize, (address(protocolAccessManager))) - ) - ); - - impl = address(new RoyaltyModule(address(disputeModule), address(licenseRegistry))); - royaltyModule = RoyaltyModule( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(RoyaltyModule.initialize, (address(protocolAccessManager))) - ) - ); - vm.label(address(royaltyModule), "RoyaltyModule"); - - impl = address( - new LicensingModule( - address(accessController), - address(ipAssetRegistry), - address(royaltyModule), - address(licenseRegistry), - address(disputeModule), - address(licenseToken) - ) - ); - - licensingModule = LicensingModule( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(LicensingModule.initialize, (address(protocolAccessManager))) - ) - ); + function setUp() public override { + super.setUp(); + // new token to make this test work (asserts) temporarily erc20 = new MockERC20(); - mockNft = new MockERC721("ape"); + vm.prank(u.admin); + royaltyModule.whitelistRoyaltyToken(address(erc20), true); - impl = address(new ArbitrationPolicySP(address(disputeModule), address(erc20), ARBITRATION_PRICE)); - ArbitrationPolicySP arbitrationPolicySP = ArbitrationPolicySP( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(ArbitrationPolicySP.initialize, (address(protocolAccessManager))) - ) - ); + tokenIds[1] = mockNFT.mint(u.alice); + tokenIds[2] = mockNFT.mint(u.bob); + tokenIds[3] = mockNFT.mint(u.carl); + tokenIds[6] = mockNFT.mint(u.dan); + tokenIds[7] = mockNFT.mint(u.eve); - impl = address(new RoyaltyPolicyLAP(address(royaltyModule), address(licensingModule))); - royaltyPolicyLAP = RoyaltyPolicyLAP( - TestProxyHelper.deployUUPSProxy( - impl, - abi.encodeCall(RoyaltyPolicyLAP.initialize, (address(protocolAccessManager))) - ) - ); + ipAcct[1] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[1]); + ipAcct[2] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[2]); + ipAcct[3] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[3]); + ipAcct[6] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[6]); + ipAcct[7] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[7]); - impl = address( + address anotherPILTemplateImpl = address( new PILicenseTemplate( address(accessController), - address(ipAssetRegistry), + address(ipAccountRegistry), address(licenseRegistry), address(royaltyModule) ) ); - piLicenseTemplate = PILicenseTemplate( + + anotherPILTemplate = PILicenseTemplate( TestProxyHelper.deployUUPSProxy( - impl, + anotherPILTemplateImpl, abi.encodeCall( PILicenseTemplate.initialize, - (address(protocolAccessManager), "PIL", "PIL-metadata-url") + ( + address(protocolAccessManager), + "another-pil", + "https://github.com/storyprotocol/protocol-core/blob/main/PIL_Beta_Final_2024_02.pdf" + ) ) ) ); - - // Configure protocol - vm.startPrank(admin); - - address ipRoyaltyVaultImplementation = address( - new IpRoyaltyVault(address(royaltyPolicyLAP), address(disputeModule)) - ); - address ipRoyaltyVaultBeacon = address( - new UpgradeableBeacon(ipRoyaltyVaultImplementation, address(protocolAccessManager)) - ); - royaltyPolicyLAP.setIpRoyaltyVaultBeacon(ipRoyaltyVaultBeacon); - - royaltyPolicyLAP.setSnapshotInterval(7 days); - - accessController.setAddresses(address(ipAssetRegistry), address(moduleRegistry)); - - moduleRegistry.registerModule(DISPUTE_MODULE_KEY, address(disputeModule)); - moduleRegistry.registerModule(LICENSING_MODULE_KEY, address(licensingModule)); - moduleRegistry.registerModule(ROYALTY_MODULE_KEY, address(royaltyModule)); - - royaltyModule.setLicensingModule(address(licensingModule)); - royaltyModule.whitelistRoyaltyToken(address(erc20), true); - royaltyModule.whitelistRoyaltyPolicy(address(royaltyPolicyLAP), true); - - disputeModule.whitelistDisputeTag("PLAGIARISM", true); - disputeModule.whitelistArbitrationPolicy(address(arbitrationPolicySP), true); - disputeModule.whitelistArbitrationRelayer(address(arbitrationPolicySP), admin, true); - disputeModule.setBaseArbitrationPolicy(address(arbitrationPolicySP)); - - licenseRegistry.setDisputeModule(address(disputeModule)); - licenseRegistry.setLicensingModule(address(licensingModule)); - licenseRegistry.registerLicenseTemplate(address(piLicenseTemplate)); - - licenseToken.setDisputeModule(address(disputeModule)); - licenseToken.setLicensingModule(address(licensingModule)); - - vm.stopPrank(); - } - - function create3Deploy(bytes32 salt, bytes calldata creationCode, uint256 value) external returns (address) { - return CREATE3.deploy(salt, creationCode, value); } - function test_e2e() public { - uint256 tokenId1 = mockNft.mint(alice); - uint256 tokenId2 = mockNft.mint(bob); - uint256 tokenId3 = mockNft.mint(charlie); - uint256 tokenId6 = mockNft.mint(dave); - uint256 tokenId7 = mockNft.mint(eve); - - ipId1 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId1); - ipId2 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId2); - ipId3 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId3); - ipId6 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId6); - ipId7 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId7); - + function test_LicensingIntegration_Simple() public { // register license terms - uint256 lcId1 = piLicenseTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); + uint256 lcId1 = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); assertEq(lcId1, 1); - uint256 lcId2 = piLicenseTemplate.registerLicenseTerms( + uint256 lcId2 = pilTemplate.registerLicenseTerms( PILFlavors.commercialRemix(100, 10, address(royaltyPolicyLAP), address(erc20)) ); assertEq(lcId2, 2); assertEq( - piLicenseTemplate.getLicenseTermsId( + pilTemplate.getLicenseTermsId( PILFlavors.commercialRemix(100, 10, address(royaltyPolicyLAP), address(erc20)) ), 2 ); - assertTrue(piLicenseTemplate.exists(2)); + assertTrue(pilTemplate.exists(2)); - assertTrue(piLicenseTemplate.exists(lcId2)); + assertTrue(pilTemplate.exists(lcId2)); // attach licenses - vm.startPrank(alice); - licensingModule.attachLicenseTerms(ipId1, address(piLicenseTemplate), 1); + vm.startPrank(u.alice); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), 1); - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId1, address(piLicenseTemplate), 1), true); - assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId1), 1); + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[1], address(pilTemplate), 1), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[1]), 1); - (address attachedTemplate, uint256 attachedId) = licenseRegistry.getAttachedLicenseTerms(ipId1, 0); - assertEq(attachedTemplate, address(piLicenseTemplate)); + (address attachedTemplate, uint256 attachedId) = licenseRegistry.getAttachedLicenseTerms(ipAcct[1], 0); + assertEq(attachedTemplate, address(pilTemplate)); assertEq(attachedId, 1); - licensingModule.attachLicenseTerms(ipId1, address(piLicenseTemplate), 2); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), 2); - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId1, address(piLicenseTemplate), 2), true); - assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId1), 2); + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[1], address(pilTemplate), 2), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[1]), 2); - (attachedTemplate, attachedId) = licenseRegistry.getAttachedLicenseTerms(ipId1, 1); - assertEq(attachedTemplate, address(piLicenseTemplate)); + (attachedTemplate, attachedId) = licenseRegistry.getAttachedLicenseTerms(ipAcct[1], 1); + assertEq(attachedTemplate, address(pilTemplate)); assertEq(attachedId, 2); vm.stopPrank(); // register derivative directly - vm.startPrank(bob); + vm.startPrank(u.bob); address[] memory parentIpIds = new address[](1); uint256[] memory licenseTermsIds = new uint256[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; licenseTermsIds[0] = 1; - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(piLicenseTemplate), ""); - - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId2, address(piLicenseTemplate), 1), true); - 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); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); + + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[2], address(pilTemplate), 1), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[2]), 1); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[2]), true); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[2]), false); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[1]), true); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[1]), false); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[1]), 1); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[2]), 0); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 0), ipAcct[2]); + assertEq(licenseRegistry.getParentIp(ipAcct[2], 0), ipAcct[1]); + assertEq(licenseRegistry.getParentIpCount(ipAcct[2]), 1); vm.stopPrank(); // mint license token - vm.startPrank(charlie); + vm.startPrank(u.carl); uint256 lcTokenId = licensingModule.mintLicenseTokens( - ipId1, - address(piLicenseTemplate), + ipAcct[1], + address(pilTemplate), 1, 1, - address(charlie), + address(u.carl), "" ); - assertEq(licenseToken.ownerOf(lcTokenId), charlie); + assertEq(licenseToken.ownerOf(lcTokenId), u.carl); assertEq(licenseToken.getLicenseTermsId(lcTokenId), 1); - assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(piLicenseTemplate)); - assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1); + assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate)); + assertEq(licenseToken.getLicensorIpId(lcTokenId), ipAcct[1]); assertEq(licenseToken.getExpirationTime(lcTokenId), 0); assertEq(licenseToken.totalMintedTokens(), 1); @@ -346,20 +146,20 @@ contract e2e is Test { uint256[] memory licenseTokens = new uint256[](1); licenseTokens[0] = lcTokenId; - licensingModule.registerDerivativeWithLicenseTokens(ipId3, licenseTokens, ""); - - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId3, address(piLicenseTemplate), 1), true); - assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId3), 1); - assertEq(licenseRegistry.isDerivativeIp(ipId3), true); - assertEq(licenseRegistry.hasDerivativeIps(ipId3), false); - assertEq(licenseRegistry.hasDerivativeIps(ipId1), true); - assertEq(licenseRegistry.isDerivativeIp(ipId1), false); - assertEq(licenseRegistry.getDerivativeIpCount(ipId1), 2); - assertEq(licenseRegistry.getDerivativeIpCount(ipId2), 0); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 0), ipId2); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 1), ipId3); - assertEq(licenseRegistry.getParentIp(ipId3, 0), ipId1); - assertEq(licenseRegistry.getParentIpCount(ipId3), 1); + licensingModule.registerDerivativeWithLicenseTokens(ipAcct[3], licenseTokens, ""); + + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[3], address(pilTemplate), 1), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[3]), 1); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[3]), true); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[3]), false); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[1]), true); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[1]), false); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[1]), 2); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[2]), 0); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 0), ipAcct[2]); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 1), ipAcct[3]); + assertEq(licenseRegistry.getParentIp(ipAcct[3], 0), ipAcct[1]); + assertEq(licenseRegistry.getParentIpCount(ipAcct[3]), 1); vm.expectRevert(abi.encodeWithSelector(ERC721NonexistentToken.selector, lcTokenId)); assertEq(licenseToken.ownerOf(lcTokenId), address(0)); @@ -367,39 +167,39 @@ contract e2e is Test { vm.stopPrank(); // mint license token with payments - vm.startPrank(dave); - erc20.mint(dave, 1000); + vm.startPrank(u.dan); + erc20.mint(u.dan, 1000); erc20.approve(address(royaltyPolicyLAP), 100); - lcTokenId = licensingModule.mintLicenseTokens(ipId1, address(piLicenseTemplate), 2, 1, address(dave), ""); + lcTokenId = licensingModule.mintLicenseTokens(ipAcct[1], address(pilTemplate), 2, 1, address(u.dan), ""); - assertEq(licenseToken.ownerOf(lcTokenId), dave); + assertEq(licenseToken.ownerOf(lcTokenId), u.dan); assertEq(licenseToken.getLicenseTermsId(lcTokenId), 2); - assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(piLicenseTemplate)); - assertEq(licenseToken.getLicensorIpId(lcTokenId), ipId1); + assertEq(licenseToken.getLicenseTemplate(lcTokenId), address(pilTemplate)); + assertEq(licenseToken.getLicensorIpId(lcTokenId), ipAcct[1]); assertEq(licenseToken.getExpirationTime(lcTokenId), 0); assertEq(licenseToken.totalMintedTokens(), 2); - assertEq(erc20.balanceOf(dave), 900); + assertEq(erc20.balanceOf(u.dan), 900); // register derivative with license tokens licenseTokens = new uint256[](1); licenseTokens[0] = lcTokenId; - licensingModule.registerDerivativeWithLicenseTokens(ipId6, licenseTokens, ""); - - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId6, address(piLicenseTemplate), 2), true); - assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId6), 1); - assertEq(licenseRegistry.isDerivativeIp(ipId6), true); - assertEq(licenseRegistry.hasDerivativeIps(ipId6), false); - assertEq(licenseRegistry.hasDerivativeIps(ipId1), true); - assertEq(licenseRegistry.isDerivativeIp(ipId1), false); - assertEq(licenseRegistry.getDerivativeIpCount(ipId1), 3); - assertEq(licenseRegistry.getDerivativeIpCount(ipId6), 0); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 0), ipId2); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 1), ipId3); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 2), ipId6); - assertEq(licenseRegistry.getParentIp(ipId6, 0), ipId1); - assertEq(licenseRegistry.getParentIpCount(ipId6), 1); + licensingModule.registerDerivativeWithLicenseTokens(ipAcct[6], licenseTokens, ""); + + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[6], address(pilTemplate), 2), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[6]), 1); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[6]), true); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[6]), false); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[1]), true); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[1]), false); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[1]), 3); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[6]), 0); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 0), ipAcct[2]); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 1), ipAcct[3]); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 2), ipAcct[6]); + assertEq(licenseRegistry.getParentIp(ipAcct[6], 0), ipAcct[1]); + assertEq(licenseRegistry.getParentIpCount(ipAcct[6]), 1); vm.expectRevert(abi.encodeWithSelector(ERC721NonexistentToken.selector, lcTokenId)); assertEq(licenseToken.ownerOf(lcTokenId), address(0)); @@ -407,29 +207,100 @@ contract e2e is Test { vm.stopPrank(); // register derivative directly with payments - vm.startPrank(eve); - erc20.mint(eve, 1000); + vm.startPrank(u.eve); + erc20.mint(u.eve, 1000); erc20.approve(address(royaltyPolicyLAP), 100); parentIpIds = new address[](1); licenseTermsIds = new uint256[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; licenseTermsIds[0] = 2; - licensingModule.registerDerivative(ipId7, parentIpIds, licenseTermsIds, address(piLicenseTemplate), ""); - - assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipId7, address(piLicenseTemplate), 2), true); - assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipId7), 1); - assertEq(licenseRegistry.isDerivativeIp(ipId7), true); - assertEq(licenseRegistry.hasDerivativeIps(ipId7), false); - assertEq(licenseRegistry.hasDerivativeIps(ipId1), true); - assertEq(licenseRegistry.isDerivativeIp(ipId1), false); - assertEq(licenseRegistry.getDerivativeIpCount(ipId1), 4); - assertEq(licenseRegistry.getDerivativeIpCount(ipId7), 0); - assertEq(licenseRegistry.getDerivativeIp(ipId1, 3), ipId7); - assertEq(licenseRegistry.getParentIp(ipId7, 0), ipId1); - assertEq(licenseRegistry.getParentIpCount(ipId7), 1); + licensingModule.registerDerivative(ipAcct[7], parentIpIds, licenseTermsIds, address(pilTemplate), ""); + + assertEq(licenseRegistry.hasIpAttachedLicenseTerms(ipAcct[7], address(pilTemplate), 2), true); + assertEq(licenseRegistry.getAttachedLicenseTermsCount(ipAcct[7]), 1); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[7]), true); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[7]), false); + assertEq(licenseRegistry.hasDerivativeIps(ipAcct[1]), true); + assertEq(licenseRegistry.isDerivativeIp(ipAcct[1]), false); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[1]), 4); + assertEq(licenseRegistry.getDerivativeIpCount(ipAcct[7]), 0); + assertEq(licenseRegistry.getDerivativeIp(ipAcct[1], 3), ipAcct[7]); + assertEq(licenseRegistry.getParentIp(ipAcct[7], 0), ipAcct[1]); + assertEq(licenseRegistry.getParentIpCount(ipAcct[7]), 1); assertEq(licenseToken.totalMintedTokens(), 2); - assertEq(erc20.balanceOf(eve), 900); + assertEq(erc20.balanceOf(u.eve), 900); vm.stopPrank(); } + + function test_LicensingIntegration_revert_registerDerivative_parentIpUnmatchedLicenseTemplate() public { + uint256 commRemixTermsId = anotherPILTemplate.registerLicenseTerms( + PILFlavors.commercialRemix({ + commercialRevShare: 100, + mintingFee: 1 ether, + royaltyPolicy: address(royaltyPolicyLAP), + currencyToken: address(USDC) + }) + ); + + address[] memory parentIpIds = new address[](1); + parentIpIds[0] = ipAcct[1]; + + uint256[] memory licenseTermsIds = new uint256[](1); + licenseTermsIds[0] = commRemixTermsId; + + vm.prank(u.carl); + vm.expectRevert( + abi.encodeWithSelector( + Errors.LicenseRegistry__ParentIpUnmatchedLicenseTemplate.selector, + ipAcct[1], + address(anotherPILTemplate) + ) + ); + licensingModule.registerDerivative({ + childIpId: ipAcct[3], + parentIpIds: parentIpIds, + licenseTermsIds: licenseTermsIds, + licenseTemplate: address(anotherPILTemplate), + royaltyContext: "" + }); + } + + function test_LicensingIntegration_revert_registerDerivative_parentIpNoLicenseTerms() public { + uint256 ncSocialRemixTermsId = registerSelectedPILicenseTerms_NonCommercialSocialRemixing(); + uint256 commRemixTermsId = registerSelectedPILicenseTerms( + "commercial_remix", + PILFlavors.commercialRemix({ + commercialRevShare: 100, + mintingFee: 1 ether, + royaltyPolicy: address(royaltyPolicyLAP), + currencyToken: address(USDC) + }) + ); + + vm.prank(u.alice); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), ncSocialRemixTermsId); + + address[] memory parentIpIds = new address[](1); + parentIpIds[0] = ipAcct[1]; + + uint256[] memory licenseTermsIds = new uint256[](1); + licenseTermsIds[0] = commRemixTermsId; + + vm.prank(u.carl); + vm.expectRevert( + abi.encodeWithSelector( + Errors.LicenseRegistry__ParentIpHasNoLicenseTerms.selector, + ipAcct[1], + commRemixTermsId + ) + ); + licensingModule.registerDerivative({ + childIpId: ipAcct[3], + parentIpIds: parentIpIds, + licenseTermsIds: licenseTermsIds, + licenseTemplate: address(pilTemplate), + royaltyContext: "" + }); + } } diff --git a/test/foundry/integration/flows/licensing/LicensingScenarios.t.sol b/test/foundry/integration/flows/licensing/LicensingScenarios.t.sol index 24340e35..bd34abea 100644 --- a/test/foundry/integration/flows/licensing/LicensingScenarios.t.sol +++ b/test/foundry/integration/flows/licensing/LicensingScenarios.t.sol @@ -10,7 +10,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { PILFlavors } from "../../../../../contracts/lib/PILFlavors.sol"; // test -import { BaseIntegration } from "../..//BaseIntegration.t.sol"; +import { BaseIntegration } from "../../BaseIntegration.t.sol"; contract Licensing_Scenarios is BaseIntegration { using EnumerableSet for EnumerableSet.UintSet; diff --git a/test/foundry/modules/licensing/PILicenseTemplate.t.sol b/test/foundry/modules/licensing/PILicenseTemplate.t.sol index 527d3e21..2e8d8af7 100644 --- a/test/foundry/modules/licensing/PILicenseTemplate.t.sol +++ b/test/foundry/modules/licensing/PILicenseTemplate.t.sol @@ -10,48 +10,44 @@ import { PILFlavors } from "../../../../contracts/lib/PILFlavors.sol"; import { PILTerms } from "../../../../contracts/interfaces/modules/licensing/IPILicenseTemplate.sol"; // test -import { MockERC721 } from "../../mocks/token/MockERC721.sol"; import { BaseTest } from "../../utils/BaseTest.t.sol"; +import { MockERC721 } from "../../mocks/token/MockERC721.sol"; contract PILicenseTemplateTest is BaseTest { using Strings for *; - MockERC721 internal mockNft = new MockERC721("MockERC721"); MockERC721 internal gatedNftFoo = new MockERC721{ salt: bytes32(uint256(1)) }("GatedNftFoo"); MockERC721 internal gatedNftBar = new MockERC721{ salt: bytes32(uint256(2)) }("GatedNftBar"); - address public ipId1; - address public ipId2; - address public ipId3; - address public ipId5; - address public ipOwner1 = address(0x111); - address public ipOwner2 = address(0x222); - address public ipOwner3 = address(0x333); - address public ipOwner5 = address(0x444); - uint256 public tokenId1 = 1; - uint256 public tokenId2 = 2; - uint256 public tokenId3 = 3; - uint256 public tokenId5 = 5; - - address public licenseHolder = address(0x101); + mapping(uint256 => address) internal ipAcct; + mapping(uint256 => address) internal ipOwner; + mapping(uint256 => uint256) internal tokenIds; + + address internal licenseHolder = address(0x101); function setUp() public override { super.setUp(); + + ipOwner[1] = u.alice; + ipOwner[2] = u.bob; + ipOwner[3] = u.carl; + ipOwner[5] = u.dan; + // Create IPAccounts - mockNft.mintId(ipOwner1, tokenId1); - mockNft.mintId(ipOwner2, tokenId2); - mockNft.mintId(ipOwner3, tokenId3); - mockNft.mintId(ipOwner5, tokenId5); - - ipId1 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId1); - ipId2 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId2); - ipId3 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId3); - ipId5 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId5); - - vm.label(ipId1, "IPAccount1"); - vm.label(ipId2, "IPAccount2"); - vm.label(ipId3, "IPAccount3"); - vm.label(ipId5, "IPAccount5"); + tokenIds[1] = mockNFT.mint(ipOwner[1]); + tokenIds[2] = mockNFT.mint(ipOwner[2]); + tokenIds[3] = mockNFT.mint(ipOwner[3]); + tokenIds[5] = mockNFT.mint(ipOwner[5]); + + ipAcct[1] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[1]); + ipAcct[2] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[2]); + ipAcct[3] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[3]); + ipAcct[5] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[5]); + + vm.label(ipAcct[1], "IPAccount1"); + vm.label(ipAcct[2], "IPAccount2"); + vm.label(ipAcct[3], "IPAccount3"); + vm.label(ipAcct[5], "IPAccount5"); } // this contract is for testing for each PILicenseTemplate's functions // register license terms with PILTerms struct @@ -292,7 +288,7 @@ contract PILicenseTemplateTest is BaseTest { royaltyPolicy: address(royaltyPolicyLAP) }) ); - bool result = pilTemplate.verifyMintLicenseToken(commUseTermsId, ipId2, ipId1, 1); + bool result = pilTemplate.verifyMintLicenseToken(commUseTermsId, ipAcct[2], ipAcct[1], 1); assertTrue(result); } @@ -304,19 +300,19 @@ contract PILicenseTemplateTest is BaseTest { royaltyPolicy: address(royaltyPolicyLAP) }) ); - vm.prank(ipOwner1); - licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), commUseTermsId); + vm.prank(ipOwner[1]); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), commUseTermsId); address[] memory parentIpIds = new address[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](1); licenseTermsIds[0] = commUseTermsId; - vm.prank(ipOwner2); - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), ""); + vm.prank(ipOwner[2]); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); uint256 anotherTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); - bool result = pilTemplate.verifyMintLicenseToken(anotherTermsId, ipOwner3, ipId2, 1); + bool result = pilTemplate.verifyMintLicenseToken(anotherTermsId, ipOwner[3], ipAcct[2], 1); assertFalse(result); } @@ -328,17 +324,17 @@ contract PILicenseTemplateTest is BaseTest { royaltyPolicy: address(royaltyPolicyLAP) }) ); - vm.prank(ipOwner1); - licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), commUseTermsId); + vm.prank(ipOwner[1]); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), commUseTermsId); address[] memory parentIpIds = new address[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](1); licenseTermsIds[0] = commUseTermsId; - vm.prank(ipOwner2); - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), ""); + vm.prank(ipOwner[2]); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); - bool result = pilTemplate.verifyMintLicenseToken(commUseTermsId, ipOwner3, ipId2, 1); + bool result = pilTemplate.verifyMintLicenseToken(commUseTermsId, ipOwner[3], ipAcct[2], 1); assertFalse(result); } @@ -352,7 +348,7 @@ contract PILicenseTemplateTest is BaseTest { }) ); - bool result = pilTemplate.verifyRegisterDerivative(ipId2, ipId1, commUseTermsId, ipOwner2); + bool result = pilTemplate.verifyRegisterDerivative(ipAcct[2], ipAcct[1], commUseTermsId, ipOwner[2]); assertTrue(result); } @@ -360,11 +356,11 @@ contract PILicenseTemplateTest is BaseTest { PILTerms memory terms = PILFlavors.nonCommercialSocialRemixing(); terms.derivativesApproval = true; uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(terms); - vm.prank(ipId1); - pilTemplate.setApproval(ipId1, socialRemixTermsId, ipId2, true); - assertTrue(pilTemplate.isDerivativeApproved(ipId1, socialRemixTermsId, ipId2)); + vm.prank(ipAcct[1]); + pilTemplate.setApproval(ipAcct[1], socialRemixTermsId, ipAcct[2], true); + assertTrue(pilTemplate.isDerivativeApproved(ipAcct[1], socialRemixTermsId, ipAcct[2])); - bool result = pilTemplate.verifyRegisterDerivative(ipId2, ipId1, socialRemixTermsId, ipOwner2); + bool result = pilTemplate.verifyRegisterDerivative(ipAcct[2], ipAcct[1], socialRemixTermsId, ipOwner[2]); assertTrue(result); } @@ -372,18 +368,18 @@ contract PILicenseTemplateTest is BaseTest { PILTerms memory terms = PILFlavors.nonCommercialSocialRemixing(); terms.derivativesApproval = true; uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(terms); - vm.prank(ipId1); - pilTemplate.setApproval(ipId1, socialRemixTermsId, ipId2, false); - assertFalse(pilTemplate.isDerivativeApproved(ipId1, socialRemixTermsId, ipId2)); + vm.prank(ipAcct[1]); + pilTemplate.setApproval(ipAcct[1], socialRemixTermsId, ipAcct[2], false); + assertFalse(pilTemplate.isDerivativeApproved(ipAcct[1], socialRemixTermsId, ipAcct[2])); - bool result = pilTemplate.verifyRegisterDerivative(ipId2, ipId1, socialRemixTermsId, ipOwner2); + bool result = pilTemplate.verifyRegisterDerivative(ipAcct[2], ipAcct[1], socialRemixTermsId, ipOwner[2]); assertFalse(result); } function test_PILicenseTemplate_verifyRegisterDerivative_derivativeNotAllowed() public { PILTerms memory terms = PILFlavors.defaultValuesLicenseTerms(); uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(terms); - bool result = pilTemplate.verifyRegisterDerivative(ipId2, ipId1, socialRemixTermsId, ipOwner2); + bool result = pilTemplate.verifyRegisterDerivative(ipAcct[2], ipAcct[1], socialRemixTermsId, ipOwner[2]); assertFalse(result); } @@ -445,23 +441,31 @@ contract PILicenseTemplateTest is BaseTest { licenseTermsIds[0] = commUseTermsId; licenseTermsIds[1] = commRemixTermsId; address[] memory parentIpIds = new address[](2); - parentIpIds[0] = ipId1; - parentIpIds[1] = ipId3; - assertFalse(pilTemplate.verifyRegisterDerivativeForAllParents(ipId2, parentIpIds, licenseTermsIds, ipOwner2)); + parentIpIds[0] = ipAcct[1]; + parentIpIds[1] = ipAcct[3]; + assertFalse( + pilTemplate.verifyRegisterDerivativeForAllParents(ipAcct[2], parentIpIds, licenseTermsIds, ipOwner[2]) + ); uint256 defaultTermsId = pilTemplate.registerLicenseTerms(PILFlavors.defaultValuesLicenseTerms()); uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); licenseTermsIds[0] = defaultTermsId; licenseTermsIds[1] = socialRemixTermsId; - assertFalse(pilTemplate.verifyRegisterDerivativeForAllParents(ipId2, parentIpIds, licenseTermsIds, ipOwner2)); + assertFalse( + pilTemplate.verifyRegisterDerivativeForAllParents(ipAcct[2], parentIpIds, licenseTermsIds, ipOwner[2]) + ); licenseTermsIds[0] = socialRemixTermsId; licenseTermsIds[1] = commRemixTermsId; - assertFalse(pilTemplate.verifyRegisterDerivativeForAllParents(ipId2, parentIpIds, licenseTermsIds, ipOwner2)); + assertFalse( + pilTemplate.verifyRegisterDerivativeForAllParents(ipAcct[2], parentIpIds, licenseTermsIds, ipOwner[2]) + ); licenseTermsIds[0] = socialRemixTermsId; licenseTermsIds[1] = socialRemixTermsId; - assertTrue(pilTemplate.verifyRegisterDerivativeForAllParents(ipId2, parentIpIds, licenseTermsIds, ipOwner2)); + assertTrue( + pilTemplate.verifyRegisterDerivativeForAllParents(ipAcct[2], parentIpIds, licenseTermsIds, ipOwner[2]) + ); } // test isLicenseTransferable diff --git a/test/foundry/registries/LicenseRegistry.t.sol b/test/foundry/registries/LicenseRegistry.t.sol index ce55ae9c..d01aecef 100644 --- a/test/foundry/registries/LicenseRegistry.t.sol +++ b/test/foundry/registries/LicenseRegistry.t.sol @@ -22,42 +22,38 @@ contract LicenseRegistryTest is BaseTest { error ERC721NonexistentToken(uint256 tokenId); - MockERC721 internal mockNft = new MockERC721("MockERC721"); MockERC721 internal gatedNftFoo = new MockERC721{ salt: bytes32(uint256(1)) }("GatedNftFoo"); MockERC721 internal gatedNftBar = new MockERC721{ salt: bytes32(uint256(2)) }("GatedNftBar"); - address public ipId1; - address public ipId2; - address public ipId3; - address public ipId5; - address public ipOwner1 = address(0x111); - address public ipOwner2 = address(0x222); - address public ipOwner3 = address(0x333); - address public ipOwner5 = address(0x444); - uint256 public tokenId1 = 1; - uint256 public tokenId2 = 2; - uint256 public tokenId3 = 3; - uint256 public tokenId5 = 5; + mapping(uint256 => address) internal ipAcct; + mapping(uint256 => address) internal ipOwner; + mapping(uint256 => uint256) internal tokenIds; address public licenseHolder = address(0x101); function setUp() public override { super.setUp(); + + ipOwner[1] = u.alice; + ipOwner[2] = u.bob; + ipOwner[3] = u.carl; + ipOwner[5] = u.dan; + // Create IPAccounts - mockNft.mintId(ipOwner1, tokenId1); - mockNft.mintId(ipOwner2, tokenId2); - mockNft.mintId(ipOwner3, tokenId3); - mockNft.mintId(ipOwner5, tokenId5); - - ipId1 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId1); - ipId2 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId2); - ipId3 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId3); - ipId5 = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId5); - - vm.label(ipId1, "IPAccount1"); - vm.label(ipId2, "IPAccount2"); - vm.label(ipId3, "IPAccount3"); - vm.label(ipId5, "IPAccount5"); + tokenIds[1] = mockNFT.mint(ipOwner[1]); + tokenIds[2] = mockNFT.mint(ipOwner[2]); + tokenIds[3] = mockNFT.mint(ipOwner[3]); + tokenIds[5] = mockNFT.mint(ipOwner[5]); + + ipAcct[1] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[1]); + ipAcct[2] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[2]); + ipAcct[3] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[3]); + ipAcct[5] = ipAssetRegistry.register(block.chainid, address(mockNFT), tokenIds[5]); + + vm.label(ipAcct[1], "IPAccount1"); + vm.label(ipAcct[2], "IPAccount2"); + vm.label(ipAcct[3], "IPAccount3"); + vm.label(ipAcct[5], "IPAccount5"); } function test_LicenseRegistry_setDisputeModule() public { @@ -109,10 +105,10 @@ contract LicenseRegistryTest is BaseTest { function test_LicenseRegistry_setExpireTime() public { vm.prank(address(licensingModule)); - licenseRegistry.setExpireTime(ipId1, block.timestamp + 100); - assertEq(licenseRegistry.getExpireTime(ipId1), block.timestamp + 100); + licenseRegistry.setExpireTime(ipAcct[1], block.timestamp + 100); + assertEq(licenseRegistry.getExpireTime(ipAcct[1]), block.timestamp + 100); assertEq( - IIPAccount(payable(ipId1)).getUint256(address(licenseRegistry), licenseRegistry.EXPIRATION_TIME()), + IIPAccount(payable(ipAcct[1])).getUint256(address(licenseRegistry), licenseRegistry.EXPIRATION_TIME()), block.timestamp + 100 ); } @@ -129,13 +125,13 @@ contract LicenseRegistryTest is BaseTest { vm.prank(address(licensingModule)); licenseRegistry.setMintingLicenseConfigForLicense( - ipId1, + ipAcct[1], address(pilTemplate), defaultTermsId, mintingLicenseConfig ); Licensing.MintingLicenseConfig memory returnedMintingLicenseConfig = licenseRegistry.getMintingLicenseConfig( - ipId1, + ipAcct[1], address(pilTemplate), defaultTermsId ); @@ -160,7 +156,12 @@ contract LicenseRegistryTest is BaseTest { abi.encodeWithSelector(Errors.LicenseRegistry__UnregisteredLicenseTemplate.selector, address(pilTemplate2)) ); vm.prank(address(licensingModule)); - licenseRegistry.setMintingLicenseConfigForLicense(ipId1, address(pilTemplate2), termsId, mintingLicenseConfig); + licenseRegistry.setMintingLicenseConfigForLicense( + ipAcct[1], + address(pilTemplate2), + termsId, + mintingLicenseConfig + ); } function test_LicenseRegistry_setMintingLicenseConfigForIp() public { @@ -174,10 +175,10 @@ contract LicenseRegistryTest is BaseTest { }); vm.prank(address(licensingModule)); - licenseRegistry.setMintingLicenseConfigForIp(ipId1, mintingLicenseConfig); + licenseRegistry.setMintingLicenseConfigForIp(ipAcct[1], mintingLicenseConfig); Licensing.MintingLicenseConfig memory returnedMintingLicenseConfig = licenseRegistry.getMintingLicenseConfig( - ipId1, + ipAcct[1], address(pilTemplate), defaultTermsId ); @@ -194,16 +195,16 @@ contract LicenseRegistryTest is BaseTest { licenseRegistry.setDefaultLicenseTerms(address(pilTemplate), socialRemixTermsId); address[] memory parentIpIds = new address[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](1); licenseTermsIds[0] = socialRemixTermsId; - vm.prank(ipOwner2); - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), ""); + vm.prank(ipOwner[2]); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); uint256 defaultTermsId = pilTemplate.registerLicenseTerms(PILFlavors.defaultValuesLicenseTerms()); vm.expectRevert(Errors.LicensingModule__DerivativesCannotAddLicenseTerms.selector); vm.prank(address(licensingModule)); - licenseRegistry.attachLicenseTermsToIp(ipId2, address(pilTemplate), defaultTermsId); + licenseRegistry.attachLicenseTermsToIp(ipAcct[2], address(pilTemplate), defaultTermsId); } function test_LicenseRegistry_registerDerivativeIp_revert_parentsArrayEmpty() public { @@ -216,17 +217,17 @@ contract LicenseRegistryTest is BaseTest { licenseTermsIds[0] = socialRemixTermsId; vm.expectRevert(Errors.LicenseRegistry__NoParentIp.selector); vm.prank(address(licensingModule)); - licenseRegistry.registerDerivativeIp(ipId2, parentIpIds, address(pilTemplate), licenseTermsIds); + licenseRegistry.registerDerivativeIp(ipAcct[2], parentIpIds, address(pilTemplate), licenseTermsIds); } // test getAttachedLicenseTerms function test_LicenseRegistry_getAttachedLicenseTerms_revert_OutOfIndex() public { uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); - vm.prank(ipOwner1); - licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), socialRemixTermsId); - vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipId1, 1, 1)); - licenseRegistry.getAttachedLicenseTerms(ipId1, 1); + vm.prank(ipOwner[1]); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId); + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipAcct[1], 1, 1)); + licenseRegistry.getAttachedLicenseTerms(ipAcct[1], 1); } // test getDerivativeIp revert IndexOutOfBounds( @@ -236,14 +237,14 @@ contract LicenseRegistryTest is BaseTest { licenseRegistry.setDefaultLicenseTerms(address(pilTemplate), socialRemixTermsId); address[] memory parentIpIds = new address[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](1); licenseTermsIds[0] = socialRemixTermsId; - vm.prank(ipOwner2); - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), ""); + vm.prank(ipOwner[2]); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); - vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipId1, 1, 1)); - licenseRegistry.getDerivativeIp(ipId1, 1); + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipAcct[1], 1, 1)); + licenseRegistry.getDerivativeIp(ipAcct[1], 1); } function test_LicenseRegistry_getParentIp_revert_IndexOutOfBounds() public { @@ -252,71 +253,87 @@ contract LicenseRegistryTest is BaseTest { licenseRegistry.setDefaultLicenseTerms(address(pilTemplate), socialRemixTermsId); address[] memory parentIpIds = new address[](1); - parentIpIds[0] = ipId1; + parentIpIds[0] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](1); licenseTermsIds[0] = socialRemixTermsId; - vm.prank(ipOwner2); - licensingModule.registerDerivative(ipId2, parentIpIds, licenseTermsIds, address(pilTemplate), ""); + vm.prank(ipOwner[2]); + licensingModule.registerDerivative(ipAcct[2], parentIpIds, licenseTermsIds, address(pilTemplate), ""); - vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipId2, 1, 1)); - licenseRegistry.getParentIp(ipId2, 1); + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__IndexOutOfBounds.selector, ipAcct[2], 1, 1)); + licenseRegistry.getParentIp(ipAcct[2], 1); } function test_LicenseRegistry_registerDerivativeIp() public { uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); - vm.prank(ipOwner1); - licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), socialRemixTermsId); - vm.prank(ipOwner2); - licensingModule.attachLicenseTerms(ipId2, address(pilTemplate), socialRemixTermsId); + vm.prank(ipOwner[1]); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId); + vm.prank(ipOwner[2]); + licensingModule.attachLicenseTerms(ipAcct[2], address(pilTemplate), socialRemixTermsId); address[] memory parentIpIds = new address[](2); - parentIpIds[0] = ipId1; - parentIpIds[1] = ipId2; + parentIpIds[0] = ipAcct[1]; + parentIpIds[1] = ipAcct[2]; uint256[] memory licenseTermsIds = new uint256[](2); licenseTermsIds[0] = socialRemixTermsId; licenseTermsIds[1] = socialRemixTermsId; vm.prank(address(licensingModule)); - licenseRegistry.registerDerivativeIp(ipId3, parentIpIds, address(pilTemplate), licenseTermsIds); + licenseRegistry.registerDerivativeIp(ipAcct[3], parentIpIds, address(pilTemplate), licenseTermsIds); } function test_LicenseRegistry_registerDerivativeIp_revert_DuplicateLicense() public { uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing()); - vm.prank(ipOwner1); - licensingModule.attachLicenseTerms(ipId1, address(pilTemplate), socialRemixTermsId); + vm.prank(ipOwner[1]); + licensingModule.attachLicenseTerms(ipAcct[1], address(pilTemplate), socialRemixTermsId); address[] memory parentIpIds = new address[](2); - parentIpIds[0] = ipId1; - parentIpIds[1] = ipId1; + parentIpIds[0] = ipAcct[1]; + parentIpIds[1] = ipAcct[1]; uint256[] memory licenseTermsIds = new uint256[](2); licenseTermsIds[0] = socialRemixTermsId; licenseTermsIds[1] = socialRemixTermsId; vm.expectRevert( abi.encodeWithSelector( Errors.LicenseRegistry__DuplicateLicense.selector, - ipId1, + ipAcct[1], address(pilTemplate), socialRemixTermsId ) ); vm.prank(address(licensingModule)); - licenseRegistry.registerDerivativeIp(ipId2, parentIpIds, address(pilTemplate), licenseTermsIds); + licenseRegistry.registerDerivativeIp(ipAcct[2], parentIpIds, address(pilTemplate), licenseTermsIds); } function test_LicenseRegistry_isExpiredNow() public { vm.startPrank(address(licensingModule)); - licenseRegistry.setExpireTime(ipId1, block.timestamp + 100); - licenseRegistry.setExpireTime(ipId2, block.timestamp + 200); + licenseRegistry.setExpireTime(ipAcct[1], block.timestamp + 100); + licenseRegistry.setExpireTime(ipAcct[2], block.timestamp + 200); vm.warp(block.timestamp + 101); - assertTrue(licenseRegistry.isExpiredNow(ipId1)); - assertFalse(licenseRegistry.isExpiredNow(ipId2)); - assertFalse(licenseRegistry.isExpiredNow(ipId3)); + assertTrue(licenseRegistry.isExpiredNow(ipAcct[1])); + assertFalse(licenseRegistry.isExpiredNow(ipAcct[2])); + assertFalse(licenseRegistry.isExpiredNow(ipAcct[3])); vm.warp(block.timestamp + 201); - assertTrue(licenseRegistry.isExpiredNow(ipId1)); - assertTrue(licenseRegistry.isExpiredNow(ipId2)); - assertFalse(licenseRegistry.isExpiredNow(ipId3)); + assertTrue(licenseRegistry.isExpiredNow(ipAcct[1])); + assertTrue(licenseRegistry.isExpiredNow(ipAcct[2])); + assertFalse(licenseRegistry.isExpiredNow(ipAcct[3])); vm.stopPrank(); } + function test_LicenseRegistry_revert_verifyMintLicenseToken_parentIpExpired() public { + vm.startPrank(address(licensingModule)); + licenseRegistry.setExpireTime(ipAcct[1], block.timestamp + 100); + + vm.warp(block.timestamp + 101); + assertTrue(licenseRegistry.isExpiredNow(ipAcct[1])); + + vm.expectRevert(abi.encodeWithSelector(Errors.LicenseRegistry__ParentIpExpired.selector, ipAcct[1])); + licenseRegistry.verifyMintLicenseToken({ + licensorIpId: ipAcct[1], + licenseTemplate: address(pilTemplate), + licenseTermsId: 1, // dones't need to exist for this test case + isMintedByIpOwner: false + }); + } + function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { return this.onERC721Received.selector; } diff --git a/test/foundry/utils/BaseTest.t.sol b/test/foundry/utils/BaseTest.t.sol index e78bffd2..7b8081c7 100644 --- a/test/foundry/utils/BaseTest.t.sol +++ b/test/foundry/utils/BaseTest.t.sol @@ -93,4 +93,14 @@ contract BaseTest is Test, DeployHelper, LicensingHelper { address impl = address(new MockRoyaltyPolicyLAP()); vm.etch(address(royaltyPolicyLAP), impl.code); } + + function _disputeIp(address disputeInitiator, address ipAddrToDispute) internal returns (uint256 disputeId) { + vm.startPrank(disputeInitiator); + USDC.approve(address(arbitrationPolicySP), ARBITRATION_PRICE); + disputeId = disputeModule.raiseDispute(ipAddrToDispute, string("urlExample"), "PLAGIARISM", ""); + vm.stopPrank(); + + vm.prank(u.relayer); // admin is a judge + disputeModule.setDisputeJudgement(disputeId, true, ""); + } }