Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(license attachment): add permission handling in registerPILTermsAndAttach #89

Merged
merged 3 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ interface ILicenseAttachmentWorkflows {
/// @notice Register Programmable IP License Terms (if unregistered) and attach it to IP.
/// @param ipId The ID of the IP.
/// @param terms The PIL terms to be registered.
/// @param sigAttach Signature data for attachLicenseTerms to the IP via the Licensing Module.
/// @return licenseTermsId The ID of the newly registered PIL terms.
function registerPILTermsAndAttach(address ipId, PILTerms calldata terms) external returns (uint256 licenseTermsId);
function registerPILTermsAndAttach(
address ipId,
PILTerms calldata terms,
WorkflowStructs.SignatureData calldata sigAttach
) external returns (uint256 licenseTermsId);

/// @notice Mint an NFT from a SPGNFT collection, register it with metadata as an IP,
/// register Programmable IPLicense
Expand Down
12 changes: 11 additions & 1 deletion contracts/workflows/LicenseAttachmentWorkflows.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,21 @@ contract LicenseAttachmentWorkflows is
/// @notice Register Programmable IP License Terms (if unregistered) and attach it to IP.
/// @param ipId The ID of the IP.
/// @param terms The PIL terms to be registered.
/// @param sigAttach Signature data for attachLicenseTerms to the IP via the Licensing Module.
/// @return licenseTermsId The ID of the newly registered PIL terms.
function registerPILTermsAndAttach(
address ipId,
PILTerms calldata terms
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),
Expand Down
14 changes: 3 additions & 11 deletions test/integration/workflows/LicenseAttachmentIntegration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ contract LicenseAttachmentIntegration is BaseIntegration {
});

uint256 deadline = block.timestamp + 1000;
(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
(bytes memory signature, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: licenseAttachmentWorkflowsAddr,
module: licensingModuleAddr,
Expand All @@ -59,18 +59,10 @@ contract LicenseAttachmentIntegration is BaseIntegration {
signerSk: testSenderSk
});

IIPAccount(payable(ipId)).executeWithSig({
to: accessControllerAddr,
value: 0,
data: data,
signer: testSender,
deadline: deadline,
signature: signature
});

uint256 licenseTermsId = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ipId,
terms: commUseTerms
terms: commUseTerms,
sigAttach: WorkflowStructs.SignatureData({ signer: testSender, deadline: deadline, signature: signature })
});

assertEq(licenseTermsId, pilTemplate.getLicenseTermsId(commUseTerms));
Expand Down
35 changes: 13 additions & 22 deletions test/integration/workflows/RoyaltyIntegration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -310,26 +310,15 @@ contract RoyaltyIntegration is BaseIntegration {
uint256 deadline = block.timestamp + 1000;

// set permission for licensing module to attach license terms to ancestor IP
{
(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
ipId: ancestorIpId,
to: licenseAttachmentWorkflowsAddr,
module: licensingModuleAddr,
selector: licensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(payable(ancestorIpId)).state(),
signerSk: testSenderSk
});

IIPAccount(payable(ancestorIpId)).executeWithSig({
to: accessControllerAddr,
value: 0,
data: data,
signer: testSender,
deadline: deadline,
signature: signature
});
}
(bytes memory signature, , ) = _getSetPermissionSigForPeriphery({
ipId: ancestorIpId,
to: licenseAttachmentWorkflowsAddr,
module: licensingModuleAddr,
selector: licensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(payable(ancestorIpId)).state(),
signerSk: testSenderSk
});

// register and attach Terms A and C to ancestor IP
commRemixTermsIdA = licenseAttachmentWorkflows.registerPILTermsAndAttach({
Expand All @@ -339,7 +328,8 @@ contract RoyaltyIntegration is BaseIntegration {
commercialRevShare: defaultCommRevShareA,
royaltyPolicy: royaltyPolicyLRPAddr,
currencyToken: address(StoryUSD)
})
}),
sigAttach: WorkflowStructs.SignatureData({ signer: testSender, deadline: deadline, signature: signature })
});

commRemixTermsIdC = licenseAttachmentWorkflows.registerPILTermsAndAttach({
Expand All @@ -349,7 +339,8 @@ contract RoyaltyIntegration is BaseIntegration {
commercialRevShare: defaultCommRevShareC,
royaltyPolicy: royaltyPolicyLAPAddr,
currencyToken: address(StoryUSD)
})
}),
sigAttach: WorkflowStructs.SignatureData({ signer: testSender, deadline: deadline, signature: signature })
});

// register childIpA as derivative of ancestorIp under Terms A
Expand Down
2 changes: 1 addition & 1 deletion test/story-nft/OrgNFT.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract OrgNFTTest is BaseTest {
);
}

function test_OrgNFT_interfaceSupport() public {
function test_OrgNFT_interfaceSupport() public view {
assertTrue(IOrgNFT(address(orgNft)).supportsInterface(type(IOrgNFT).interfaceId));
assertTrue(orgNft.supportsInterface(type(IERC721).interfaceId));
assertTrue(orgNft.supportsInterface(type(IERC721Metadata).interfaceId));
Expand Down
4 changes: 2 additions & 2 deletions test/story-nft/StoryBadgeNFT.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ contract StoryBadgeNFTTest is BaseTest {
);
}

function test_StoryBadgeNFT_interfaceSupport() public {
function test_StoryBadgeNFT_interfaceSupport() public view {
sebsadface marked this conversation as resolved.
Show resolved Hide resolved
assertTrue(BaseStoryNFT(rootStoryNft).supportsInterface(type(IStoryNFT).interfaceId));
assertTrue(BaseStoryNFT(rootStoryNft).supportsInterface(type(IERC721).interfaceId));
assertTrue(BaseStoryNFT(rootStoryNft).supportsInterface(type(IERC721Metadata).interfaceId));
Expand Down Expand Up @@ -210,7 +210,7 @@ contract StoryBadgeNFTTest is BaseTest {
bytes memory signature = _signAddress(rootStoryNftSignerSk, u.carl);

vm.startPrank(u.carl);
(uint256 tokenId, address ipId) = rootStoryNft.mint(u.carl, signature);
(uint256 tokenId, ) = rootStoryNft.mint(u.carl, signature);

vm.expectRevert(IStoryBadgeNFT.StoryBadgeNFT__TransferLocked.selector);
rootStoryNft.approve(u.bob, tokenId);
Expand Down
2 changes: 1 addition & 1 deletion test/story-nft/StoryNFTFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ contract StoryNFTFactoryTest is BaseTest {

function test_StoryNFTFactory_getStoryNftAddress() public {
vm.startPrank(u.carl);
(address orgNft, uint256 orgTokenId, address orgIpId, address storyNft) = storyNftFactory.deployStoryNft({
(, uint256 orgTokenId, address orgIpId, address storyNft) = storyNftFactory.deployStoryNft({
storyNftTemplate: defaultStoryNftTemplate,
orgNftRecipient: u.carl,
orgName: orgName,
Expand Down
2 changes: 1 addition & 1 deletion test/utils/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ contract BaseTest is Test, DeployHelper {
}

/// @dev Uses `signerSk` to sign `addr` and return the signature.
function _signAddress(uint256 signerSk, address addr) internal view returns (bytes memory signature) {
function _signAddress(uint256 signerSk, address addr) internal pure returns (bytes memory signature) {
bytes32 digest = keccak256(abi.encodePacked(addr)).toEthSignedMessageHash();
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerSk, digest);
signature = abi.encodePacked(r, s, v);
Expand Down
60 changes: 23 additions & 37 deletions test/workflows/LicenseAttachmentWorkflows.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
address payable ipId = ipAsset[1].ipId;
uint256 deadline = block.timestamp + 1000;

(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
(bytes memory signature, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
Expand All @@ -59,25 +59,16 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
signerSk: sk.alice
});

vm.prank(address(0x111));
IIPAccount(ipId).executeWithSig({
to: address(accessController),
value: 0,
data: data,
signer: u.alice,
deadline: deadline,
signature: signature
});

uint256 ltAmt = pilTemplate.totalRegisteredLicenseTerms();

uint256 licenseTermsId = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ipAsset[1].ipId,
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);
Expand Down Expand Up @@ -168,7 +159,7 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
address payable ipId = ipAsset[1].ipId;
uint256 deadline = block.timestamp + 1000;

(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
(bytes memory signature1, , ) = _getSetPermissionSigForPeriphery({
ipId: ipId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
Expand All @@ -178,22 +169,24 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
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)
})
}),
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
Expand All @@ -203,7 +196,8 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
mintingFee: 100,
currencyToken: address(mockToken),
royaltyPolicy: address(royaltyPolicyLAP)
})
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature2 })
});

assertEq(licenseTermsId1, licenseTermsId2);
Expand All @@ -229,7 +223,7 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
uint256[] memory licenseTermsIds = new uint256[](1);
licenseTermsIds[0] = licenseTermsIdParent;

(address ipIdChild, uint256 tokenIdChild) = derivativeWorkflows.mintAndRegisterIpAndMakeDerivative({
(address ipIdChild, ) = derivativeWorkflows.mintAndRegisterIpAndMakeDerivative({
spgNftContract: address(nftContract),
derivData: WorkflowStructs.MakeDerivative({
parentIpIds: parentIpIds,
Expand All @@ -243,7 +237,7 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {

uint256 deadline = block.timestamp + 1000;

(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
(bytes memory signature, , ) = _getSetPermissionSigForPeriphery({
ipId: ipIdChild,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
Expand All @@ -253,15 +247,6 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
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({
Expand All @@ -270,7 +255,8 @@ contract LicenseAttachmentWorkflowsTest is BaseTest {
mintingFee: 100,
currencyToken: address(mockToken),
royaltyPolicy: address(royaltyPolicyLAP)
})
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.alice, deadline: deadline, signature: signature })
});
}
}
59 changes: 32 additions & 27 deletions test/workflows/RoyaltyWorkflows.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ contract RoyaltyWorkflowsTest is BaseTest {

// set permission for licensing module to attach license terms to ancestor IP
{
(bytes memory signature, , bytes memory data) = _getSetPermissionSigForPeriphery({
(bytes memory signatureA, , ) = _getSetPermissionSigForPeriphery({
ipId: ancestorIpId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
Expand All @@ -404,36 +404,41 @@ contract RoyaltyWorkflowsTest is BaseTest {
signerSk: sk.admin
});

IIPAccount(payable(ancestorIpId)).executeWithSig({
to: address(accessController),
value: 0,
data: data,
signer: u.admin,
deadline: deadline,
signature: signature
// register and attach Terms A and C to ancestor IP
commRemixTermsIdA = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ancestorIpId,
terms: PILFlavors.commercialRemix({
mintingFee: defaultMintingFeeA,
commercialRevShare: defaultCommRevShareA,
royaltyPolicy: address(royaltyPolicyLRP),
currencyToken: address(mockTokenA)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.admin, deadline: deadline, signature: signatureA })
});
}

// register and attach Terms A and C to ancestor IP
commRemixTermsIdA = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ancestorIpId,
terms: PILFlavors.commercialRemix({
mintingFee: defaultMintingFeeA,
commercialRevShare: defaultCommRevShareA,
royaltyPolicy: address(royaltyPolicyLRP),
currencyToken: address(mockTokenA)
})
});
{
(bytes memory signatureC, , ) = _getSetPermissionSigForPeriphery({
ipId: ancestorIpId,
to: address(licenseAttachmentWorkflows),
module: address(licensingModule),
selector: licensingModule.attachLicenseTerms.selector,
deadline: deadline,
state: IIPAccount(payable(ancestorIpId)).state(),
signerSk: sk.admin
});

commRemixTermsIdC = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ancestorIpId,
terms: PILFlavors.commercialRemix({
mintingFee: defaultMintingFeeC,
commercialRevShare: defaultCommRevShareC,
royaltyPolicy: address(royaltyPolicyLAP),
currencyToken: address(mockTokenC)
})
});
commRemixTermsIdC = licenseAttachmentWorkflows.registerPILTermsAndAttach({
ipId: ancestorIpId,
terms: PILFlavors.commercialRemix({
mintingFee: defaultMintingFeeC,
commercialRevShare: defaultCommRevShareC,
royaltyPolicy: address(royaltyPolicyLAP),
currencyToken: address(mockTokenC)
}),
sigAttach: WorkflowStructs.SignatureData({ signer: u.admin, deadline: deadline, signature: signatureC })
});
}

// register childIpA as derivative of ancestorIp under Terms A
{
Expand Down
Loading