diff --git a/contracts/lib/LicensingHelper.sol b/contracts/lib/LicensingHelper.sol index 25561f8..2dca9c5 100644 --- a/contracts/lib/LicensingHelper.sol +++ b/contracts/lib/LicensingHelper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import { ILicenseRegistry } from "@storyprotocol/core/interfaces/registries/ILicenseRegistry.sol"; +import { Errors as CoreErrors } from "@storyprotocol/core/lib/Errors.sol"; import { ILicensingModule } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingModule.sol"; import { IPILicenseTemplate, PILTerms } from "@storyprotocol/core/interfaces/modules/licensing/IPILicenseTemplate.sol"; @@ -40,9 +40,15 @@ library LicensingHelper { address licenseTemplate, uint256 licenseTermsId ) internal { - // Returns if license terms are already attached. - if (ILicenseRegistry(licenseRegistry).hasIpAttachedLicenseTerms(ipId, licenseTemplate, licenseTermsId)) return; - - ILicensingModule(licensingModule).attachLicenseTerms(ipId, licenseTemplate, licenseTermsId); + try ILicensingModule(licensingModule).attachLicenseTerms(ipId, licenseTemplate, licenseTermsId) { + return; // license terms are attached successfully + } catch (bytes memory reason) { + // if the error is not that the license terms are already attached, revert with the original error + if (CoreErrors.LicenseRegistry__LicenseTermsAlreadyAttached.selector != bytes4(reason)) { + assembly { + revert(add(reason, 32), mload(reason)) + } + } + } } } diff --git a/test/workflows/LicenseAttachmentWorkflows.t.sol b/test/workflows/LicenseAttachmentWorkflows.t.sol index ed36c1a..a3e37d8 100644 --- a/test/workflows/LicenseAttachmentWorkflows.t.sol +++ b/test/workflows/LicenseAttachmentWorkflows.t.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.26; // external import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { Errors as CoreErrors } from "@storyprotocol/core/lib/Errors.sol"; import { ICoreMetadataModule } from "@storyprotocol/core/interfaces/modules/metadata/ICoreMetadataModule.sol"; import { IIPAccount } from "@storyprotocol/core/interfaces/IIPAccount.sol"; import { ILicensingModule } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingModule.sol"; @@ -158,4 +159,118 @@ contract LicenseAttachmentWorkflowsTest is BaseTest { sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: sigAttach }) }); } + + function test_LicenseAttachmentWorkflows_registerPILTermsAndAttach_idempotency() + public + withCollection + withIp(u.alice) + { + address payable ipId = ipAsset[1].ipId; + uint256 deadline = block.timestamp + 1000; + + (bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({ + ipId: ipId, + to: address(licenseAttachmentWorkflows), + module: address(licensingModule), + selector: ILicensingModule.attachLicenseTerms.selector, + deadline: deadline, + state: IIPAccount(ipId).state(), + signerSk: sk.alice + }); + + IIPAccount(ipId).executeWithSig({ + to: address(accessController), + value: 0, + data: data, + signer: u.alice, + deadline: deadline, + signature: signature + }); + + uint256 licenseTermsId1 = licenseAttachmentWorkflows.registerPILTermsAndAttach({ + ipId: ipId, + terms: PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(mockToken), + royaltyPolicy: address(royaltyPolicyLAP) + }) + }); + + // attach the same license terms to the IP again, but it shouldn't revert + uint256 licenseTermsId2 = licenseAttachmentWorkflows.registerPILTermsAndAttach({ + ipId: ipId, + terms: PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(mockToken), + royaltyPolicy: address(royaltyPolicyLAP) + }) + }); + + assertEq(licenseTermsId1, licenseTermsId2); + } + + function test_revert_registerPILTermsAndAttach_DerivativesCannotAddLicenseTerms() + public + withCollection + whenCallerHasMinterRole + withEnoughTokens(address(licenseAttachmentWorkflows)) + { + (address ipIdParent, , uint256 licenseTermsIdParent) = licenseAttachmentWorkflows + .mintAndRegisterIpAndAttachPILTerms({ + spgNftContract: address(nftContract), + recipient: caller, + ipMetadata: ipMetadataDefault, + terms: PILFlavors.nonCommercialSocialRemixing() + }); + + address[] memory parentIpIds = new address[](1); + parentIpIds[0] = ipIdParent; + + uint256[] memory licenseTermsIds = new uint256[](1); + licenseTermsIds[0] = licenseTermsIdParent; + + (address ipIdChild, uint256 tokenIdChild) = derivativeWorkflows.mintAndRegisterIpAndMakeDerivative({ + spgNftContract: address(nftContract), + derivData: WorkflowStructs.MakeDerivative({ + parentIpIds: parentIpIds, + licenseTemplate: address(pilTemplate), + licenseTermsIds: licenseTermsIds, + royaltyContext: "" + }), + ipMetadata: ipMetadataDefault, + recipient: caller + }); + + uint256 deadline = block.timestamp + 1000; + + (bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({ + ipId: ipIdChild, + to: address(licenseAttachmentWorkflows), + module: address(licensingModule), + selector: ILicensingModule.attachLicenseTerms.selector, + deadline: deadline, + state: IIPAccount(payable(ipIdChild)).state(), + signerSk: sk.alice + }); + + IIPAccount(payable(ipIdChild)).executeWithSig({ + to: address(accessController), + value: 0, + data: data, + signer: u.alice, + deadline: deadline, + signature: signature + }); + + // attach a different license terms to the child ip, should revert with the correct error + vm.expectRevert(CoreErrors.LicensingModule__DerivativesCannotAddLicenseTerms.selector); + licenseAttachmentWorkflows.registerPILTermsAndAttach({ + ipId: ipIdChild, + terms: PILFlavors.commercialUse({ + mintingFee: 100, + currencyToken: address(mockToken), + royaltyPolicy: address(royaltyPolicyLAP) + }) + }); + } }