diff --git a/contracts/interfaces/workflows/IRoyaltyTokenDistributionWorkflows.sol b/contracts/interfaces/workflows/IRoyaltyTokenDistributionWorkflows.sol index 44bf245..07a79f3 100644 --- a/contracts/interfaces/workflows/IRoyaltyTokenDistributionWorkflows.sol +++ b/contracts/interfaces/workflows/IRoyaltyTokenDistributionWorkflows.sol @@ -14,18 +14,18 @@ interface IRoyaltyTokenDistributionWorkflows { /// @param spgNftContract The address of the SPG NFT contract. /// @param recipient The address to receive the NFT. /// @param ipMetadata The metadata for the IP. - /// @param terms The PIL terms to attach to the IP (must be a commercial license). + /// @param terms The PIL terms to attach to the IP (the license terms at index 0 must be a commercial license). /// @param royaltyShares Authors of the IP and their shares of the royalty tokens, see {WorkflowStructs.RoyaltyShare}. /// @return ipId The ID of the registered IP. /// @return tokenId The ID of the minted NFT. - /// @return licenseTermsId The ID of the attached PIL terms. + /// @return licenseTermsIds The IDs of the attached PIL terms. function mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens( address spgNftContract, address recipient, WorkflowStructs.IPMetadata calldata ipMetadata, - PILTerms calldata terms, + PILTerms[] calldata terms, WorkflowStructs.RoyaltyShare[] calldata royaltyShares - ) external returns (address ipId, uint256 tokenId, uint256 licenseTermsId); + ) external returns (address ipId, uint256 tokenId, uint256[] memory licenseTermsIds); /// @notice Mint an NFT and register the IP, make a derivative, and distribute royalty tokens. /// @dev In order to successfully distribute royalty tokens, the license terms attached to the IP must be @@ -51,20 +51,20 @@ interface IRoyaltyTokenDistributionWorkflows { /// @param nftContract The address of the NFT contract. /// @param tokenId The ID of the NFT. /// @param ipMetadata The metadata for the IP. - /// @param terms The PIL terms to attach to the IP (must be a commercial license). + /// @param terms The PIL terms to attach to the IP (the license terms at index 0 must be a commercial license). /// @param sigMetadata The signature data for the IP metadata. /// @param sigAttach The signature data for attaching the PIL terms. /// @return ipId The ID of the registered IP. - /// @return licenseTermsId The ID of the attached PIL terms. + /// @return licenseTermsIds The IDs of the attached PIL terms. /// @return ipRoyaltyVault The address of the deployed royalty vault. function registerIpAndAttachPILTermsAndDeployRoyaltyVault( address nftContract, uint256 tokenId, WorkflowStructs.IPMetadata calldata ipMetadata, - PILTerms calldata terms, + PILTerms[] calldata terms, WorkflowStructs.SignatureData calldata sigMetadata, WorkflowStructs.SignatureData calldata sigAttach - ) external returns (address ipId, uint256 licenseTermsId, address ipRoyaltyVault); + ) external returns (address ipId, uint256[] memory licenseTermsIds, address ipRoyaltyVault); /// @notice Register an IP, make a derivative, and deploy a royalty vault. /// @dev In order to successfully deploy a royalty vault, the license terms attached to the IP must be diff --git a/contracts/lib/LicensingHelper.sol b/contracts/lib/LicensingHelper.sol index 11f218f..16d4ac5 100644 --- a/contracts/lib/LicensingHelper.sol +++ b/contracts/lib/LicensingHelper.sol @@ -25,7 +25,7 @@ library LicensingHelper { address pilTemplate, address licensingModule, address licenseRegistry, - PILTerms[] calldata terms + PILTerms[] memory terms ) internal returns (uint256[] memory licenseTermsIds) { licenseTermsIds = new uint256[](terms.length); for (uint256 i = 0; i < terms.length; i++) { @@ -51,7 +51,7 @@ library LicensingHelper { address pilTemplate, address licensingModule, address licenseRegistry, - PILTerms calldata terms + PILTerms memory terms ) internal returns (uint256 licenseTermsId) { licenseTermsId = IPILicenseTemplate(pilTemplate).registerLicenseTerms(terms); attachLicenseTerms(ipId, licensingModule, licenseRegistry, pilTemplate, licenseTermsId); diff --git a/contracts/workflows/RoyaltyTokenDistributionWorkflows.sol b/contracts/workflows/RoyaltyTokenDistributionWorkflows.sol index 9be6402..09cba1e 100644 --- a/contracts/workflows/RoyaltyTokenDistributionWorkflows.sol +++ b/contracts/workflows/RoyaltyTokenDistributionWorkflows.sol @@ -82,7 +82,6 @@ contract RoyaltyTokenDistributionWorkflows is ) revert Errors.RoyaltyTokenDistributionWorkflows__ZeroAddressParam(); ROYALTY_MODULE = IRoyaltyModule(royaltyModule); - _disableInitializers(); } @@ -108,18 +107,22 @@ contract RoyaltyTokenDistributionWorkflows is /// @param spgNftContract The address of the SPG NFT contract. /// @param recipient The address to receive the NFT. /// @param ipMetadata The metadata for the IP. - /// @param terms The PIL terms to attach to the IP (must be a commercial license). + /// @param terms The PIL terms to attach to the IP (the license terms at index 0must be a commercial license). /// @param royaltyShares Authors of the IP and their shares of the royalty tokens, see {WorkflowStructs.RoyaltyShare}. /// @return ipId The ID of the registered IP. /// @return tokenId The ID of the minted NFT. - /// @return licenseTermsId The ID of the attached PIL terms. + /// @return licenseTermsIds The IDs of the attached PIL terms. function mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens( address spgNftContract, address recipient, WorkflowStructs.IPMetadata calldata ipMetadata, - PILTerms calldata terms, + PILTerms[] calldata terms, WorkflowStructs.RoyaltyShare[] calldata royaltyShares - ) external onlyMintAuthorized(spgNftContract) returns (address ipId, uint256 tokenId, uint256 licenseTermsId) { + ) + external + onlyMintAuthorized(spgNftContract) + returns (address ipId, uint256 tokenId, uint256[] memory licenseTermsIds) + { tokenId = ISPGNFT(spgNftContract).mintByPeriphery({ to: address(this), payer: msg.sender, @@ -128,7 +131,7 @@ contract RoyaltyTokenDistributionWorkflows is ipId = IP_ASSET_REGISTRY.register(block.chainid, spgNftContract, tokenId); MetadataHelper.setMetadata(ipId, address(CORE_METADATA_MODULE), ipMetadata); - licenseTermsId = LicensingHelper.registerPILTermsAndAttach( + licenseTermsIds = LicensingHelper.registerPILTermsAndAttach( ipId, address(PIL_TEMPLATE), address(LICENSING_MODULE), @@ -138,7 +141,7 @@ contract RoyaltyTokenDistributionWorkflows is _distributeRoyaltyTokens( ipId, - _deployRoyaltyVault(ipId, address(PIL_TEMPLATE), licenseTermsId), + _deployRoyaltyVault(ipId, address(PIL_TEMPLATE), licenseTermsIds[0]), royaltyShares, WorkflowStructs.SignatureData(address(0), 0, "") // no signature required. ); @@ -205,20 +208,20 @@ contract RoyaltyTokenDistributionWorkflows is /// @param nftContract The address of the NFT contract. /// @param tokenId The ID of the NFT. /// @param ipMetadata The metadata for the IP. - /// @param terms The PIL terms to attach to the IP (must be a commercial license). + /// @param terms The PIL terms to attach to the IP (the license terms at index 0 must be a commercial license). /// @param sigMetadata The signature data for the IP metadata. /// @param sigAttach The signature data for attaching the PIL terms. /// @return ipId The ID of the registered IP. - /// @return licenseTermsId The ID of the attached PIL terms. + /// @return licenseTermsIds The IDs of the attached PIL terms. /// @return ipRoyaltyVault The address of the deployed royalty vault. function registerIpAndAttachPILTermsAndDeployRoyaltyVault( address nftContract, uint256 tokenId, WorkflowStructs.IPMetadata calldata ipMetadata, - PILTerms calldata terms, + PILTerms[] calldata terms, WorkflowStructs.SignatureData calldata sigMetadata, WorkflowStructs.SignatureData calldata sigAttach - ) external returns (address ipId, uint256 licenseTermsId, address ipRoyaltyVault) { + ) external returns (address ipId, uint256[] memory licenseTermsIds, address ipRoyaltyVault) { ipId = IP_ASSET_REGISTRY.register(block.chainid, nftContract, tokenId); MetadataHelper.setMetadataWithSig( ipId, @@ -236,7 +239,7 @@ contract RoyaltyTokenDistributionWorkflows is sigAttach ); - licenseTermsId = LicensingHelper.registerPILTermsAndAttach( + licenseTermsIds = LicensingHelper.registerPILTermsAndAttach( ipId, address(PIL_TEMPLATE), address(LICENSING_MODULE), @@ -244,7 +247,7 @@ contract RoyaltyTokenDistributionWorkflows is terms ); - ipRoyaltyVault = _deployRoyaltyVault(ipId, address(PIL_TEMPLATE), licenseTermsId); + ipRoyaltyVault = _deployRoyaltyVault(ipId, address(PIL_TEMPLATE), licenseTermsIds[0]); } /// @notice Register an IP, make a derivative, and deploy a royalty vault. diff --git a/test/workflows/RoyaltyTokenDistributionWorkflows.t.sol b/test/workflows/RoyaltyTokenDistributionWorkflows.t.sol index 9b8b0fd..efbbf38 100644 --- a/test/workflows/RoyaltyTokenDistributionWorkflows.t.sol +++ b/test/workflows/RoyaltyTokenDistributionWorkflows.t.sol @@ -27,7 +27,7 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { uint256 private nftMintingFee; uint256 private licenseMintingFee; - PILTerms private commRemixTerms; + PILTerms[] private commRemixTerms; WorkflowStructs.RoyaltyShare[] private royaltyShares; WorkflowStructs.MakeDerivative private derivativeData; @@ -45,7 +45,7 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { mockToken.approve(address(spgNftPublic), nftMintingFee); mockToken.approve(address(royaltyTokenDistributionWorkflows), licenseMintingFee); - (address ipId, uint256 tokenId, uint256 licenseTermsId) = royaltyTokenDistributionWorkflows + (address ipId, uint256 tokenId, uint256[] memory licenseTermsIds) = royaltyTokenDistributionWorkflows .mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens({ spgNftContract: address(spgNftPublic), recipient: u.alice, @@ -59,13 +59,16 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { assertEq(tokenId, 2); assertEq(spgNftPublic.tokenURI(tokenId), string.concat(testBaseURI, ipMetadataDefault.nftMetadataURI)); assertMetadata(ipId, ipMetadataDefault); - assertEq(licenseTermsId, pilTemplate.getLicenseTermsId(commRemixTerms)); + assertEq(licenseTermsIds[0], pilTemplate.getLicenseTermsId(commRemixTerms[0])); (address licenseTemplateAttached, uint256 licenseTermsIdAttached) = licenseRegistry.getAttachedLicenseTerms( ipId, 0 ); assertEq(licenseTemplateAttached, address(pilTemplate)); - assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms)); + assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms[0])); + (licenseTemplateAttached, licenseTermsIdAttached) = licenseRegistry.getAttachedLicenseTerms(ipId, 1); + assertEq(licenseTemplateAttached, address(pilTemplate)); + assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms[1])); _assertRoyaltyTokenDistribution(ipId); } @@ -156,7 +159,7 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { vm.startPrank(u.alice); mockToken.mint(u.alice, licenseMintingFee); mockToken.approve(address(royaltyTokenDistributionWorkflows), licenseMintingFee); - (address ipId, uint256 licenseTermsId, address ipRoyaltyVault) = royaltyTokenDistributionWorkflows + (address ipId, uint256[] memory licenseTermsIds, address ipRoyaltyVault) = royaltyTokenDistributionWorkflows .registerIpAndAttachPILTermsAndDeployRoyaltyVault({ nftContract: address(mockNft), tokenId: tokenId, @@ -199,13 +202,16 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { assertTrue(ipAssetRegistry.isRegistered(ipId)); assertMetadata(ipId, ipMetadataDefault); - assertEq(licenseTermsId, pilTemplate.getLicenseTermsId(commRemixTerms)); + assertEq(licenseTermsIds[0], pilTemplate.getLicenseTermsId(commRemixTerms[0])); (address licenseTemplateAttached, uint256 licenseTermsIdAttached) = licenseRegistry.getAttachedLicenseTerms( ipId, 0 ); assertEq(licenseTemplateAttached, address(pilTemplate)); - assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms)); + assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms[0])); + (licenseTemplateAttached, licenseTermsIdAttached) = licenseRegistry.getAttachedLicenseTerms(ipId, 1); + assertEq(licenseTemplateAttached, address(pilTemplate)); + assertEq(licenseTermsIdAttached, pilTemplate.getLicenseTermsId(commRemixTerms[1])); _assertRoyaltyTokenDistribution(ipId); } @@ -347,12 +353,14 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { mockToken.mint(u.alice, licenseMintingFee); mockToken.approve(address(spgNftPublic), licenseMintingFee); + PILTerms[] memory terms = new PILTerms[](1); + terms[0] = PILFlavors.nonCommercialSocialRemixing(); vm.expectRevert(Errors.RoyaltyTokenDistributionWorkflows__RoyaltyVaultNotDeployed.selector); royaltyTokenDistributionWorkflows.mintAndRegisterIpAndAttachPILTermsAndDistributeRoyaltyTokens({ spgNftContract: address(spgNftPublic), recipient: u.alice, ipMetadata: ipMetadataDefault, - terms: PILFlavors.nonCommercialSocialRemixing(), + terms: terms, royaltyShares: royaltyShares }); vm.stopPrank(); @@ -364,12 +372,23 @@ contract RoyaltyTokenDistributionWorkflowsTest is BaseTest { uint32 testCommRevShare = 5 * 10 ** 6; // 5% - commRemixTerms = PILFlavors.commercialRemix({ - mintingFee: licenseMintingFee, - commercialRevShare: testCommRevShare, - royaltyPolicy: address(royaltyPolicyLAP), - currencyToken: address(mockToken) - }); + commRemixTerms.push( + PILFlavors.commercialRemix({ + mintingFee: licenseMintingFee, + commercialRevShare: testCommRevShare, + royaltyPolicy: address(royaltyPolicyLAP), + currencyToken: address(mockToken) + }) + ); + + commRemixTerms.push( + PILFlavors.commercialRemix({ + mintingFee: licenseMintingFee, + commercialRevShare: testCommRevShare, + royaltyPolicy: address(royaltyPolicyLRP), + currencyToken: address(mockToken) + }) + ); PILTerms[] memory commRemixTermsParent = new PILTerms[](1); commRemixTermsParent[0] = PILFlavors.commercialRemix({