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

Adds ERC165 check for external royalty policy registration #319

Merged
merged 2 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -1,11 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IExternalRoyaltyPolicyBase } from "./IExternalRoyaltyPolicyBase.sol";

/// @title IExternalRoyaltyPolicy interface
interface IExternalRoyaltyPolicy {
/// @notice Returns the amount of royalty tokens required to link a child to a given IP asset
/// @param ipId The ipId of the IP asset
/// @param licensePercent The percentage of the license
/// @return The amount of royalty tokens required to link a child to a given IP asset
function getPolicyRtsRequiredToLink(address ipId, uint32 licensePercent) external view returns (uint32);
}
interface IExternalRoyaltyPolicy is IExternalRoyaltyPolicyBase, IERC165 {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

/// @title IExternalRoyaltyPolicyBase interface
interface IExternalRoyaltyPolicyBase {
/// @notice Returns the amount of royalty tokens required to link a child to a given IP asset
/// @param ipId The ipId of the IP asset
/// @param licensePercent The percentage of the license
/// @return The amount of royalty tokens required to link a child to a given IP asset
function getPolicyRtsRequiredToLink(address ipId, uint32 licensePercent) external view returns (uint32);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { IExternalRoyaltyPolicy } from "./IExternalRoyaltyPolicy.sol";
import { IExternalRoyaltyPolicyBase } from "./IExternalRoyaltyPolicyBase.sol";

/// @title RoyaltyPolicy interface
interface IRoyaltyPolicy is IExternalRoyaltyPolicy {
interface IRoyaltyPolicy is IExternalRoyaltyPolicyBase {
/// @notice Executes royalty related logic on minting a license
/// @dev Enforced to be only callable by RoyaltyModule
/// @param ipId The ipId whose license is being minted (licensor)
Expand Down
3 changes: 3 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,9 @@ library Errors {
/// @notice IP is expired.
error RoyaltyModule__IpExpired();

/// @notice Invalid external royalty policy.
error RoyaltyModule__InvalidExternalRoyaltyPolicy();

////////////////////////////////////////////////////////////////////////////
// Royalty Policy LAP //
////////////////////////////////////////////////////////////////////////////
Expand Down
17 changes: 11 additions & 6 deletions contracts/modules/royalty/RoyaltyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { BaseModule } from "../BaseModule.sol";
import { VaultController } from "./policies/VaultController.sol";
import { IRoyaltyModule } from "../../interfaces/modules/royalty/IRoyaltyModule.sol";
import { IRoyaltyPolicy } from "../../interfaces/modules/royalty/policies/IRoyaltyPolicy.sol";
import { IExternalRoyaltyPolicy } from "../../interfaces/modules/royalty/policies/IExternalRoyaltyPolicy.sol";
import { IExternalRoyaltyPolicyBase } from "../../interfaces/modules/royalty/policies/IExternalRoyaltyPolicyBase.sol";
import { IGroupIPAssetRegistry } from "../../interfaces/registries/IGroupIPAssetRegistry.sol";
import { IIpRoyaltyVault } from "../../interfaces/modules/royalty/policies/IIpRoyaltyVault.sol";
import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.sol";
Expand Down Expand Up @@ -223,12 +223,17 @@ contract RoyaltyModule is IRoyaltyModule, VaultController, ReentrancyGuardUpgrad
$.isRegisteredExternalRoyaltyPolicy[externalRoyaltyPolicy]
) revert Errors.RoyaltyModule__PolicyAlreadyWhitelistedOrRegistered();

// checks if the IExternalRoyaltyPolicy call does not revert
// external royalty policies contracts should inherit IExternalRoyaltyPolicy interface
if (IExternalRoyaltyPolicy(externalRoyaltyPolicy).getPolicyRtsRequiredToLink(address(0), 0) >= uint32(0)) {
$.isRegisteredExternalRoyaltyPolicy[externalRoyaltyPolicy] = true;
emit ExternalRoyaltyPolicyRegistered(externalRoyaltyPolicy);
}
// and implement the getPolicyRtsRequiredToLink() and ERC165 supportsInterface() functions
if (
!ERC165Checker.supportsInterface(
externalRoyaltyPolicy,
IExternalRoyaltyPolicyBase.getPolicyRtsRequiredToLink.selector
)
) revert Errors.RoyaltyModule__InvalidExternalRoyaltyPolicy();

$.isRegisteredExternalRoyaltyPolicy[externalRoyaltyPolicy] = true;
emit ExternalRoyaltyPolicyRegistered(externalRoyaltyPolicy);
}

/// @notice Executes royalty related logic on license minting
Expand Down
9 changes: 8 additions & 1 deletion test/foundry/mocks/policy/MockExternalRoyaltyPolicy1.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { IERC165, ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

// solhint-disable-next-line max-line-length
import { IExternalRoyaltyPolicy } from "../../../../contracts/interfaces/modules/royalty/policies/IExternalRoyaltyPolicy.sol";

contract MockExternalRoyaltyPolicy1 is IExternalRoyaltyPolicy {
contract MockExternalRoyaltyPolicy1 is ERC165, IExternalRoyaltyPolicy {
function getPolicyRtsRequiredToLink(address ipId, uint32 licensePercent) external view returns (uint32) {
return licensePercent * 2;
}

/// @notice IERC165 interface support
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
return interfaceId == this.getPolicyRtsRequiredToLink.selector || super.supportsInterface(interfaceId);
}
}
9 changes: 8 additions & 1 deletion test/foundry/mocks/policy/MockExternalRoyaltyPolicy2.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import { IERC165, ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

// solhint-disable-next-line max-line-length
import { IExternalRoyaltyPolicy } from "../../../../contracts/interfaces/modules/royalty/policies/IExternalRoyaltyPolicy.sol";

contract MockExternalRoyaltyPolicy2 is IExternalRoyaltyPolicy {
contract MockExternalRoyaltyPolicy2 is ERC165, IExternalRoyaltyPolicy {
function getPolicyRtsRequiredToLink(address ipId, uint32 licensePercent) external view returns (uint32) {
return 10 * 10 ** 6;
}

/// @notice IERC165 interface support
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
return interfaceId == this.getPolicyRtsRequiredToLink.selector || super.supportsInterface(interfaceId);
}
}
5 changes: 5 additions & 0 deletions test/foundry/modules/royalty/RoyaltyModule.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ contract TestRoyaltyModule is BaseTest {
royaltyModule.registerExternalRoyaltyPolicy(address(royaltyPolicyLAP));
}

function test_RoyaltyModule_registerExternalRoyaltyPolicy_revert_InvalidExternalRoyaltyPolicy() public {
vm.expectRevert(Errors.RoyaltyModule__InvalidExternalRoyaltyPolicy.selector);
royaltyModule.registerExternalRoyaltyPolicy(address(1));
}

function test_RoyaltyModule_registerExternalRoyaltyPolicy() public {
address externalRoyaltyPolicy = address(new MockExternalRoyaltyPolicy1());
assertEq(royaltyModule.isRegisteredExternalRoyaltyPolicy(externalRoyaltyPolicy), false);
Expand Down