Skip to content

Commit

Permalink
fix(licensing): restore single license attachment for backward compat…
Browse files Browse the repository at this point in the history
…ibility (#127)
  • Loading branch information
sebsadface authored Nov 22, 2024
1 parent 8096551 commit b2a284e
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 0 deletions.
33 changes: 33 additions & 0 deletions contracts/interfaces/workflows/ILicenseAttachmentWorkflows.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,37 @@ interface ILicenseAttachmentWorkflows {
WorkflowStructs.SignatureData calldata sigMetadata,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (address ipId, uint256[] memory licenseTermsIds);

////////////////////////////////////////////////////////////////////////////
// DEPRECATED //
////////////////////////////////////////////////////////////////////////////

/// @notice Register Programmable IP License Terms (if unregistered) and attach it to IP.
/// @notice [DEPRECATED]
function registerPILTermsAndAttach(
address ipId,
PILTerms calldata terms,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (uint256 licenseTermsId);

/// Mint an NFT from a SPGNFT collection, register it with metadata as an IP,
/// register Programmable IPLicense
/// @notice [DEPRECATED]
function mintAndRegisterIpAndAttachPILTerms(
address spgNftContract,
address recipient,
WorkflowStructs.IPMetadata calldata ipMetadata,
PILTerms calldata terms
) external returns (address ipId, uint256 tokenId, uint256 licenseTermsId);

/// @notice Register a given NFT as an IP and attach Programmable IP License Terms.
/// @notice [DEPRECATED]
function registerIpAndAttachPILTerms(
address nftContract,
uint256 tokenId,
WorkflowStructs.IPMetadata calldata ipMetadata,
PILTerms calldata terms,
WorkflowStructs.SignatureData calldata sigMetadata,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (address ipId, uint256 licenseTermsId);
}
92 changes: 92 additions & 0 deletions contracts/workflows/LicenseAttachmentWorkflows.sol
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,98 @@ contract LicenseAttachmentWorkflows is
);
}

////////////////////////////////////////////////////////////////////////////
// DEPRECATED //
////////////////////////////////////////////////////////////////////////////

/// @notice Register Programmable IP License Terms (if unregistered) and attach it to IP.
/// @notice [DEPRECATED]
function registerPILTermsAndAttach(
address ipId,
PILTerms calldata terms,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (uint256 licenseTermsId) {
PermissionHelper.setPermissionForModule(
ipId,
address(LICENSING_MODULE),
address(ACCESS_CONTROLLER),
ILicensingModule.attachLicenseTerms.selector,
sigAttach
);

licenseTermsId = LicensingHelper.registerPILTermsAndAttach(
ipId,
address(PIL_TEMPLATE),
address(LICENSING_MODULE),
address(LICENSE_REGISTRY),
terms
);
}

/// @notice Mint an NFT from a SPGNFT collection, register it with metadata as an IP,
/// register Programmable IP License Terms (if unregistered), and attach it to the registered IP.
/// @notice [DEPRECATED]
function mintAndRegisterIpAndAttachPILTerms(
address spgNftContract,
address recipient,
WorkflowStructs.IPMetadata calldata ipMetadata,
PILTerms calldata terms
) external onlyMintAuthorized(spgNftContract) returns (address ipId, uint256 tokenId, uint256 licenseTermsId) {
tokenId = ISPGNFT(spgNftContract).mintByPeriphery({
to: address(this),
payer: msg.sender,
nftMetadataURI: ipMetadata.nftMetadataURI
});
ipId = IP_ASSET_REGISTRY.register(block.chainid, spgNftContract, tokenId);
MetadataHelper.setMetadata(ipId, address(CORE_METADATA_MODULE), ipMetadata);

licenseTermsId = LicensingHelper.registerPILTermsAndAttach(
ipId,
address(PIL_TEMPLATE),
address(LICENSING_MODULE),
address(LICENSE_REGISTRY),
terms
);

ISPGNFT(spgNftContract).safeTransferFrom(address(this), recipient, tokenId, "");
}

/// @notice Register a given NFT as an IP and attach Programmable IP License Terms.
/// @dev [DEPRECATED]
function registerIpAndAttachPILTerms(
address nftContract,
uint256 tokenId,
WorkflowStructs.IPMetadata calldata ipMetadata,
PILTerms calldata terms,
WorkflowStructs.SignatureData calldata sigMetadata,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (address ipId, uint256 licenseTermsId) {
ipId = IP_ASSET_REGISTRY.register(block.chainid, nftContract, tokenId);
MetadataHelper.setMetadataWithSig(
ipId,
address(CORE_METADATA_MODULE),
address(ACCESS_CONTROLLER),
ipMetadata,
sigMetadata
);

PermissionHelper.setPermissionForModule(
ipId,
address(LICENSING_MODULE),
address(ACCESS_CONTROLLER),
ILicensingModule.attachLicenseTerms.selector,
sigAttach
);

licenseTermsId = LicensingHelper.registerPILTermsAndAttach(
ipId,
address(PIL_TEMPLATE),
address(LICENSING_MODULE),
address(LICENSE_REGISTRY),
terms
);
}

//
// Upgrade
//
Expand Down
222 changes: 222 additions & 0 deletions test/workflows/LicenseAttachmentWorkflows.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,226 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature })
});
}

////////////////////////////////////////////////////////////////////////////
// DEPRECATED //
////////////////////////////////////////////////////////////////////////////
function test_LicenseAttachmentWorkflows_registerPILTermsAndAttach_deprecated()
public
withCollection
withIp(u.alice)
{
address payable ipId = ipAsset[1].ipId;
uint256 deadline = block.timestamp + 1000;

(bytes memory signature, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: ILicensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(ipId).state(),
signerSk: sk.alice
});

uint256 ltAmt = pilTemplate.totalRegisteredLicenseTerms();

uint256 licenseTermsId = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ipId,
terms: PILFlavors.commercialUse({
mintingFee: 100,
currencyToken: address(mockToken),
royaltyPolicy: address(royaltyPolicyLAP)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature })
});

assertEq(licenseTermsId, ltAmt + 1);
}

function test_LicenseAttachmentWorkflows_mintAndRegisterIpAndAttachPILTerms_deprecated()
public
withCollection
whenCallerHasMinterRole
withEnoughTokens(address(licenseAttachmentWorkflows))
{
(address ipId1, uint256 tokenId1, uint256 licenseTermsId1) = licenseAttachmentWorkflows
.mintAndRegisterIpAndAttachPILTerms({
spgNftContract: address(nftContract),
recipient: caller,
ipMetadata: ipMetadataEmpty,
terms: PILFlavors.nonCommercialSocialRemixing()
});
assertTrue(ipAssetRegistry.isRegistered(ipId1));
assertEq(tokenId1, 1);
assertEq(licenseTermsId1, 1);
assertEq(nftContract.tokenURI(tokenId1), string.concat(testBaseURI, tokenId1.toString()));
assertMetadata(ipId1, ipMetadataEmpty);
(address licenseTemplate, uint256 licenseTermsId) = licenseRegistry.getAttachedLicenseTerms(ipId1, 0);
assertEq(licenseTemplate, address(pilTemplate));
assertEq(licenseTermsId, licenseTermsId1);

(address ipId2, uint256 tokenId2, uint256 licenseTermsId2) = licenseAttachmentWorkflows
.mintAndRegisterIpAndAttachPILTerms({
spgNftContract: address(nftContract),
recipient: caller,
ipMetadata: ipMetadataDefault,
terms: PILFlavors.nonCommercialSocialRemixing()
});
assertTrue(ipAssetRegistry.isRegistered(ipId2));
assertEq(tokenId2, 2);
assertEq(licenseTermsId1, licenseTermsId2);
assertEq(nftContract.tokenURI(tokenId2), string.concat(testBaseURI, ipMetadataDefault.nftMetadataURI));
assertMetadata(ipId2, ipMetadataDefault);
}

function test_LicenseAttachmentWorkflows_registerIpAndAttachPILTerms_deprecated()
public
withCollection
whenCallerHasMinterRole
withEnoughTokens(address(licenseAttachmentWorkflows))
{
uint256 tokenId = nftContract.mint(address(caller), ipMetadataEmpty.nftMetadataURI);
address payable ipId = payable(ipAssetRegistry.ipId(block.chainid, address(nftContract), tokenId));

uint256 deadline = block.timestamp + 1000;

(bytes memory sigMetadata, bytes32 expectedState, ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(coreMetadataModule),
selector: ICoreMetadataModule.setAll.selector,
deadline: deadline,
state: bytes32(0),
signerSk: sk.alice
});

(bytes memory sigAttach, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: ILicensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: expectedState,
signerSk: sk.alice
});

licenseAttachmentWorkflows.registerIpAndAttachPILTerms({
nftContract: address(nftContract),
tokenId: tokenId,
ipMetadata: ipMetadataDefault,
terms: PILFlavors.nonCommercialSocialRemixing(),
sigMetadata: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: sigMetadata }),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: sigAttach })
});
}

function test_LicenseAttachmentWorkflows_registerPILTermsAndAttach_idempotency_deprecated()
public
withCollection
withIp(u.alice)
{
address payable ipId = ipAsset[1].ipId;
uint256 deadline = block.timestamp + 1000;

(bytes memory signature1, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: ILicensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(ipId).state(),
signerSk: sk.alice
});

uint256 licenseTermsId1 = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ipId,
terms: PILFlavors.commercialUse({
mintingFee: 100,
currencyToken: address(mockToken),
royaltyPolicy: address(royaltyPolicyLAP)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature1 })
});

(bytes memory signature2, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: ILicensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(ipId).state(),
signerSk: sk.alice
});

// 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)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature2 })
});

assertEq(licenseTermsId1, licenseTermsId2);
}

function test_revert_registerPILTermsAndAttach_DerivativesCannotAddLicenseTerms_deprecated()
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, ) = 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, , ) = _getSetPermissionSigForPeriphery({
ipId: ipIdChild,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: ILicensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(payable(ipIdChild)).state(),
signerSk: sk.alice
});

// 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)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature })
});
}
}

0 comments on commit b2a284e

Please sign in to comment.