diff --git a/contracts/interfaces/licensing/ILinkParamVerifier.sol b/contracts/interfaces/licensing/ILinkParamVerifier.sol index ce3b5a03..5afab032 100644 --- a/contracts/interfaces/licensing/ILinkParamVerifier.sol +++ b/contracts/interfaces/licensing/ILinkParamVerifier.sol @@ -8,11 +8,18 @@ import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier. /// @notice LicenseRegistry will call this to verify the linking an IP to its parent /// with the policy referenced by the license in use. interface ILinkParamVerifier is IPolicyVerifier { + struct VerifyLinkResponse { + bool isLinkingAllowed; + bool isRoyaltyRequired; + address royaltyPolicy; + uint32 royaltyDerivativeRevShare; + } + function verifyLink( uint256 licenseId, address caller, address ipId, address parentIpId, bytes calldata policyData - ) external returns (bool); + ) external returns (VerifyLinkResponse memory); } \ No newline at end of file diff --git a/contracts/interfaces/registries/ILicenseRegistry.sol b/contracts/interfaces/registries/ILicenseRegistry.sol index 3d127410..d976896d 100644 --- a/contracts/interfaces/registries/ILicenseRegistry.sol +++ b/contracts/interfaces/registries/ILicenseRegistry.sol @@ -58,7 +58,7 @@ interface ILicenseRegistry { /// @param caller The address that called the function /// @param ipId The id of the IP /// @param parentIpIds The ids of the parent IP - event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] indexed parentIpIds); + event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] parentIpIds); /// @notice Registers a policy framework manager into the contract, so it can add policy data for /// licenses. @@ -94,7 +94,11 @@ interface ILicenseRegistry { /// @param licenseIds The id of the licenses to burn /// @param childIpId The id of the child IP to be linked /// @param holder The address that holds the license - function linkIpToParents(uint256[] calldata licenseIds, address childIpId, address holder) external; + function linkIpToParents( + uint256[] calldata licenseIds, + address childIpId, + address holder + ) external; /// /// Getters diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index 5988a0d9..be902b99 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -130,6 +130,7 @@ library Errors { error LicenseRegistry__LicensorNotRegistered(); error LicenseRegistry__CallerNotLicensorAndPolicyNotSet(); error LicenseRegistry__DerivativesCannotAddPolicy(); + error LicenseRegistry__IncompatibleLicensorRoyaltyPolicy(); //////////////////////////////////////////////////////////////////////////// // LicenseRegistryAware // diff --git a/contracts/modules/licensing/UMLPolicyFrameworkManager.sol b/contracts/modules/licensing/UMLPolicyFrameworkManager.sol index 7f4a04c4..376bab81 100644 --- a/contracts/modules/licensing/UMLPolicyFrameworkManager.sol +++ b/contracts/modules/licensing/UMLPolicyFrameworkManager.sol @@ -19,11 +19,8 @@ import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransfer import { IUMLPolicyFrameworkManager, UMLPolicy, UMLRights } from "contracts/interfaces/licensing/IUMLPolicyFrameworkManager.sol"; import { IPolicyFrameworkManager } from "contracts/interfaces/licensing/IPolicyFrameworkManager.sol"; import { BasePolicyFrameworkManager } from "contracts/modules/licensing/BasePolicyFrameworkManager.sol"; -import { IRoyaltyModule } from "contracts/interfaces/modules/royalty/IRoyaltyModule.sol"; -import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol"; import { LicensorApprovalChecker } from "contracts/modules/licensing/parameter-helpers/LicensorApprovalChecker.sol"; - /// @title UMLPolicyFrameworkManager /// @notice This is the UML Policy Framework Manager, which implements the UML Policy Framework /// logic for encoding and decoding UML policies into the LicenseRegistry and verifying @@ -36,21 +33,16 @@ contract UMLPolicyFrameworkManager is ITransferParamVerifier, LicensorApprovalChecker { - IRoyaltyModule public immutable ROYALTY_MODULE; - constructor( address accessController, address ipAccountRegistry, address licRegistry, - address royaltyModule, string memory name_, string memory licenseUrl_ ) BasePolicyFrameworkManager(licRegistry, name_, licenseUrl_) LicensorApprovalChecker(accessController, ipAccountRegistry) - { - ROYALTY_MODULE = IRoyaltyModule(royaltyModule); - } + {} function licenseRegistry() external @@ -80,11 +72,17 @@ contract UMLPolicyFrameworkManager is uint256 licenseId, address, // caller address ipId, - address parentIpId, + address, // parentIpId bytes calldata policyData - ) external override onlyLicenseRegistry returns (bool) { + ) external override onlyLicenseRegistry returns (ILinkParamVerifier.VerifyLinkResponse memory) { UMLPolicy memory policy = abi.decode(policyData, (UMLPolicy)); - bool linkingOK = true; + ILinkParamVerifier.VerifyLinkResponse memory response = ILinkParamVerifier.VerifyLinkResponse({ + isLinkingAllowed: true, // If you successfully mint and now hold a license, you have the right to link. + isRoyaltyRequired: false, + royaltyPolicy: address(0), + royaltyDerivativeRevShare: 0 + }); + // If the policy defines commercial revenue sharing, call the royalty module // to set it for the licensor if (policy.commercialRevShare > 0) { @@ -94,22 +92,17 @@ contract UMLPolicyFrameworkManager is // to set it for the licensor in future derivatives if (policy.derivativesRevShare > 0) { // RoyaltyModule.setRevShareForDerivatives() - address[] memory parentIpIds = new address[](1); - parentIpIds[0] = parentIpId; - - ROYALTY_MODULE.setRoyaltyPolicy( - ipId, - policy.royaltyPolicy, - parentIpIds, - abi.encode(policy.derivativesRevShare) // uint32 - ); + response.isRoyaltyRequired = true; + response.royaltyPolicy = policy.royaltyPolicy; + response.royaltyDerivativeRevShare = policy.derivativesRevShare; } // If the policy defines the licensor must approve derivatives, check if the // derivative is approved by the licensor if (policy.derivativesApproval) { - linkingOK = linkingOK && isDerivativeApproved(licenseId, ipId); + response.isLinkingAllowed = response.isLinkingAllowed && isDerivativeApproved(licenseId, ipId); } - return linkingOK; + + return response; } /// Called by licenseRegistry to verify policy parameters for minting a license diff --git a/contracts/modules/royalty-module/RoyaltyModule.sol b/contracts/modules/royalty-module/RoyaltyModule.sol index 97004923..60fecf19 100644 --- a/contracts/modules/royalty-module/RoyaltyModule.sol +++ b/contracts/modules/royalty-module/RoyaltyModule.sol @@ -14,7 +14,7 @@ import { Errors } from "contracts/lib/Errors.sol"; /// and pay royalties as a derivative ip. contract RoyaltyModule is IRoyaltyModule, Governable, ReentrancyGuard { /// @notice registration module address - address public immutable REGISTRATION_MODULE; + address public REGISTRATION_MODULE; /// @notice Indicates if a royalty policy is whitelisted mapping(address royaltyPolicy => bool allowed) public isWhitelistedRoyaltyPolicy; @@ -32,11 +32,11 @@ contract RoyaltyModule is IRoyaltyModule, Governable, ReentrancyGuard { } /// @notice Constructor - /// @param _registrationModule The address of the registration module /// @param _governance The address of the governance contract - constructor(address _registrationModule, address _governance) Governable(_governance) { - if (_registrationModule == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule(); + constructor(address _governance) Governable(_governance) {} + function initialize(address _registrationModule) external onlyProtocolAdmin { + if (_registrationModule == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule(); REGISTRATION_MODULE = _registrationModule; } diff --git a/contracts/registries/LicenseRegistry.sol b/contracts/registries/LicenseRegistry.sol index 483625bf..162f7414 100644 --- a/contracts/registries/LicenseRegistry.sol +++ b/contracts/registries/LicenseRegistry.sol @@ -9,19 +9,19 @@ import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortSt import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; // contracts -import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; +import { AccessControlled } from "contracts/access/AccessControlled.sol"; import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.sol"; import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; import { ILicenseRegistry } from "contracts/interfaces/registries/ILicenseRegistry.sol"; -import { Errors } from "contracts/lib/Errors.sol"; -import { Licensing } from "contracts/lib/Licensing.sol"; import { IPolicyFrameworkManager } from "contracts/interfaces/licensing/IPolicyFrameworkManager.sol"; import { IAccessController } from "contracts/interfaces/IAccessController.sol"; import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { Licensing } from "contracts/lib/Licensing.sol"; import { IPAccountChecker } from "contracts/lib/registries/IPAccountChecker.sol"; -import { AccessControlled } from "contracts/access/AccessControlled.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; // TODO: consider disabling operators/approvals on creation contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { @@ -39,6 +39,8 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { bool isInherited; } + RoyaltyModule public immutable ROYALTY_MODULE; + mapping(address framework => bool registered) private _registeredFrameworkManagers; mapping(bytes32 policyHash => uint256 policyId) private _hashedPolicies; mapping(uint256 policyId => Licensing.Policy policyData) private _policies; @@ -58,8 +60,11 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { constructor( address accessController, - address ipAccountRegistry - ) ERC1155("") AccessControlled(accessController, ipAccountRegistry) {} + address ipAccountRegistry, + address royaltyModule + ) ERC1155("") AccessControlled(accessController, ipAccountRegistry) { + ROYALTY_MODULE = RoyaltyModule(royaltyModule); + } /// @notice registers a policy framework manager into the contract, so it can add policy data for /// licenses. @@ -89,7 +94,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { if (!isPolicyDefined(polId)) { revert Errors.LicenseRegistry__PolicyNotFound(); } - return _addPolicyIdToIp(ipId, polId, false); + return _addPolicyIdToIp({ ipId: ipId, policyId: polId, isInherited: false, skipIfDuplicate: false }); } /// @notice Registers a policy into the contract. MUST be called by a registered @@ -180,14 +185,19 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { address childIpId, address holder ) external verifyPermission(childIpId) { - uint256 licenses = licenseIds.length; - address[] memory licensors = new address[](licenses); - uint256[] memory values = new uint256[](licenses); - for (uint256 i = 0; i < licenses; i++) { + address[] memory licensors = new address[](licenseIds.length); + uint256[] memory values = new uint256[](licenseIds.length); + // If royalty policy address is address(0), this means no royalty policy to set. + // When a child passes in a royalty policy + address royaltyPolicyAddress = address(0); + uint32 royaltyDerivativeRevShare = 0; + + for (uint256 i = 0; i < licenseIds.length; i++) { uint256 licenseId = licenseIds[i]; if (balanceOf(holder, licenseId) == 0) { revert Errors.LicenseRegistry__NotLicensee(); } + Licensing.License memory licenseData = _licenses[licenseId]; licensors[i] = licenseData.licensorIpId; // TODO: check licensor not part of a branch tagged by disputer @@ -197,25 +207,54 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { // Verify linking params Licensing.Policy memory pol = policy(licenseData.policyId); if (ERC165Checker.supportsInterface(pol.policyFramework, type(ILinkParamVerifier).interfaceId)) { - if ( - !ILinkParamVerifier(pol.policyFramework).verifyLink( - licenseId, - msg.sender, - childIpId, - licensors[i], - pol.data - ) - ) { + ILinkParamVerifier.VerifyLinkResponse memory response = ILinkParamVerifier(pol.policyFramework) + .verifyLink(licenseId, msg.sender, childIpId, licensors[i], pol.data); + + if (!response.isLinkingAllowed) { revert Errors.LicenseRegistry__LinkParentParamFailed(); } + + // Compatibility check: If link says no royalty is required for license (licenseIds[i]) but + // another license requires royalty, revert. + if (!response.isRoyaltyRequired && royaltyPolicyAddress != address(0)) { + revert Errors.LicenseRegistry__IncompatibleLicensorRoyaltyPolicy(); + } + + // If link says royalty is required for license (licenseIds[i]) and no royalty policy is set, set it. + // But if the index is NOT 0, this is previous licenses didn't set the royalty policy because they don't + // require royalty payment. So, revert in this case. + if (response.isRoyaltyRequired && royaltyPolicyAddress == address(0)) { + if (i != 0) { + revert Errors.LicenseRegistry__IncompatibleLicensorRoyaltyPolicy(); + } + royaltyPolicyAddress = response.royaltyPolicy; + royaltyDerivativeRevShare = response.royaltyDerivativeRevShare; + } } - // Add policy to kid - _addPolicyIdToIp(childIpId, licenseData.policyId, true); + // Add the policy of licenseIds[i] to the child. If the policy's already set from previous parents, + // then the addition will be skipped. + _addPolicyIdToIp({ + ipId: childIpId, + policyId: licenseData.policyId, + isInherited: true, + skipIfDuplicate: true + }); // Set parent _ipIdParents[childIpId].add(licensors[i]); values[i] = 1; } emit IpIdLinkedToParents(msg.sender, childIpId, licensors); + + // Licenses unanimously require royalty. + if (royaltyPolicyAddress != address(0)) { + ROYALTY_MODULE.setRoyaltyPolicy( + childIpId, + royaltyPolicyAddress, + licensors, + abi.encode(royaltyDerivativeRevShare) + ); + } + // Burn licenses _burnBatch(holder, licenseIds, values); } @@ -375,12 +414,21 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled { /// @param ipId the IP identifier /// @param policyId id of the policy data /// @param isInherited true if set in linkIpToParent, false otherwise + /// @param skipIfDuplicate if true, will skip if policyId is already set /// @return index of the policy added to the set - function _addPolicyIdToIp(address ipId, uint256 policyId, bool isInherited) private returns (uint256 index) { + function _addPolicyIdToIp( + address ipId, + uint256 policyId, + bool isInherited, + bool skipIfDuplicate + ) private returns (uint256 index) { _verifyCanAddPolicy(policyId, ipId, isInherited); // Try and add the policy into the set. EnumerableSet.UintSet storage _pols = _policySetPerIpId(isInherited, ipId); if (!_pols.add(policyId)) { + if (skipIfDuplicate) { + return _policySetups[ipId][policyId].index; + } revert Errors.LicenseRegistry__PolicyAlreadySetForIpId(); } index = _pols.length() - 1; diff --git a/script/foundry/deployment/Main.s.sol b/script/foundry/deployment/Main.s.sol index e4f470f9..bf952d53 100644 --- a/script/foundry/deployment/Main.s.sol +++ b/script/foundry/deployment/Main.s.sol @@ -131,9 +131,18 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { ipAssetRegistry = new IPAssetRegistry(address(accessController), ERC6551_REGISTRY, address(implementation)); _postdeploy(contractKey, address(ipAssetRegistry)); + contractKey = "RoyaltyModule"; + _predeploy(contractKey); + royaltyModule = new RoyaltyModule(address(governance)); + _postdeploy(contractKey, address(royaltyModule)); + contractKey = "LicenseRegistry"; _predeploy(contractKey); - licenseRegistry = new LicenseRegistry(address(accessController), address(ipAssetRegistry)); + licenseRegistry = new LicenseRegistry( + address(accessController), + address(ipAssetRegistry), + address(royaltyModule) + ); _postdeploy(contractKey, address(licenseRegistry)); contractKey = "IPResolver"; @@ -156,11 +165,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { taggingModule = new TaggingModule(); _postdeploy(contractKey, address(taggingModule)); - contractKey = "RoyaltyModule"; - _predeploy(contractKey); - royaltyModule = new RoyaltyModule(address(registrationModule), address(governance)); - _postdeploy(contractKey, address(royaltyModule)); - contractKey = "RoyaltyPolicyLS"; _predeploy(contractKey); royaltyPolicyLS = new RoyaltyPolicyLS( @@ -173,7 +177,12 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { contractKey = "DisputeModule"; _predeploy(contractKey); - disputeModule = new DisputeModule(address(accessController), address(ipAssetRegistry), address(licenseRegistry), address(governance)); + disputeModule = new DisputeModule( + address(accessController), + address(ipAssetRegistry), + address(licenseRegistry), + address(governance) + ); _postdeploy(contractKey, address(disputeModule)); contractKey = "IPAssetRenderer"; @@ -217,15 +226,16 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { } function _configureDeployment() private { - _configureAccessController(); + _configInitialize(); _configureModuleRegistry(); _configureInteractions(); // _configureIPAccountRegistry(); // _configureIPAssetRegistry(); } - function _configureAccessController() private { + function _configInitialize() private { accessController.initialize(address(ipAssetRegistry), address(moduleRegistry)); + royaltyModule.initialize(address(registrationModule)); } function _configureModuleRegistry() private { @@ -297,7 +307,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { address(accessController), address(ipAssetRegistry), address(licenseRegistry), - address(royaltyModule), "UML_ALL_TRUE", "https://very-nice-verifier-license.com/{id}.json" ); @@ -306,7 +315,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { address(accessController), address(ipAssetRegistry), address(licenseRegistry), - address(royaltyModule), "UML_MINT_PAYMENT", "https://expensive-minting-license.com/{id}.json" ); @@ -389,7 +397,7 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler { // /*/////////////////////////////////////////////////////////////// // LINK IPACCOUNTS TO PARENTS USING LICENSES // ////////////////////////////////////////////////////////////////*/ - + licenseRegistry.linkIpToParents(licenseIds, getIpId(mockNft, nftIds[4]), deployer); } diff --git a/test/foundry/integration/BaseIntegration.sol b/test/foundry/integration/BaseIntegration.sol index 786b2ba7..5b53d14b 100644 --- a/test/foundry/integration/BaseIntegration.sol +++ b/test/foundry/integration/BaseIntegration.sol @@ -117,13 +117,18 @@ contract BaseIntegration is Test { address(accessController), address(ipAccountImpl) ); - licenseRegistry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); - ipMetadataProvider = new IPMetadataProvider(address(moduleRegistry)); + royaltyModule = new RoyaltyModule(address(governance)); ipAssetRegistry = new IPAssetRegistry( address(accessController), address(erc6551Registry), address(ipAccountImpl) ); + licenseRegistry = new LicenseRegistry( + address(accessController), + address(ipAssetRegistry), + address(royaltyModule) + ); + ipMetadataProvider = new IPMetadataProvider(address(moduleRegistry)); ipResolver = new IPResolver(address(accessController), address(ipAssetRegistry), address(licenseRegistry)); registrationModule = new RegistrationModule( address(accessController), @@ -132,8 +137,12 @@ contract BaseIntegration is Test { address(ipResolver) ); taggingModule = new TaggingModule(); - royaltyModule = new RoyaltyModule(address(registrationModule), address(governance)); - disputeModule = new DisputeModule(address(accessController), address(ipAssetRegistry), address(licenseRegistry), address(governance)); + disputeModule = new DisputeModule( + address(accessController), + address(ipAssetRegistry), + address(licenseRegistry), + address(governance) + ); ipAssetRenderer = new IPAssetRenderer( address(ipAssetRegistry), address(licenseRegistry), @@ -141,7 +150,12 @@ contract BaseIntegration is Test { address(royaltyModule) ); - arbitrationPolicySP = new ArbitrationPolicySP(address(disputeModule), address(USDC), ARBITRATION_PRICE, address(governance)); + arbitrationPolicySP = new ArbitrationPolicySP( + address(disputeModule), + address(USDC), + ARBITRATION_PRICE, + address(governance) + ); royaltyPolicyLS = new RoyaltyPolicyLS( address(royaltyModule), address(licenseRegistry), @@ -156,6 +170,7 @@ contract BaseIntegration is Test { function _configDeployedContracts() internal { vm.startPrank(u.admin); accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); + royaltyModule.initialize(address(registrationModule)); moduleRegistry.registerModule(REGISTRATION_MODULE_KEY, address(registrationModule)); moduleRegistry.registerModule(IP_RESOLVER_MODULE_KEY, address(ipResolver)); @@ -360,16 +375,16 @@ contract BaseIntegration is Test { }); // Note that below events are emitted in function that's called by the registration module. - for (uint256 i = 0; i < licenseIds.length; i++) { - vm.expectEmit(); - emit ILicenseRegistry.PolicyAddedToIpId({ - caller: address(registrationModule), - ipId: expectedAddr, - policyId: policyIds[i], - index: i, - isInherited: true - }); - } + // for (uint256 i = 0; i < licenseIds.length; i++) { + // vm.expectEmit(); + // emit ILicenseRegistry.PolicyAddedToIpId({ + // caller: address(registrationModule), + // ipId: expectedAddr, + // policyId: policyIds[i], + // index: i, + // isInherited: true + // }); + // } vm.expectEmit(); emit ILicenseRegistry.IpIdLinkedToParents({ @@ -479,15 +494,7 @@ contract BaseIntegration is Test { assertTrue(licenseRegistry.isParent(parentIpIds[i], ipId), "parent IP account is not parent"); (uint256 index, bool isInherited, bool active) = licenseRegistry.policyStatus(parentIpIds[i], policyIds[i]); assertEq( - keccak256( - abi.encode( - licenseRegistry.policyForIpAtIndex( - isInherited, - parentIpIds[i], - index - ) - ) - ), + keccak256(abi.encode(licenseRegistry.policyForIpAtIndex(isInherited, parentIpIds[i], index))), keccak256(abi.encode(licenseRegistry.policyForIpAtIndex(true, ipId, i))), "policy not the same in parent to child" ); diff --git a/test/foundry/integration/big-bang/SingleNftCollection.t.sol b/test/foundry/integration/big-bang/SingleNftCollection.t.sol index ef7bada4..c2c837d7 100644 --- a/test/foundry/integration/big-bang/SingleNftCollection.t.sol +++ b/test/foundry/integration/big-bang/SingleNftCollection.t.sol @@ -7,6 +7,7 @@ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableS // contract import { IIPAccount } from "contracts/interfaces/IIPAccount.sol"; import { IP } from "contracts/lib/IP.sol"; +import { Errors } from "contracts/lib/Errors.sol"; // test import { BaseIntegration } from "test/foundry/integration/BaseIntegration.sol"; @@ -33,13 +34,6 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration ); nft = erc721.cat; - - nft.mintId(u.alice, 1); - nft.mintId(u.alice, 2); - nft.mintId(u.bob, 3); - nft.mintId(u.bob, 4); - nft.mintId(u.carl, 5); - nft.mintId(u.carl, 6); } function test_Integration_SingleNftCollection_DirectCallsByIPAccountOwners() @@ -48,7 +42,7 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration withLFM_MintPayment(erc20, 1) withUMLPolicy_Commerical_Derivative( UMLPolicyGenericParams({ - policyName: "cheap_flexible", // => com_deriv_cheap_flexible + policyName: "cheap_flexible", // => uml_com_deriv_cheap_flexible attribution: false, transferable: true, territories: new string[](0), @@ -69,7 +63,7 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration ) withUMLPolicy_NonCommercial_Derivative( UMLPolicyGenericParams({ - policyName: "reciprocal_derivative", // => noncom_deriv_reciprocal_derivative + policyName: "reciprocal_derivative", // => uml_noncom_derive_reciprocal_derivative attribution: false, transferable: true, territories: new string[](0), @@ -84,7 +78,7 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration ) withUMLPolicy_NonCommercial_NonDerivative( UMLPolicyGenericParams({ - policyName: "self", // => noncom_nonderiv_self + policyName: "self", // => uml_noncom_nonderiv_self attribution: false, transferable: false, territories: new string[](0), @@ -102,12 +96,19 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration // owner is the vm.pranker vm.startPrank(u.alice); + nft.mintId(u.alice, 1); + nft.mintId(u.alice, 100); ipAcct[1] = registerIpAccount(nft, 1, u.alice); + ipAcct[100] = registerIpAccount(nft, 100, u.alice); vm.startPrank(u.bob); + nft.mintId(u.bob, 3); + nft.mintId(u.bob, 300); ipAcct[3] = registerIpAccount(nft, 3, u.bob); + ipAcct[300] = registerIpAccount(nft, 300, u.bob); vm.startPrank(u.carl); + nft.mintId(u.carl, 5); ipAcct[5] = registerIpAccount(nft, 5, u.carl); /*/////////////////////////////////////////////////////////////// @@ -116,18 +117,29 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration vm.startPrank(u.alice); licenseRegistry.addPolicyToIp(ipAcct[1], policyIds["uml_com_deriv_cheap_flexible"]); + licenseRegistry.addPolicyToIp(ipAcct[100], policyIds["uml_noncom_deriv_reciprocal_derivative"]); - // Alice sets royalty policy for her IPAccount (so other IPAccounts can use her policies that - // inits royalty policy on linking) + // Alice sets royalty policy for her root IPAccounts + // (so other IPAccounts can use her policies that inits royalty policy on linking) royaltyModule.setRoyaltyPolicy( ipAcct[1], address(royaltyPolicyLS), new address[](0), // no parent - abi.encode(50) + abi.encode(10) ); vm.startPrank(u.bob); licenseRegistry.addPolicyToIp(ipAcct[3], policyIds["mint_payment_normal"]); + licenseRegistry.addPolicyToIp(ipAcct[300], policyIds["uml_com_deriv_cheap_flexible"]); + + // Bob sets royalty policy for his root IPAccounts + // (so other IPAccounts can use his policies that inits royalty policy on linking) + royaltyModule.setRoyaltyPolicy( + ipAcct[300], + address(royaltyPolicyLS), + new address[](0), // no parent + abi.encode(10) + ); vm.startPrank(u.bob); // NOTE: the two calls below achieve the same functionality @@ -151,6 +163,7 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration // Carl activates the license on his NFT 6 IPAccount, linking as child to Alice's NFT 1 IPAccount { vm.startPrank(u.carl); + nft.mintId(u.carl, 6); uint256[] memory carl_license_from_root_alice = new uint256[](1); carl_license_from_root_alice[0] = licenseRegistry.mintLicense( policyIds["uml_com_deriv_cheap_flexible"], @@ -163,13 +176,14 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration linkIpToParents(carl_license_from_root_alice, ipAcct[6], u.carl); } - // Alice mints 2 license for policy "noncom_deriv_mint_payment" on Bob's NFT 3 IPAccount + // Alice mints 2 license for policy "mint_payment_normal" on Bob's NFT 3 IPAccount // Alice creates NFT 2 IPAccount // Alice activates one of the two licenses on her NFT 2 IPAccount, linking as child to Bob's NFT 3 IPAccount // Alice creates derivative NFT 3 directly using the other license // NOTE: since this policy has `MintPaymentPolicyFrameworkManager` attached, Alice must pay the mint payment { vm.startPrank(u.alice); + nft.mintId(u.alice, 2); uint256 mintAmount = 2; uint256 paymentPerMint = MintPaymentPolicyFrameworkManager(pfm["mint_payment"].addr).payment(); @@ -231,38 +245,65 @@ contract BigBang_Integration_SingleNftCollection is BaseIntegration, Integration { vm.startPrank(u.carl); + uint256 tokenId = 70000; // dummy number that shouldn't conflict with any other token IDs used in this test + nft.mintId(u.carl, tokenId); + uint256 paymentPerMint = MintPaymentPolicyFrameworkManager(pfm["mint_payment"].addr).payment(); - uint256 tokenId = 7; - nft.mintId(u.carl, tokenId); + IP.MetadataV1 memory metadata = IP.MetadataV1({ + name: "IP NAME", + hash: bytes32("hash"), + registrationDate: uint64(block.timestamp), + registrant: u.carl, // caller + uri: "external URL" + }); erc20.approve(pfm["mint_payment"].addr, 1 * paymentPerMint); uint256[] memory carl_licenses = new uint256[](2); + // Commercial license carl_licenses[0] = licenseRegistry.mintLicense( policyIds["uml_com_deriv_cheap_flexible"], // ipAcct[1] has this policy attached ipAcct[1], - 1, + 100, // mint 100 licenses u.carl ); + + // Non-commercial license carl_licenses[1] = licenseRegistry.mintLicense( - policyIds["mint_payment_normal"], // ipAcct[3] has this policy attached + policyIds["uml_noncom_deriv_reciprocal_derivative"], // ipAcct[3] has this policy attached ipAcct[3], 1, u.carl ); - registerDerivativeIps( + // This should revert since license[0] is commercial but license[1] is non-commercial + vm.expectRevert(Errors.LicenseRegistry__IncompatibleLicensorRoyaltyPolicy.selector); + // Call `registrationModule.registerDerivativeIps` directly because expecting revert on the + // wrapper `registerDerivativeIps` fails due to the implementation of the wrapper function. + registrationModule.registerDerivativeIp( carl_licenses, address(nft), tokenId, - IP.MetadataV1({ - name: "IP NAME", - hash: bytes32("hash"), - registrationDate: uint64(block.timestamp), - registrant: u.carl, // caller - uri: "external URL" - }), + metadata.name, + metadata.hash, + metadata.uri + ); + + // Modify license[1] to a Commercial license + carl_licenses[1] = licenseRegistry.mintLicense( + policyIds["uml_com_deriv_cheap_flexible"], // ipAcct[300] has this policy attached + ipAcct[300], + 1, + u.carl + ); + + // This should succeed since both license[0] and license[1] are commercial + registerDerivativeIps( + carl_licenses, // ipAcct[1] and ipAcct[3] licenses + address(nft), + tokenId, + metadata, u.carl // caller ); } diff --git a/test/foundry/integration/shared/LicenseHelper.sol b/test/foundry/integration/shared/LicenseHelper.sol index 7206e7eb..8a52ca6d 100644 --- a/test/foundry/integration/shared/LicenseHelper.sol +++ b/test/foundry/integration/shared/LicenseHelper.sol @@ -87,7 +87,6 @@ contract Integration_Shared_LicensingHelper { address(accessController), address(ipAccountRegistry), address(licenseRegistry), - address(royaltyModule), "uml", "license Url" ) diff --git a/test/foundry/mocks/licensing/MockPolicyFrameworkManager.sol b/test/foundry/mocks/licensing/MockPolicyFrameworkManager.sol index 0f17ac60..cddd040f 100644 --- a/test/foundry/mocks/licensing/MockPolicyFrameworkManager.sol +++ b/test/foundry/mocks/licensing/MockPolicyFrameworkManager.sol @@ -64,9 +64,21 @@ contract MockPolicyFrameworkManager is return policy.returnVerifyMint; } - function verifyLink(uint256, address, address, address, bytes calldata data) external pure override returns (bool) { + function verifyLink( + uint256, + address, + address, + address, + bytes calldata data + ) external pure override returns (ILinkParamVerifier.VerifyLinkResponse memory) { MockPolicy memory policy = abi.decode(data, (MockPolicy)); - return policy.returnVerifyLink; + return + ILinkParamVerifier.VerifyLinkResponse({ + isLinkingAllowed: policy.returnVerifyLink, + isRoyaltyRequired: false, + royaltyPolicy: address(0), + royaltyDerivativeRevShare: 0 + }); } function verifyTransfer( @@ -83,7 +95,7 @@ contract MockPolicyFrameworkManager is function policyToJson(bytes memory policyData) public pure returns (string memory) { return "MockPolicyFrameworkManager"; } - + function processInheritedPolicies( bytes memory ipRights, bytes memory policy diff --git a/test/foundry/modules/ModuleBase.t.sol b/test/foundry/modules/ModuleBase.t.sol index f723db25..99e98ee7 100644 --- a/test/foundry/modules/ModuleBase.t.sol +++ b/test/foundry/modules/ModuleBase.t.sol @@ -16,16 +16,18 @@ import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol"; import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol"; import { MockModuleRegistry } from "test/foundry/mocks/MockModuleRegistry.sol"; import { IIPAssetRegistry } from "contracts/interfaces/registries/IIPAssetRegistry.sol"; -import { IPAccountImpl} from "contracts/IPAccountImpl.sol"; +import { IPAccountImpl } from "contracts/IPAccountImpl.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; import { IP } from "contracts/lib/IP.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { Governance } from "contracts/governance/Governance.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; +import { IPResolver } from "contracts/resolvers/IPResolver.sol"; +import { RegistrationModule } from "contracts/modules/RegistrationModule.sol"; /// @title Module Base Test Contract /// @notice Base contract for testing standard module functionality. abstract contract ModuleBaseTest is BaseTest { - /// @notice Gets the protocol-wide license registry. LicenseRegistry public licenseRegistry; @@ -43,7 +45,9 @@ abstract contract ModuleBaseTest is BaseTest { Governance public governance; - IPMetadataProvider metadataProvider; + IPMetadataProvider public metadataProvider; + + RoyaltyModule public royaltyModule; /// @notice Initializes the base module for testing. function setUp() public virtual override(BaseTest) { @@ -58,9 +62,26 @@ abstract contract ModuleBaseTest is BaseTest { address(new ERC6551Registry()), address(new IPAccountImpl()) ); - accessController.initialize(address(ipAssetRegistry), address(moduleRegistry)); - licenseRegistry = new LicenseRegistry(address(accessController), address(ipAssetRegistry)); + royaltyModule = new RoyaltyModule(address(governance)); + licenseRegistry = new LicenseRegistry( + address(accessController), + address(ipAssetRegistry), + address(royaltyModule) + ); + IPResolver ipResolver = new IPResolver( + address(accessController), + address(ipAssetRegistry), + address(licenseRegistry) + ); + RegistrationModule registrationModule = new RegistrationModule( + address(accessController), + address(ipAssetRegistry), + address(licenseRegistry), + address(ipResolver) + ); baseModule = IModule(_deployModule()); + accessController.initialize(address(ipAssetRegistry), address(moduleRegistry)); + royaltyModule.initialize(address(registrationModule)); } /// @notice Tests that the default resolver constructor runs successfully. @@ -72,6 +93,5 @@ abstract contract ModuleBaseTest is BaseTest { function _deployModule() internal virtual returns (address); /// @dev Gets the expected name for the module. - function _expectedName() internal virtual view returns (string memory); - + function _expectedName() internal view virtual returns (string memory); } diff --git a/test/foundry/modules/licensing/UMLPolicyFramework.compat.t.sol b/test/foundry/modules/licensing/UMLPolicyFramework.compat.t.sol index 77e23c85..582fd896 100644 --- a/test/foundry/modules/licensing/UMLPolicyFramework.compat.t.sol +++ b/test/foundry/modules/licensing/UMLPolicyFramework.compat.t.sol @@ -59,7 +59,6 @@ contract UMLPolicyFrameworkCompatibilityTest is TestHelper { address(accessController), address(ipAccountRegistry), address(licenseRegistry), - address(royaltyModule), "UMLPolicyFrameworkManager", licenseUrl ); diff --git a/test/foundry/modules/licensing/UMLPolicyFramework.t.sol b/test/foundry/modules/licensing/UMLPolicyFramework.t.sol index 514e61e5..c3590153 100644 --- a/test/foundry/modules/licensing/UMLPolicyFramework.t.sol +++ b/test/foundry/modules/licensing/UMLPolicyFramework.t.sol @@ -14,17 +14,13 @@ import { ERC6551Registry } from "lib/reference/src/ERC6551Registry.sol"; import { IPAccountImpl } from "contracts/IPAccountImpl.sol"; import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; +import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol"; +import { TestHelper } from "test/utils/TestHelper.sol"; -contract UMLPolicyFrameworkTest is Test { - MockAccessController public accessController = new MockAccessController(); - IPAccountRegistry public ipAccountRegistry; - - LicenseRegistry public registry; - +contract UMLPolicyFrameworkTest is TestHelper { UMLPolicyFrameworkManager public umlFramework; - MockERC721 nft = new MockERC721("MockERC721"); - string public licenseUrl = "https://example.com/license"; address public ipId1; address public ipId2; @@ -32,22 +28,19 @@ contract UMLPolicyFrameworkTest is Test { address public licenseHolder = address(0x101); string[] public emptyStringArray = new string[](0); - function setUp() public { - ipAccountRegistry = new IPAccountRegistry( - address(new ERC6551Registry()), - address(accessController), - address(new IPAccountImpl()) - ); - registry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); + function setUp() public override { + TestHelper.setUp(); + + nft = erc721.ape; + umlFramework = new UMLPolicyFrameworkManager( address(accessController), address(ipAccountRegistry), - address(registry), - address(0), // TODO: mock royaltyModule + address(licenseRegistry), "UMLPolicyFrameworkManager", licenseUrl ); - registry.registerPolicyFrameworkManager(address(umlFramework)); + licenseRegistry.registerPolicyFrameworkManager(address(umlFramework)); nft.mintId(ipOwner, 1); nft.mintId(ipOwner, 2); @@ -221,7 +214,7 @@ contract UMLPolicyFrameworkTest is Test { // APPROVAL TERMS - function test_UMLPolicyFrameworkManager_derivativesWithApproval_revert_linkNotApproved() public { + function test_UMLPolicyFrameworkManager_derivatives_withApproval_revert_linkNotApproved() public { uint256 policyId = umlFramework.registerPolicy( UMLPolicy({ attribution: false, @@ -240,19 +233,22 @@ contract UMLPolicyFrameworkTest is Test { royaltyPolicy: address(0) }) ); + vm.prank(ipOwner); - registry.addPolicyToIp(ipId1, policyId); - uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); + licenseRegistry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = licenseRegistry.mintLicense(policyId, ipId1, 1, licenseHolder); assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); - vm.prank(ipOwner); + + vm.prank(licenseRegistry.licensorIpId(licenseId)); umlFramework.setApproval(licenseId, ipId2, false); assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); uint256[] memory licenseIds = new uint256[](1); licenseIds[0] = licenseId; + vm.expectRevert(Errors.LicenseRegistry__LinkParentParamFailed.selector); vm.prank(ipOwner); - registry.linkIpToParents(licenseIds, ipId2, licenseHolder); + licenseRegistry.linkIpToParents(licenseIds, ipId2, licenseHolder); } function test_UMLPolicyFrameworkManager_derivatives_withApproval_linkApprovedIpId() public { @@ -275,19 +271,21 @@ contract UMLPolicyFrameworkTest is Test { }) ); vm.prank(ipOwner); - registry.addPolicyToIp(ipId1, policyId); - uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); + licenseRegistry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = licenseRegistry.mintLicense(policyId, ipId1, 1, licenseHolder); assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); - vm.prank(ipOwner); + + vm.prank(licenseRegistry.licensorIpId(licenseId)); umlFramework.setApproval(licenseId, ipId2, true); assertTrue(umlFramework.isDerivativeApproved(licenseId, ipId2)); + uint256[] memory licenseIds = new uint256[](1); licenseIds[0] = licenseId; vm.prank(ipOwner); - registry.linkIpToParents(licenseIds, ipId2, licenseHolder); - assertTrue(registry.isParent(ipId1, ipId2)); + licenseRegistry.linkIpToParents(licenseIds, ipId2, licenseHolder); + assertTrue(licenseRegistry.isParent(ipId1, ipId2)); } function test_UMLPolicyFrameworkManager_derivatives_withApproval_revert_approverNotLicensor() public { @@ -315,14 +313,14 @@ contract UMLPolicyFrameworkTest is Test { }); uint256 policyId = umlFramework.registerPolicy(umlPolicy); vm.prank(ipOwner); - registry.addPolicyToIp(ipId1, policyId); - uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); - assertEq(registry.balanceOf(licenseHolder, licenseId), 1); + licenseRegistry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = licenseRegistry.mintLicense(policyId, ipId1, 1, licenseHolder); + assertEq(licenseRegistry.balanceOf(licenseHolder, licenseId), 1); address licenseHolder2 = address(0x222); vm.prank(licenseHolder); - registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); - assertEq(registry.balanceOf(licenseHolder, licenseId), 0); - assertEq(registry.balanceOf(licenseHolder2, licenseId), 1); + licenseRegistry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); + assertEq(licenseRegistry.balanceOf(licenseHolder, licenseId), 0); + assertEq(licenseRegistry.balanceOf(licenseHolder2, licenseId), 1); } function test_UMLPolicyFrameworkManager_nonTransferrable_revertIfTransferExceptFromLicensor() public { @@ -344,13 +342,13 @@ contract UMLPolicyFrameworkTest is Test { }); uint256 policyId = umlFramework.registerPolicy(umlPolicy); vm.prank(ipOwner); - registry.addPolicyToIp(ipId1, policyId); - uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); - assertEq(registry.balanceOf(licenseHolder, licenseId), 1); + licenseRegistry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = licenseRegistry.mintLicense(policyId, ipId1, 1, licenseHolder); + assertEq(licenseRegistry.balanceOf(licenseHolder, licenseId), 1); address licenseHolder2 = address(0x222); vm.startPrank(licenseHolder); vm.expectRevert(Errors.LicenseRegistry__TransferParamFailed.selector); - registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); + licenseRegistry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); vm.stopPrank(); } diff --git a/test/foundry/modules/royalty/RoyaltyModule.t.sol b/test/foundry/modules/royalty/RoyaltyModule.t.sol index 9b3fc931..8c60a935 100644 --- a/test/foundry/modules/royalty/RoyaltyModule.t.sol +++ b/test/foundry/modules/royalty/RoyaltyModule.t.sol @@ -31,14 +31,18 @@ contract TestRoyaltyModule is TestHelper { } function test_RoyaltyModule_constructor_revert_ZeroRegistrationModule() public { + RoyaltyModule testRoyaltyModule = new RoyaltyModule(address(governance)); vm.expectRevert(Errors.RoyaltyModule__ZeroLicensingModule.selector); - RoyaltyModule testRoyaltyModule = new RoyaltyModule(address(0), address(1)); + vm.prank(u.admin); + testRoyaltyModule.initialize(address(0)); } function test_RoyaltyModule_constructor() public { - RoyaltyModule testRoyaltyModule = new RoyaltyModule(address(registrationModule), address(1)); + RoyaltyModule testRoyaltyModule = new RoyaltyModule(address(governance)); + vm.prank(u.admin); + testRoyaltyModule.initialize(address(registrationModule)); assertEq(testRoyaltyModule.REGISTRATION_MODULE(), address(registrationModule)); - assertEq(testRoyaltyModule.governance(), address(1)); + assertEq(testRoyaltyModule.governance(), address(governance)); } function test_RoyaltyModule_whitelistRoyaltyPolicy_revert_ZeroRoyaltyToken() public { diff --git a/test/foundry/registries/LicenseRegistry.t.sol b/test/foundry/registries/LicenseRegistry.t.sol index 77724bdd..8f583674 100644 --- a/test/foundry/registries/LicenseRegistry.t.sol +++ b/test/foundry/registries/LicenseRegistry.t.sol @@ -10,6 +10,7 @@ import { ERC6551Registry } from "lib/reference/src/ERC6551Registry.sol"; // contracts import { IPAccountImpl } from "contracts/IPAccountImpl.sol"; +import { Governance } from "contracts/governance/Governance.sol"; import { AccessPermission } from "contracts/lib/AccessPermission.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; @@ -17,6 +18,8 @@ import { UMLPolicyFrameworkManager, UMLPolicy } from "contracts/modules/licensin import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol"; import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; +import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol"; // test import { MockPolicyFrameworkManager, MockPolicyFrameworkConfig, MockPolicy } from "test/foundry/mocks/licensing/MockPolicyFrameworkManager.sol"; @@ -37,6 +40,14 @@ contract LicenseRegistryTest is Test { MockERC721 internal nft = new MockERC721("MockERC721"); + ERC6551Registry internal erc6551Registry; + + IPAccountImpl internal ipAccountImpl; + + RoyaltyModule internal royaltyModule; + + IPAssetRegistry internal ipAssetRegistry; + string public licenseUrl = "https://example.com/license"; address public ipId1; address public ipId2; @@ -45,12 +56,25 @@ contract LicenseRegistryTest is Test { function setUp() public { // Registry + Governance governance = new Governance(address(this)); + erc6551Registry = new ERC6551Registry(); + ipAccountImpl = new IPAccountImpl(); ipAccountRegistry = new IPAccountRegistry( - address(new ERC6551Registry()), + address(erc6551Registry), + address(accessController), + address(ipAccountImpl) + ); + ipAssetRegistry = new IPAssetRegistry( + address(accessController), + address(erc6551Registry), + address(ipAccountImpl) + ); + royaltyModule = new RoyaltyModule(address(governance)); + registry = new LicenseRegistry( address(accessController), - address(new IPAccountImpl()) + address(ipAssetRegistry), + address(royaltyModule) ); - registry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); // Setup Framework Managers (don't register PFM here, do in each test case) module1 = new MockPolicyFrameworkManager( @@ -67,7 +91,6 @@ contract LicenseRegistryTest is Test { address(accessController), address(ipAccountRegistry), address(registry), - address(0), // TODO: mock royaltyModule "UMLPolicyFrameworkManager", licenseUrl ); diff --git a/test/foundry/registries/metadata/IPAssetRenderer.t.sol b/test/foundry/registries/metadata/IPAssetRenderer.t.sol index b7b44534..10404ed1 100644 --- a/test/foundry/registries/metadata/IPAssetRenderer.t.sol +++ b/test/foundry/registries/metadata/IPAssetRenderer.t.sol @@ -28,6 +28,7 @@ import { IP } from "contracts/lib/IP.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { IP_RESOLVER_MODULE_KEY, REGISTRATION_MODULE_KEY } from "contracts/lib/modules/Module.sol"; import { Governance } from "contracts/governance/Governance.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; /// @title IP Asset Renderer Test Contract /// @notice Tests IP asset rendering functionality. @@ -37,7 +38,6 @@ contract IPAssetRendererTest is BaseTest { // Module placeholders // TODO: Mock these out. address taggingModule = vm.addr(0x1111); - address royaltyModule = vm.addr(0x2222); /// @notice Gets the metadata provider used for IP registration. MetadataProviderV1 metadataProvider; @@ -89,35 +89,49 @@ contract IPAssetRendererTest is BaseTest { accessController = new AccessController(address(governance)); moduleRegistry = new ModuleRegistry(address(governance)); MockERC721 erc721 = new MockERC721("MockERC721"); + + ERC6551Registry erc6551Registry = new ERC6551Registry(); + IPAccountImpl ipAccountImpl = new IPAccountImpl(); ipAccountRegistry = new IPAccountRegistry( - address(new ERC6551Registry()), + address(erc6551Registry), address(accessController), - address(new IPAccountImpl()) + address(ipAccountImpl) ); - accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); - licenseRegistry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); - - vm.prank(alice); - uint256 tokenId = erc721.mintId(alice, 99); - ipAssetRegistry = new IPAssetRegistry( address(accessController), - address(new ERC6551Registry()), - address(new IPAccountImpl()) + address(erc6551Registry), + address(ipAccountImpl) + ); + RoyaltyModule royaltyModule = new RoyaltyModule(address(governance)); + licenseRegistry = new LicenseRegistry( + address(accessController), + address(ipAssetRegistry), + address(royaltyModule) ); - resolver = new IPResolver( address(accessController), address(ipAssetRegistry), address(licenseRegistry) ); - renderer = new IPAssetRenderer( address(ipAssetRegistry), address(licenseRegistry), taggingModule, - royaltyModule + address(royaltyModule) ); + registrationModule = new RegistrationModule( + address(accessController), + address(ipAssetRegistry), + address(licenseRegistry), + address(resolver) + ); + + accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); + royaltyModule.initialize(address(registrationModule)); + + vm.prank(alice); + uint256 tokenId = erc721.mintId(alice, 99); + bytes memory metadata = abi.encode( IP.MetadataV1({ name: IP_NAME, diff --git a/test/foundry/registries/metadata/MetadataProvider.t.sol b/test/foundry/registries/metadata/MetadataProvider.t.sol index 36cf18d1..e8153f04 100644 --- a/test/foundry/registries/metadata/MetadataProvider.t.sol +++ b/test/foundry/registries/metadata/MetadataProvider.t.sol @@ -20,6 +20,7 @@ import { MetadataProviderV1 } from "contracts/registries/metadata/MetadataProvid import { IMetadataProvider } from "contracts/interfaces/registries/metadata/IMetadataProvider.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; +import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol"; /// @title IP Metadata Provider Testing Contract /// @notice Contract for metadata provider settings. @@ -116,18 +117,27 @@ contract MetadataProviderTest is BaseTest { Governance governance = new Governance(address(this)); AccessController accessController = new AccessController(address(governance)); ModuleRegistry moduleRegistry = new ModuleRegistry(address(governance)); + + ERC6551Registry erc6551Registry = new ERC6551Registry(); + IPAccountImpl ipAccountImpl = new IPAccountImpl(); IPAccountRegistry ipAccountRegistry = new IPAccountRegistry( - address(new ERC6551Registry()), + address(erc6551Registry), address(accessController), - address(new IPAccountImpl()) + address(ipAccountImpl) ); - accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); - LicenseRegistry licenseRegistry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); vm.prank(bob); registry = new IPAssetRegistry( address(accessController), - address(new ERC6551Registry()), - address(new IPAccountImpl()) + address(erc6551Registry), + address(ipAccountImpl) + ); + RoyaltyModule royaltyModule = new RoyaltyModule(address(governance)); + + accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); + LicenseRegistry licenseRegistry = new LicenseRegistry( + address(accessController), + address(registry), + address(royaltyModule) ); MockERC721 erc721 = new MockERC721("MockERC721"); uint256 tokenId = erc721.mintId(alice, 99); diff --git a/test/utils/DeployHelper.sol b/test/utils/DeployHelper.sol index 852268d2..6ac9c7c7 100644 --- a/test/utils/DeployHelper.sol +++ b/test/utils/DeployHelper.sol @@ -126,13 +126,18 @@ contract DeployHelper is Test { address(accessController), address(ipAccountImpl) ); - licenseRegistry = new LicenseRegistry(address(accessController), address(ipAccountRegistry)); - ipMetadataProvider = new IPMetadataProvider(address(moduleRegistry)); + royaltyModule = new RoyaltyModule(address(governance)); ipAssetRegistry = new IPAssetRegistry( address(accessController), address(erc6551Registry), address(ipAccountImpl) ); + licenseRegistry = new LicenseRegistry( + address(accessController), + address(ipAssetRegistry), + address(royaltyModule) + ); + ipMetadataProvider = new IPMetadataProvider(address(moduleRegistry)); ipResolver = new IPResolver(address(accessController), address(ipAssetRegistry), address(licenseRegistry)); registrationModule = new RegistrationModule( address(accessController), @@ -141,7 +146,6 @@ contract DeployHelper is Test { address(ipResolver) ); taggingModule = new TaggingModule(); - royaltyModule = new RoyaltyModule(address(registrationModule), address(governance)); disputeModule = new DisputeModule( address(accessController), address(ipAssetRegistry), @@ -178,6 +182,7 @@ contract DeployHelper is Test { function _configDeployedContracts() internal { vm.startPrank(u.admin); accessController.initialize(address(ipAccountRegistry), address(moduleRegistry)); + royaltyModule.initialize(address(registrationModule)); moduleRegistry.registerModule(REGISTRATION_MODULE_KEY, address(registrationModule)); moduleRegistry.registerModule(IP_RESOLVER_MODULE_KEY, address(ipResolver)); diff --git a/test/utils/TestHelper.sol b/test/utils/TestHelper.sol index c7ccfd22..59298cdc 100644 --- a/test/utils/TestHelper.sol +++ b/test/utils/TestHelper.sol @@ -64,7 +64,6 @@ contract TestHelper is Test, DeployHelper { address(accessController), address(ipAssetRegistry), address(licenseRegistry), - address(royaltyModule), "UML_MINT_PAYMENT", "license Url" );