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(hooks): add LockLicenseHook to locking/disabling licensing operations #102

Merged
merged 2 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
86 changes: 86 additions & 0 deletions contracts/hooks/LockLicenseHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { BaseModule } from "@storyprotocol/core/modules/BaseModule.sol";
import { ILicensingHook } from "@storyprotocol/core/interfaces/modules/licensing/ILicensingHook.sol";

contract LockLicenseHook is BaseModule, ILicensingHook {
string public constant override name = "LockLicenseHook";

/// @notice Emitted when attempting to perform an action on a locked license
/// @param licensorIpId The licensor IP id that is locked
/// @param licenseTemplate The license template address that is locked
/// @param licenseTermsId The license terms id that is locked
error LockLicenseHook_LicenseLocked(address licensorIpId, address licenseTemplate, uint256 licenseTermsId);

/// @notice This function is called when the LicensingModule mints license tokens.
/// @dev This function will always revert to prevent any license token minting.
/// @param caller The address of the caller who calling the mintLicenseTokens() function.
/// @param licensorIpId The ID of licensor IP from which issue the license tokens.
/// @param licenseTemplate The address of the license template.
/// @param licenseTermsId The ID of the license terms within the license template,
/// which is used to mint license tokens.
/// @param amount The amount of license tokens to mint.
/// @param receiver The address of the receiver who receive the license tokens.
/// @param hookData The data to be used by the licensing hook.
/// @return totalMintingFee The total minting fee to be paid when minting amount of license tokens.
function beforeMintLicenseTokens(
address caller,
address licensorIpId,
address licenseTemplate,
uint256 licenseTermsId,
uint256 amount,
address receiver,
bytes calldata hookData
) external returns (uint256 totalMintingFee) {
revert LockLicenseHook_LicenseLocked(licensorIpId, licenseTemplate, licenseTermsId);
}

/// @notice This function is called before finalizing LicensingModule.registerDerivative(), after calling
/// LicenseRegistry.registerDerivative().
/// @dev This function will always revert to prevent any derivative registration.
/// @param childIpId The derivative IP ID.
/// @param parentIpId The parent IP ID.
/// @param licenseTemplate The address of the license template.
/// @param licenseTermsId The ID of the license terms within the license template.
/// @param hookData The data to be used by the licensing hook.
/// @return mintingFee The minting fee to be paid when register child IP to the parent IP as derivative.
function beforeRegisterDerivative(
address caller,
address childIpId,
address parentIpId,
address licenseTemplate,
uint256 licenseTermsId,
bytes calldata hookData
) external returns (uint256 mintingFee) {
revert LockLicenseHook_LicenseLocked(parentIpId, licenseTemplate, licenseTermsId);
}

/// @notice This function is called when the LicensingModule calculates/predict the minting fee for license tokens.
/// @dev This function will always revert to prevent any license minting fee calculation/prediction.
/// @param caller The address of the caller who calling the mintLicenseTokens() function.
/// @param licensorIpId The ID of licensor IP from which issue the license tokens.
/// @param licenseTemplate The address of the license template.
/// @param licenseTermsId The ID of the license terms within the license template,
/// which is used to mint license tokens.
/// @param amount The amount of license tokens to mint.
/// @param receiver The address of the receiver who receive the license tokens.
/// @param hookData The data to be used by the licensing hook.
/// @return totalMintingFee The minting fee to be paid when minting amount of license tokens.
function calculateMintingFee(
address caller,
address licensorIpId,
address licenseTemplate,
uint256 licenseTermsId,
uint256 amount,
address receiver,
bytes calldata hookData
) external view returns (uint256 totalMintingFee) {
revert LockLicenseHook_LicenseLocked(licensorIpId, licenseTemplate, licenseTermsId);
}

function supportsInterface(bytes4 interfaceId) public view virtual override(BaseModule, IERC165) returns (bool) {
return interfaceId == type(ILicensingHook).interfaceId || super.supportsInterface(interfaceId);
}
}
136 changes: 136 additions & 0 deletions test/hooks/LockLicenseHook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import { Licensing } from "@storyprotocol/core/lib/Licensing.sol";
import { PILFlavors } from "@storyprotocol/core/lib/PILFlavors.sol";

import { BaseTest } from "../utils/BaseTest.t.sol";
import { LockLicenseHook } from "../../contracts/hooks/LockLicenseHook.sol";

contract LockLicenseHookTest is BaseTest {
address public ipId;
address public ipOwner;

function setUp() public override {
super.setUp();
ipOwner = u.alice;
uint256 tokenId = mockNft.mint(ipOwner);
ipId = ipAssetRegistry.register(block.chainid, address(mockNft), tokenId);
vm.label(ipId, "IPAccount");
}

function test_LockLicenseHook_revert_beforeMintLicenseTokens() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
LockLicenseHook lockLicenseHook = new LockLicenseHook();
vm.prank(u.admin);
moduleRegistry.registerModule("LockLicenseHook", address(lockLicenseHook));

vm.startPrank(ipOwner);
Licensing.LicensingConfig memory licensingConfig = Licensing.LicensingConfig({
isSet: true,
mintingFee: 0,
licensingHook: address(lockLicenseHook),
hookData: "",
commercialRevShare: 0
});
licensingModule.setLicensingConfig(ipId, address(pilTemplate), socialRemixTermsId, licensingConfig);
vm.stopPrank();

vm.startPrank(u.bob);
vm.expectRevert(
abi.encodeWithSelector(
LockLicenseHook.LockLicenseHook_LicenseLocked.selector,
ipId,
address(pilTemplate),
socialRemixTermsId
)
);
licensingModule.mintLicenseTokens({
licensorIpId: ipId,
licenseTemplate: address(pilTemplate),
licenseTermsId: socialRemixTermsId,
amount: 1,
receiver: u.bob,
royaltyContext: "",
maxMintingFee: 0
});
}

function test_LockLicenseHook_revert_beforeRegisterDerivative() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
LockLicenseHook lockLicenseHook = new LockLicenseHook();
vm.prank(u.admin);
moduleRegistry.registerModule("LockLicenseHook", address(lockLicenseHook));

vm.startPrank(ipOwner);
Licensing.LicensingConfig memory licensingConfig = Licensing.LicensingConfig({
isSet: true,
mintingFee: 0,
licensingHook: address(lockLicenseHook),
hookData: "",
commercialRevShare: 0
});
licensingModule.setLicensingConfig(ipId, address(pilTemplate), socialRemixTermsId, licensingConfig);
vm.stopPrank();

address[] memory parentIpIds = new address[](1);
parentIpIds[0] = ipId;
uint256[] memory licenseTermsIds = new uint256[](1);
licenseTermsIds[0] = socialRemixTermsId;

vm.startPrank(u.bob);
address ipIdChild = ipAssetRegistry.register(block.chainid, address(mockNft), mockNft.mint(u.bob));
vm.expectRevert(
abi.encodeWithSelector(
LockLicenseHook.LockLicenseHook_LicenseLocked.selector,
ipId,
address(pilTemplate),
socialRemixTermsId
)
);
licensingModule.registerDerivative({
childIpId: ipIdChild,
parentIpIds: parentIpIds,
licenseTermsIds: licenseTermsIds,
licenseTemplate: address(pilTemplate),
royaltyContext: "",
maxMintingFee: 0
});
}

function test_LockLicenseHook_revert_calculateMintingFee() public {
uint256 socialRemixTermsId = pilTemplate.registerLicenseTerms(PILFlavors.nonCommercialSocialRemixing());
LockLicenseHook lockLicenseHook = new LockLicenseHook();
vm.prank(u.admin);
moduleRegistry.registerModule("LockLicenseHook", address(lockLicenseHook));

vm.startPrank(ipOwner);
Licensing.LicensingConfig memory licensingConfig = Licensing.LicensingConfig({
isSet: true,
mintingFee: 0,
licensingHook: address(lockLicenseHook),
hookData: "",
commercialRevShare: 0
});
licensingModule.setLicensingConfig(ipId, address(pilTemplate), socialRemixTermsId, licensingConfig);
vm.stopPrank();

vm.startPrank(u.bob);
vm.expectRevert(
abi.encodeWithSelector(
LockLicenseHook.LockLicenseHook_LicenseLocked.selector,
ipId,
address(pilTemplate),
socialRemixTermsId
)
);
licensingModule.predictMintingLicenseFee({
licensorIpId: ipId,
licenseTemplate: address(pilTemplate),
licenseTermsId: socialRemixTermsId,
amount: 1,
receiver: u.bob,
royaltyContext: ""
});
}
}
2 changes: 1 addition & 1 deletion test/hooks/TotalLicenseTokenLimitHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PILFlavors } from "@storyprotocol/core/lib/PILFlavors.sol";
import { Licensing } from "@storyprotocol/core/lib/Licensing.sol";
import { Errors } from "@storyprotocol/core/lib/Errors.sol";

import { TotalLicenseTokenLimitHook } from "contracts/hooks/TotalLicenseTokenLimitHook.sol";
import { TotalLicenseTokenLimitHook } from "../../contracts/hooks/TotalLicenseTokenLimitHook.sol";

import { BaseTest } from "../utils/BaseTest.t.sol";

Expand Down
Loading