diff --git a/contracts/interfaces/licensing/ILicensingFramework.sol b/contracts/interfaces/licensing/ILicensingFramework.sol new file mode 100644 index 000000000..3a7cd9af5 --- /dev/null +++ b/contracts/interfaces/licensing/ILicensingFramework.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; + +import { Licensing } from "contracts/lib/Licensing.sol"; +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; + +interface ILicensingFramework is IParamVerifier { + function licenseRegistry() external view returns (address); + function policyToJson(bytes memory policyData) external view returns (string memory); +} \ No newline at end of file diff --git a/contracts/interfaces/licensing/ILinkParamVerifier.sol b/contracts/interfaces/licensing/ILinkParamVerifier.sol index 16f6f4d6d..af2885651 100644 --- a/contracts/interfaces/licensing/ILinkParamVerifier.sol +++ b/contracts/interfaces/licensing/ILinkParamVerifier.sol @@ -10,6 +10,6 @@ interface ILinkParamVerifier is IParamVerifier { address caller, address ipId, address parentIpId, - bytes calldata data + bytes calldata policyData ) external returns (bool); } \ No newline at end of file diff --git a/contracts/interfaces/licensing/IMintParamVerifier.sol b/contracts/interfaces/licensing/IMintParamVerifier.sol index 137a8de73..9abbf22d3 100644 --- a/contracts/interfaces/licensing/IMintParamVerifier.sol +++ b/contracts/interfaces/licensing/IMintParamVerifier.sol @@ -6,12 +6,10 @@ import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.so interface IMintParamVerifier is IParamVerifier { function verifyMint( address caller, - uint256 policyId, bool policyAddedByLinking, - address licensor, + address licensors, address receiver, uint256 mintAmount, - bytes memory data + bytes memory policyData ) external returns (bool); -} - +} \ No newline at end of file diff --git a/contracts/interfaces/licensing/IParamVerifier.sol b/contracts/interfaces/licensing/IParamVerifier.sol index 7dfe2ca90..0b7081436 100644 --- a/contracts/interfaces/licensing/IParamVerifier.sol +++ b/contracts/interfaces/licensing/IParamVerifier.sol @@ -5,11 +5,5 @@ pragma solidity ^0.8.23; import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; interface IParamVerifier is IERC165 { - function name() external view returns (bytes32); - function nameString() external view returns (string memory); - - function json() external view returns (string memory); - - function allowsOtherPolicyOnSameIp(bytes memory data) external view returns (bool); -} +} \ No newline at end of file diff --git a/contracts/interfaces/registries/ILicenseRegistry.sol b/contracts/interfaces/registries/ILicenseRegistry.sol index fd886ce67..eb8526ce5 100644 --- a/contracts/interfaces/registries/ILicenseRegistry.sol +++ b/contracts/interfaces/registries/ILicenseRegistry.sol @@ -7,7 +7,7 @@ interface ILicenseRegistry { event LicenseFrameworkCreated( address indexed creator, uint256 indexed frameworkId, - Licensing.FrameworkCreationParams frameworkCreationParams + Licensing.Framework framework ); event PolicyCreated(address indexed creator, uint256 indexed policyId, Licensing.Policy policy); @@ -30,14 +30,7 @@ interface ILicenseRegistry { event IpIdLinkedToParent(address indexed caller, address indexed ipId, address indexed parentIpId); - function addLicenseFramework( - Licensing.FrameworkCreationParams calldata fwCreation - ) external returns (uint256 frameworkId); - - function addPolicyToIp( - address ipId, - Licensing.Policy memory pol - ) external returns (uint256 policyId, bool isNew, uint256 indexOnIpId); + function addLicenseFramework(Licensing.Framework calldata fw) external returns (uint256 frameworkId); function addPolicyToIp(address ipId, uint256 polId) external returns (uint256 indexOnIpId); @@ -58,7 +51,7 @@ interface ILicenseRegistry { function totalFrameworks() external view returns (uint256); - function frameworkParam(uint256 frameworkId, string calldata name) external view returns (Licensing.Parameter memory); + function framework(uint256 frameworkId) external view returns (Licensing.Framework memory); function frameworkUrl(uint256 frameworkId) external view returns (string memory); function totalPolicies() external view returns (uint256); @@ -90,4 +83,4 @@ interface ILicenseRegistry { function parentIpIds(address ipId) external view returns (address[] memory); function totalParentsForIpId(address ipId) external view returns (uint256); -} +} \ No newline at end of file diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index b1e1848fa..e39046844 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -74,28 +74,46 @@ library Errors { error LicenseRegistry__PolicyAlreadySetForIpId(); error LicenseRegistry__FrameworkNotFound(); error LicenseRegistry__EmptyLicenseUrl(); + error LicenseRegistry__ZeroLicensingFramework(); error LicenseRegistry__PolicyAlreadyAdded(); error LicenseRegistry__ParamVerifierLengthMismatch(); - error LicenseRegistry__InvalidParamVerifierType(); error LicenseRegistry__PolicyNotFound(); error LicenseRegistry__NotLicensee(); error LicenseRegistry__ParentIdEqualThanChild(); error LicenseRegistry__LicensorDoesntHaveThisPolicy(); - error LicenseRegistry__ParamVerifierFailed(uint8 verifierType, address verifier); + error LicenseRegistry__MintLicenseParamFailed(); error LicenseRegistry__LinkParentParamFailed(); + error LicenseRegistry__TransferParamFailed(); error LicenseRegistry__InvalidLicensor(); error LicenseRegistry__ParamVerifierAlreadySet(); + error LicenseRegistry__CommercialTermInNonCommercialPolicy(); + error LicenseRegistry__EmptyParamName(); + error LicenseRegistry__UnregisteredFrameworkAddingPolicy(); //////////////////////////////////////////////////////////////////////////// - // BaseParamVerifier // + // LicenseRegistryAware // //////////////////////////////////////////////////////////////////////////// - error BaseParamVerifier__Unauthorized(); + error LicenseRegistryAware__CallerNotLicenseRegistry(); + + //////////////////////////////////////////////////////////////////////////// + // LicensingFrameworkUML // + //////////////////////////////////////////////////////////////////////////// + + error LicensingFrameworkUML_CommecialDisabled_CantAddAttribution(); + error LicensingFrameworkUML_CommecialDisabled_CantAddCommercializers(); + error LicensingFrameworkUML_CommecialDisabled_CantAddRevShare(); + error LicensingFrameworkUML_CommecialDisabled_CantAddDerivRevShare(); + error LicensingFrameworkUML_DerivativesDisabled_CantAddAttribution(); + error LicensingFrameworkUML_DerivativesDisabled_CantAddApproval(); + error LicensingFrameworkUML_DerivativesDisabled_CantAddReciprocal(); + error LicensingFrameworkUML_DerivativesDisabled_CantAddRevShare(); + error LicensingFrameworkUML_FrameworkNotYetRegistered(); + //////////////////////////////////////////////////////////////////////////// - // DerivativesParamVerifier // + // LicensorApprovalManager // //////////////////////////////////////////////////////////////////////////// - error DerivativesParamVerifier__InvalidDerivativesConfig(); - error DerivativesParamVerifier__ZeroShare(); + error LicensorApprovalManager__Unauthorized(); //////////////////////////////////////////////////////////////////////////// // Dispute Module // diff --git a/contracts/lib/Licensing.sol b/contracts/lib/Licensing.sol index f80c5443e..3a1712d52 100644 --- a/contracts/lib/Licensing.sol +++ b/contracts/lib/Licensing.sol @@ -6,54 +6,23 @@ import { Errors } from "./Errors.sol"; library Licensing { - - /// Identifies a license parameter (term) from a license framework - struct Parameter { - /// Contract that must check if the condition of the paremeter is set - IParamVerifier verifier; - /// Default value for the parameter, as defined in the license framework text - bytes defaultValue; - } - - /// Moment of the license lifetime where a Parameter will be verified - enum ParamVerifierType { - Mint, - LinkParent, - Transfer - } - /// Describes a licensing framework, which is a set of licensing terms (parameters) /// that come into effect in different moments of the licensing life cycle. /// Must correspond to human (or at least lawyer) readable text describing them in licenseUrl. /// To be valid in Story Protocol, the parameters described in the text must express default values /// corresponding to those of each Parameter struct struct Framework { - /// @notice Stores the parameters that need to be verified in each moment of the licensing lifetime - mapping(bytes32 => Parameter) parameters; + address licensingFramework; /// @notice URL to the file containing the legal text for the license agreement string licenseUrl; } - // Needed because Solidity doesn't support passing nested struct arrays to storage - struct FrameworkCreationParams { - IParamVerifier[] parameters; - bytes[] defaultValues; - string licenseUrl; - } - /// A particular configuration of a Licensing Framework, setting (or not) values for the licensing /// terms (parameters) of the framework. struct Policy { - /// True if the policy accepts commercial terms - bool commercialUse; - /// True if the policy accepts derivative-related terms - bool derivatives; /// Id of a Licensing Framework uint256 frameworkId; - /// Names of the parameters of the framework. Must be the same that IParamVerifier.name() returns - bytes32[] paramNames; - /// Values for the parameters of the framework. Index must correspond to paramNames[] - bytes[] paramValues; + bytes data; } /// Data that define a License Agreement NFT @@ -63,4 +32,4 @@ library Licensing { /// Id for the licensor of the Ip Id address licensorIpId; } -} +} \ No newline at end of file diff --git a/contracts/modules/licensing/BaseLicensingFramework.sol b/contracts/modules/licensing/BaseLicensingFramework.sol new file mode 100644 index 000000000..8013b3942 --- /dev/null +++ b/contracts/modules/licensing/BaseLicensingFramework.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf +pragma solidity ^0.8.23; + +// contracts +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; +import { ILicensingFramework } from "contracts/interfaces/licensing/ILicensingFramework.sol"; +import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; +import { Licensing } from "contracts/lib/Licensing.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { LicenseRegistryAware } from "contracts/modules/licensing/LicenseRegistryAware.sol"; + +// external +import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +abstract contract BaseLicensingFramework is IParamVerifier, ILicensingFramework, ERC165, LicenseRegistryAware { + + string public licenseUrl; + + uint256 public frameworkId; + + /// @notice Initializes the base module contract. + /// @param registry The address of the license registry. + constructor(address registry, string memory templateUrl) LicenseRegistryAware(registry) { + licenseUrl = templateUrl; + } + + function register() external returns(uint256) { + Licensing.Framework memory framework = Licensing.Framework({ + licensingFramework: address(this), + licenseUrl: licenseUrl + }); + frameworkId = LICENSE_REGISTRY.addLicenseFramework(framework); + return frameworkId; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + return interfaceId == type(ILicensingFramework).interfaceId || super.supportsInterface(interfaceId); + } + + function licenseRegistry() virtual override external view returns (address) { + return address(LICENSE_REGISTRY); + } +} \ No newline at end of file diff --git a/contracts/modules/licensing/LicenseRegistryAware.sol b/contracts/modules/licensing/LicenseRegistryAware.sol new file mode 100644 index 000000000..5b231ca97 --- /dev/null +++ b/contracts/modules/licensing/LicenseRegistryAware.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf +pragma solidity ^0.8.23; + +// contracts +import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; +import { Errors } from "contracts/lib/Errors.sol"; + +abstract contract LicenseRegistryAware { + + /// @notice Gets the protocol-wide license registry. + LicenseRegistry public immutable LICENSE_REGISTRY; + + /// @notice Initializes the base module contract. + /// @param licenseRegistry The address of the license registry. + constructor(address licenseRegistry) { + LICENSE_REGISTRY = LicenseRegistry(licenseRegistry); + } + + /// @notice Modifier for authorizing the calling entity. + modifier onlyLicenseRegistry() { + if (msg.sender != address(LICENSE_REGISTRY)) { + revert Errors.LicenseRegistryAware__CallerNotLicenseRegistry(); + } + _; + } + +} \ No newline at end of file diff --git a/contracts/modules/licensing/LicensingFrameworkUML.sol b/contracts/modules/licensing/LicensingFrameworkUML.sol new file mode 100644 index 000000000..181458cd2 --- /dev/null +++ b/contracts/modules/licensing/LicensingFrameworkUML.sol @@ -0,0 +1,169 @@ +// // SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; + +// contracts + +import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; +import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; +import { Licensing } from "contracts/lib/Licensing.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; +import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; +import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; +import { BaseLicensingFramework } from "contracts/modules/licensing/BaseLicensingFramework.sol"; +import { LicensorApprovalManager } from "contracts/modules/licensing/parameter-helpers/LicensorApprovalManager.sol"; + +// external +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; + + +struct UMLv1Policy { + bool attribution; + bool commercialUse; + bool commercialAttribution; + string[] commercializers; + uint256 commercialRevShare; + bool derivativesAllowed; + bool derivativesAttribution; + bool derivativesApproval; + bool derivativesReciprocal; + uint256 derivativesRevShare; + string[] territories; + string[] distributionChannels; +} + +contract LicensingFrameworkUML is + BaseLicensingFramework, + ILinkParamVerifier, + IMintParamVerifier, + ITransferParamVerifier, + LicensorApprovalManager + { + + + event UMLv1PolicyAdded(uint256 indexed policyId, UMLv1Policy policy); + + constructor(address licRegistry, string memory licenseUrl) BaseLicensingFramework(licRegistry, licenseUrl) {} + + function licenseRegistry() view override external returns (address) { + return address(LICENSE_REGISTRY); + } + + function addPolicy(UMLv1Policy calldata umlPolicy) external returns(uint256 policyId) { + if (frameworkId == 0) { + revert Errors.LicensingFrameworkUML_FrameworkNotYetRegistered(); + } + _verifyComercialUse(umlPolicy); + _verifyDerivatives(umlPolicy); + Licensing.Policy memory protocolPolicy = Licensing.Policy({ + frameworkId: frameworkId, + data: abi.encode(umlPolicy) + }); + return LICENSE_REGISTRY.addPolicy(protocolPolicy); + emit UMLv1PolicyAdded(policyId, umlPolicy); + } + + function policyToUmlPolicy(uint256 policyId) public view returns (UMLv1Policy memory policy) { + Licensing.Policy memory protocolPolicy = LICENSE_REGISTRY.policy(policyId); + if(protocolPolicy.frameworkId != frameworkId) { + revert Errors.LicenseRegistry__FrameworkNotFound(); + } + policy = abi.decode(protocolPolicy.data, (UMLv1Policy)); + } + + function policyToJson(bytes memory) public view returns (string memory) { + return "TODO"; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(BaseLicensingFramework, IERC165) returns (bool) { + return super.supportsInterface(interfaceId) || + interfaceId == type(ILinkParamVerifier).interfaceId || + interfaceId == type(IMintParamVerifier).interfaceId || + interfaceId == type(ITransferParamVerifier).interfaceId; + } + + function verifyLink( + uint256 licenseId, + address, + address ipId, + address, + bytes calldata policyData + ) + external override + onlyLicenseRegistry + returns (bool) { + UMLv1Policy memory policy = abi.decode(policyData, (UMLv1Policy)); + bool linkingOK = true; + if (policy.commercialRevShare > 0) { + // RoyaltyModule.setRevShare() + } + if (policy.derivativesRevShare > 0) { + // RoyaltyModule.setRevShareForDerivatives() + } + if (policy.derivativesApproval) { + linkingOK = linkingOK && isDerivativeApproved(licenseId, ipId); + } + return linkingOK; + } + + function verifyMint( + address, + bool policyAddedByLinking, + address, + address, + uint256, + bytes memory policyData + ) external returns (bool) { + UMLv1Policy memory policy = abi.decode(policyData, (UMLv1Policy)); + if (!policy.derivativesAllowed && policyAddedByLinking) { + // Parent said no derivatives, but child is trying to mint + return false; + } + return true; + } + + function verifyTransfer( + uint256, + address, + address, + uint256, + bytes memory + ) external returns (bool) { + return true; + } + + function _verifyComercialUse(UMLv1Policy calldata policy) internal pure { + if (!policy.commercialUse) { + if (policy.commercialAttribution) { + revert Errors.LicensingFrameworkUML_CommecialDisabled_CantAddAttribution(); + } + if (policy.commercializers.length > 0) { + revert Errors.LicensingFrameworkUML_CommecialDisabled_CantAddCommercializers(); + } + if (policy.commercialRevShare > 0) { + revert Errors.LicensingFrameworkUML_CommecialDisabled_CantAddRevShare(); + } + if (policy.derivativesRevShare > 0) { + revert Errors.LicensingFrameworkUML_CommecialDisabled_CantAddDerivRevShare(); + } + } + } + + function _verifyDerivatives(UMLv1Policy calldata policy) internal pure { + if (!policy.derivativesAllowed) { + if (policy.derivativesAttribution) { + revert Errors.LicensingFrameworkUML_DerivativesDisabled_CantAddAttribution(); + } + if (policy.derivativesApproval) { + revert Errors.LicensingFrameworkUML_DerivativesDisabled_CantAddApproval(); + } + if (policy.derivativesReciprocal) { + revert Errors.LicensingFrameworkUML_DerivativesDisabled_CantAddReciprocal(); + } + if (policy.derivativesRevShare > 0) { + revert Errors.LicensingFrameworkUML_DerivativesDisabled_CantAddRevShare(); + } + } + } +} diff --git a/contracts/modules/licensing/parameter-helpers/LicensorApprovalManager.sol b/contracts/modules/licensing/parameter-helpers/LicensorApprovalManager.sol new file mode 100644 index 000000000..00515a86b --- /dev/null +++ b/contracts/modules/licensing/parameter-helpers/LicensorApprovalManager.sol @@ -0,0 +1,33 @@ +// // SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; + +import { Errors } from "contracts/lib/Errors.sol"; +import { LicenseRegistryAware } from "contracts/modules/licensing/LicenseRegistryAware.sol"; + +// NOTE: this could be a standalone contract or part of a licensing module +abstract contract LicensorApprovalManager is LicenseRegistryAware { + + event DerivativeApproved(uint256 indexed licenseId, address indexed ipId, address indexed caller, bool approved); + + // License Id => licensor => childIpId => approved + mapping(uint256 => mapping(address => mapping(address => bool))) private _approvals; + + // TODO: meta tx version? + function setApproval(uint256 licenseId, address childIpId, bool approved) external { + address licensorIpId = LICENSE_REGISTRY.licensorIpId(licenseId); + // TODO: ACL + bool callerIsLicensor = true; // msg.sender == IPAccountRegistry(licensorIpId).owner() or IP Account itself; + if (!callerIsLicensor) { + revert Errors.LicensorApprovalManager__Unauthorized(); + } + _approvals[licenseId][licensorIpId][childIpId] = approved; + emit DerivativeApproved(licenseId, licensorIpId, msg.sender, approved); + } + + function isDerivativeApproved(uint256 licenseId, address childIpId) public view returns (bool) { + address licensorIpId = LICENSE_REGISTRY.licensorIpId(licenseId); + return _approvals[licenseId][licensorIpId][childIpId]; + } + +} diff --git a/contracts/modules/licensing/parameters/BaseParamVerifier.sol b/contracts/modules/licensing/parameters/BaseParamVerifier.sol deleted file mode 100644 index b62ed7fdf..000000000 --- a/contracts/modules/licensing/parameters/BaseParamVerifier.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf -pragma solidity ^0.8.23; - -// contracts -import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; -import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; -import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; -import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; -import { Errors } from "contracts/lib/Errors.sol"; -import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; -import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; - -abstract contract BaseParamVerifier is IParamVerifier { - // /// @notice Gets the protocol-wide module access controller. - // IAccessController public immutable ACCESS_CONTROLLER; - - // /// @notice Gets the protocol-wide IP account registry. - // IPAccountRegistry public immutable IP_ACCOUNT_REGISTRY; - - // /// @notice Gets the protocol-wide IP record registry. - // IPRecordRegistry public immutable IP_RECORD_REGISTRY; - - /// @notice Gets the protocol-wide license registry. - LicenseRegistry public immutable LICENSE_REGISTRY; - - string internal NAME; - - /// @notice Initializes the base module contract. - /// @param licenseRegistry The address of the license registry. - constructor(address licenseRegistry, string memory name_) { - LICENSE_REGISTRY = LicenseRegistry(licenseRegistry); - NAME = name_; - } - - /// @notice Modifier for authorizing the calling entity. - modifier onlyLicenseRegistry() { - if (msg.sender != address(LICENSE_REGISTRY)) { - revert Errors.BaseParamVerifier__Unauthorized(); - } - _; - } - - function name() external view override returns (bytes32) { - return ShortStringOps.stringToBytes32(NAME); - } - - function nameString() external view override returns (string memory) { - return NAME; - } - - // TODO: implement flexible json() - function json() external view returns (string memory) { - return ""; - } -} diff --git a/contracts/modules/licensing/parameters/DerivativesParamVerifier.sol b/contracts/modules/licensing/parameters/DerivativesParamVerifier.sol deleted file mode 100644 index 7da519561..000000000 --- a/contracts/modules/licensing/parameters/DerivativesParamVerifier.sol +++ /dev/null @@ -1,168 +0,0 @@ -// // SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.8.23; - -// import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; -// import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; -// import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; -// import { BaseParamVerifier } from "contracts/modules/licensing/parameters/BaseParamVerifier.sol"; -// import { Errors } from "contracts/lib/Errors.sol"; - -// /// This corresponds with 1 term in UML text, but it's actually 5 interconnected parameters (hooks if you will) -// /* -// | Parameter | On chain | Can be enabled if | Can be enabled if | Incompatibility with other policy | Side effect | | -// |----------------------------|-----------------------|--------------------|-------------------|------------------------------------------|--------------------------------------|---| -// | Derivatives | yes | - | - | - | - | | -// | Deriv With Attribution | Yes (verify offchain) | Derivatives = true | - | - | - | | -// | Deriv With Approval | yes | Derivatives = true | - | - | – | | -// | Deriv With Reciprocal | yes | Derivatives = true | - | Disallow different policies on same IpId | Address other than licensor can mint | | -// | Deriv With Revenue Share | yes | Derivatives = true | Commercial = true | - | Royalties set on linking | | -// | Deriv With Revenue Ceiling | no | Derivatives = true | Commercial = true | - | - | | - - - -// */ -// contract DerivativesParamVerifier is BaseParamVerifier, IMintParamVerifier, ILinkParamVerifier { - -// event DerivativeApproved(address indexed licenseId, address indexed ipId, address indexed licensor, bool licensorApproval, bool approved); - -// struct DerivativesConfig { -// // Derivatives -// bool derivativesAllowed; -// // Allowed-With-Attribution -// bool withAttribution; -// // Allowed-With-Approval -> cannot link to parent if ipId not approved by licensors -// bool withApproval; -// bool withReciprocal; // Allowed-With-Reciprocal -// // Allowed-With-Revenue-Share -// // Can only be tagged if commercial use is allowed -// bool withRevenueShare; -> link -// uint256 revenueSharePercentage; -// // TODO: Allowed-With-Revenue-Ceiling -// } - -// // License Id => IP Id => licensor => approved -// mapping(uint256 => mapping(address => mapping(address => bool))) private _approvals; -// // License Id => IP Id => licensor => total approvals -// mapping(uint256 => mapping(address => uint256)) private _totalLicensorApprovals; - -// constructor(address licenseRegistry) BaseParamVerifier(licenseRegistry) { - -// } - -// function encodeConfig(DerivativesConfig memory config) external pure returns (bytes memory) { -// if (!config.derivativesAllowed -// && -// (config.withAttribution || config.withApproval || config.withReciprocal || config.withRevenueShare) -// ) { -// revert Errors.DerivativesParamVerifier__InvalidDerivativesConfig(); -// } -// // TODO: check if commercial use is allowed? How do yo we enforce this on verification? we need structure -// if (config.withRevenueShare && config.revenueSharePercentage == 0) { -// revert Errors.DerivativesParamVerifier__ZeroShare(); -// } -// return abi.encode(config); -// } - -// function decodeConfig(bytes memory data) external pure returns (DerivativesConfig memory) { -// return abi.decode(data, (DerivativesConfig)); -// } - -// function setApproval(address licenseId, address ipId, bool approved) external { -// address[] memory licensors = LICENSE_REGISTRY.getLicensors(licenseId); -// uint256 totalLicensors = licensors.length; -// bool callerIsLicensor = false; -// address caller = msg.sender; -// for (uint256 i = 0; i < totalLicensors; i++) { -// if (caller == licensors[i]) { -// // TODO: check delegation too? -// callerIsLicensor = true; -// _approvals[licenseId][ipId][licensors[i]] = approved; -// uint256 totalApprovals = _totalLicensorApprovals[licenseId][ipId]; -// if (approved) { -// totalApprovals += 1; -// } else { -// // If this reverts it's fine, revoking before any approval exists -// // doesn't make sense -// totalApprovals -= 1; -// } -// _totalLicensorApprovals[licenseId][ipId] = totalApprovals; -// emit DerivativeApproved(licenseId, ipId, licensors[i], approved, totalApprovals == totalLicensors); -// } -// } -// if (!callerIsLicensor) { -// revert Errors.DerivativesParamVerifier__Unauthorized(); -// } -// } - -// function isDerivativeApproved(address licenseId, address ipId) public view returns (bool) { -// return _totalLicensorApprovals[licenseId][ipId] == LICENSE_REGISTRY.getLicensors(licenseId).length; -// } - -// function verifyMint( -// address caller, -// uint256 policyId, -// bool policyAddedByLinking, -// address[] memory licensors, -// address receiver, -// uint256 amount, -// bytes memory data -// ) external view returns (bool) { -// DerivativesConfig memory config = abi.decode(data, (DerivativesConfig)); -// if (config.derivativesAllowed && config.withReciprocal) { -// // Permissionless -// return true; -// } -// if (policyAddedByLinking) { -// // Minting license for derivative ip -// if (!config.derivativesAllowed) { -// // If IP Licensor has not tagged “Derivatives”, then you are not allowed to create Derivative IP Assets. -// return false; -// } -// } -// // TODO: check delegation too -// uint256 totalLicensors = licensors.length; -// for (uint256 i = 0; i < totalLicensors; i++) { -// if (caller == licensors[i]) { -// // Caller is a licensor -// return true; -// } -// } -// // Caller is not a licensor -// return false; -// } - -// function verifyLink( -// address licenseId, -// address licenseHolder, -// address ipId, -// address parentIpId, -// bytes calldata data -// ) external view override returns (bool) { -// DerivativesConfig memory config = abi.decode(data, (DerivativesConfig)); -// if (config.withRevenueShare) { -// // TODO: setRoyaltyPolicy(ipId, parentIpId, config.revenueSharePercentage) -// // What happens if called twice? -// } -// if (config.withApproval) { -// return isDerivativeApproved(licenseId, ipId); -// } -// return true; -// } - -// function json() external pure override(IParamVerifier, BaseParamVerifier) returns (string memory) { -// return ""; -// } - -// function name() external pure override(IParamVerifier, BaseParamVerifier) returns (string memory) { -// return "Derivatives"; -// } - -// function allowsOtherPolicyOnSameIp(bytes memory data) external pure override(IParamVerifier, BaseParamVerifier) returns (bool) { -// DerivativesConfig memory config = abi.decode(data, (DerivativesConfig)); -// return !config.withReciprocal; -// } -// } - - - diff --git a/contracts/registries/LicenseRegistry.sol b/contracts/registries/LicenseRegistry.sol index 870a6a819..5a2c4117d 100644 --- a/contracts/registries/LicenseRegistry.sol +++ b/contracts/registries/LicenseRegistry.sol @@ -9,6 +9,7 @@ import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortSt import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; // contracts +import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; @@ -16,6 +17,8 @@ import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransfer import { ILicenseRegistry } from "contracts/interfaces/registries/ILicenseRegistry.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; +import { ILicensingFramework } from "contracts/interfaces/licensing/ILicensingFramework.sol"; + // TODO: consider disabling operators/approvals on creation contract LicenseRegistry is ERC1155, ILicenseRegistry { @@ -61,37 +64,26 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { _; } - constructor(string memory uri) ERC1155(uri) {} + constructor() ERC1155("") {} /// Adds a license framework to Story Protocol. /// Must be called by protocol admin - /// @param fwCreation parameters + /// @param fwCreation framework parameters /// @return frameworkId identifier for framework, starting in 1 function addLicenseFramework( - Licensing.FrameworkCreationParams calldata fwCreation + Licensing.Framework calldata fwCreation ) external returns (uint256 frameworkId) { - // check protocol auth + // TODO: check protocol auth if (bytes(fwCreation.licenseUrl).length == 0 || fwCreation.licenseUrl.equal("")) { revert Errors.LicenseRegistry__EmptyLicenseUrl(); } + if (fwCreation.licensingFramework == address(0)) { + revert Errors.LicenseRegistry__ZeroLicensingFramework(); + } // Todo: check duplications ++_totalFrameworks; - Licensing.Framework storage fw = _frameworks[_totalFrameworks]; - fw.licenseUrl = fwCreation.licenseUrl; - uint256 paramLength = fwCreation.parameters.length; - if (paramLength != fwCreation.defaultValues.length) { - revert Errors.LicenseRegistry__ParamVerifierLengthMismatch(); - } - mapping(bytes32 => Licensing.Parameter) storage params = fw.parameters; - for (uint256 i = 0; i < paramLength; i++) { - IParamVerifier verifier = fwCreation.parameters[i]; - bytes32 paramName = verifier.name(); - if (address(params[paramName].verifier) != address(0)) { - revert Errors.LicenseRegistry__ParamVerifierAlreadySet(); - } - params[paramName] = Licensing.Parameter({ verifier: verifier, defaultValue: fwCreation.defaultValues[i] }); - } + _frameworks[_totalFrameworks] = fwCreation; emit LicenseFrameworkCreated(msg.sender, _totalFrameworks, fwCreation); return _totalFrameworks; @@ -102,23 +94,18 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { return _totalFrameworks; } - /// Returns framework for id. Reverts if not found - function frameworkParam( - uint256 frameworkId, - string calldata name - ) public view returns (Licensing.Parameter memory) { - Licensing.Framework storage fw = _framework(frameworkId); - return fw.parameters[ShortString.unwrap(name.toShortString())]; - } - function _framework(uint256 frameworkId) internal view returns (Licensing.Framework storage fw) { fw = _frameworks[frameworkId]; - if (bytes(fw.licenseUrl).length == 0) { + if (fw.licensingFramework == address(0)) { revert Errors.LicenseRegistry__FrameworkNotFound(); } return fw; } + function framework(uint256 frameworkId) external view returns (Licensing.Framework memory) { + return _framework(frameworkId); + } + function frameworkUrl(uint256 frameworkId) external view returns (string memory) { return _framework(frameworkId).licenseUrl; } @@ -148,24 +135,6 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { return (id, true); } - /// Adds a policy to an ipId, which can be used to mint licenses. - /// Licenses are permissions for ipIds to be derivatives (children). - /// If an exact policy already existed, it will reuse the id. - /// Will revert if ipId already has the same policy - /// @param ipId to receive the policy - /// @param pol policy data - /// @return policyId if policy data was in the contract, policyId is reused, if it's new, id will be new. - /// @return isNew true if policy data was not in the contract, false if it was already stored - /// @return indexOnIpId position of policy within the ipIds policy set - function addPolicyToIp( - address ipId, - Licensing.Policy memory pol - ) public returns (uint256 policyId, bool isNew, uint256 indexOnIpId) { - // check protocol auth - (uint256 polId, bool newPolicy) = _addPolicy(pol); - return (polId, newPolicy, _addPolictyIdToIp(ipId, polId, false)); - } - /// Adds a policy to an ipId, which can be used to mint licenses. /// Licnses are permissions for ipIds to be derivatives (children). /// if policyId is not defined in LicenseRegistry, reverts. @@ -180,24 +149,25 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { return _addPolictyIdToIp(ipId, polId, false); } + /// Adds a particular configuration of license terms to the protocol. + /// Must be called by a LicensingFramework, which is responsible for verifying the parameters + /// are valid and the configuration makes sense. + /// @param pol policy data + /// @return policyId if policy data was in the contract, policyId is reused, if it's new, id will be new. function addPolicy(Licensing.Policy memory pol) public returns (uint256 policyId) { - (uint256 polId, bool newPol) = _addPolicy(pol); - if (!newPol) { - revert Errors.LicenseRegistry__PolicyAlreadyAdded(); + address licensingFramework = _framework(pol.frameworkId).licensingFramework; + if (msg.sender != licensingFramework) { + revert Errors.LicenseRegistry__UnregisteredFrameworkAddingPolicy(); } - return polId; - } - - function _addPolicy(Licensing.Policy memory pol) public returns (uint256 policyId, bool isNew) { - // We ignore the return value, we just want to check if the framework exists - _framework(pol.frameworkId); (uint256 polId, bool newPol) = _addIdOrGetExisting(abi.encode(pol), _hashedPolicies, _totalPolicies); - if (newPol) { + if (!newPol) { + revert Errors.LicenseRegistry__PolicyAlreadyAdded(); + } else { _totalPolicies = polId; _policies[polId] = pol; emit PolicyCreated(msg.sender, polId, pol); } - return (polId, newPol); + return polId; } /// Adds a policy id to the ipId policy set @@ -211,6 +181,8 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { if (!_pols.add(policyId)) { revert Errors.LicenseRegistry__PolicyAlreadySetForIpId(); } + // TODO: check for policy compatibility. + // compatibilityManager.isPolicyCompatible(newPolicy, policiesInIpId); index = _pols.length() - 1; PolicySetup storage setup = _policySetups[ipId][policyId]; // This should not happen, but just in case @@ -306,29 +278,20 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { Licensing.Policy memory pol = policy(policyId); Licensing.Framework storage fw = _framework(pol.frameworkId); bool setByLinking = _policySetups[licensorIp][policyId].setByLinking; - for (uint256 i = 0; i < pol.paramNames.length; i++) { - Licensing.Parameter memory param = fw.parameters[pol.paramNames[i]]; - - if (ERC165Checker.supportsInterface(address(param.verifier), type(IMintParamVerifier).interfaceId)) { - bool verificationOk = IMintParamVerifier(address(param.verifier)).verifyMint( - msg.sender, - policyId, - setByLinking, - licensorIp, - receiver, - amount, - pol.paramValues[i].length == 0 ? param.defaultValue : pol.paramValues[i] - ); - - if (!verificationOk) { - revert Errors.LicenseRegistry__ParamVerifierFailed( - uint8(Licensing.ParamVerifierType.Mint), - address(param.verifier) - ); - } + + if (ERC165Checker.supportsInterface(fw.licensingFramework, type(IMintParamVerifier).interfaceId)) { + if(!IMintParamVerifier(fw.licensingFramework).verifyMint( + msg.sender, + setByLinking, + licensorIp, + receiver, + amount, + pol.data + )) { + revert Errors.LicenseRegistry__MintLicenseParamFailed(); } } - + Licensing.License memory licenseData = Licensing.License({ policyId: policyId, licensorIpId: licensorIp }); bool isNew; (licenseId, isNew) = _addIdOrGetExisting(abi.encode(licenseData), _hashedLicenses, _totalLicenses); @@ -380,27 +343,17 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { Licensing.Policy memory pol = policy(licenseData.policyId); Licensing.Framework storage fw = _framework(pol.frameworkId); - for (uint256 i = 0; i < pol.paramNames.length; i++) { - Licensing.Parameter memory param = fw.parameters[pol.paramNames[i]]; - - if (ERC165Checker.supportsInterface(address(param.verifier), type(ILinkParamVerifier).interfaceId)) { - bool verificationOk = ILinkParamVerifier(address(param.verifier)).verifyLink( - licenseId, - msg.sender, - childIpId, - parentIpId, - pol.paramValues[i].length == 0 ? param.defaultValue : pol.paramValues[i] - ); - - if (!verificationOk) { - revert Errors.LicenseRegistry__ParamVerifierFailed( - uint8(uint8(Licensing.ParamVerifierType.LinkParent)), - address(param.verifier) - ); - } + if (ERC165Checker.supportsInterface(fw.licensingFramework, type(ILinkParamVerifier).interfaceId)) { + if(!ILinkParamVerifier(fw.licensingFramework).verifyLink( + licenseId, + msg.sender, + childIpId, + parentIpId, + pol.data + )) { + revert Errors.LicenseRegistry__LinkParentParamFailed(); } } - // Add policy to kid _addPolictyIdToIp(childIpId, licenseData.policyId, true); // Set parent @@ -445,29 +398,21 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { // Verify transfer params Licensing.Policy memory pol = policy(_licenses[ids[i]].policyId); Licensing.Framework storage fw = _framework(pol.frameworkId); - for (uint256 j = 0; j < pol.paramNames.length; j++) { - Licensing.Parameter memory param = fw.parameters[pol.paramNames[j]]; - bytes memory paramValue = pol.paramValues[j]; - if ( - ERC165Checker.supportsInterface( - address(param.verifier), - type(ITransferParamVerifier).interfaceId - ) - ) { - bool verificationOk = ITransferParamVerifier(address(param.verifier)).verifyTransfer( - ids[i], - from, - to, - values[i], - paramValue.length == 0 ? param.defaultValue : paramValue - ); - - if (!verificationOk) { - revert Errors.LicenseRegistry__ParamVerifierFailed( - uint8(Licensing.ParamVerifierType.Transfer), - address(param.verifier) - ); - } + + if ( + ERC165Checker.supportsInterface( + fw.licensingFramework, + type(ITransferParamVerifier).interfaceId + ) + ) { + if(!ITransferParamVerifier(fw.licensingFramework).verifyTransfer( + ids[i], + from, + to, + values[i], + pol.data + )) { + revert Errors.LicenseRegistry__TransferParamFailed(); } } } @@ -475,5 +420,10 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { super._update(from, to, ids, values); } - // TODO: tokenUri from parameters, from a metadata resolver contract -} + function uri(uint256 id) public view virtual override returns (string memory) { + Licensing.License memory licenseData = _licenses[id]; + Licensing.Policy memory pol = policy(licenseData.policyId); + Licensing.Framework storage fw = _framework(pol.frameworkId); + return ILicensingFramework(fw.licensingFramework).policyToJson(pol.data); + } +} \ No newline at end of file diff --git a/contracts/utils/ShortStringOps.sol b/contracts/utils/ShortStringOps.sol index e29c5a30b..ebf7f01c7 100644 --- a/contracts/utils/ShortStringOps.sol +++ b/contracts/utils/ShortStringOps.sol @@ -41,4 +41,8 @@ library ShortStringOps { function stringToBytes32(string memory s) internal pure returns (bytes32) { return ShortString.unwrap(s.toShortString()); } -} + + function bytes32ToString(bytes32 b) internal pure returns (string memory) { + return ShortString.wrap(b).toString(); + } +} \ No newline at end of file diff --git a/test/foundry/mocks/licensing/MintPaymentVerifier.sol b/test/foundry/mocks/licensing/MintPaymentLicensingFramework.sol similarity index 64% rename from test/foundry/mocks/licensing/MintPaymentVerifier.sol rename to test/foundry/mocks/licensing/MintPaymentLicensingFramework.sol index 2eab59d8e..bd9991e56 100644 --- a/test/foundry/mocks/licensing/MintPaymentVerifier.sol +++ b/test/foundry/mocks/licensing/MintPaymentLicensingFramework.sol @@ -4,31 +4,34 @@ pragma solidity ^0.8.23; // external import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ERC165, IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; + // contracts -import { BaseParamVerifier } from "contracts/modules/licensing/parameters/BaseParamVerifier.sol"; +import { BaseLicensingFramework } from "contracts/modules/licensing/BaseLicensingFramework.sol"; import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVerifier.sol"; import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; -contract MintPaymentVerifier is BaseParamVerifier, ERC165, IMintParamVerifier { - using ShortStrings for *; +contract MintPaymentLicensingFramework is BaseLicensingFramework, IMintParamVerifier { IERC20 public token; uint256 public payment; constructor( address licenseRegistry, + string memory licenseUrl, address _token, uint256 _payment - ) BaseParamVerifier(licenseRegistry, "MintPaymentVerifier") { + ) BaseLicensingFramework(licenseRegistry, licenseUrl) { token = IERC20(_token); payment = _payment; } - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + function supportsInterface(bytes4 interfaceId) + public view virtual + override(IERC165, BaseLicensingFramework) + returns (bool) { return interfaceId == type(IParamVerifier).interfaceId || interfaceId == type(IMintParamVerifier).interfaceId || @@ -39,12 +42,11 @@ contract MintPaymentVerifier is BaseParamVerifier, ERC165, IMintParamVerifier { /// to return true, pass in abi.encode(true) as the value. function verifyMint( address caller, - uint256 policyId, bool policyAddedByLinking, address licensors, address receiver, uint256 mintAmount, - bytes memory data + bytes memory policyData ) external returns (bool) { // TODO: return false on approval or transfer failure uint256 payment_ = mintAmount * payment; @@ -53,19 +55,8 @@ contract MintPaymentVerifier is BaseParamVerifier, ERC165, IMintParamVerifier { return true; } - function verifyTransfer(address, uint256, bytes memory) external pure returns (bool) { - return true; - } - - function verifyLinkParent(address, bytes memory) external pure returns (bool) { - return true; - } - - function verifyActivation(address, bytes memory) external pure returns (bool) { - return true; + function policyToJson(bytes memory policyData) public view returns (string memory) { + return "MintPaymentLicensingFramework"; } - function allowsOtherPolicyOnSameIp(bytes memory data) external pure returns (bool) { - return true; - } -} +} \ No newline at end of file diff --git a/test/foundry/mocks/licensing/MockParamVerifier.sol b/test/foundry/mocks/licensing/MockLicensingFramework.sol similarity index 60% rename from test/foundry/mocks/licensing/MockParamVerifier.sol rename to test/foundry/mocks/licensing/MockLicensingFramework.sol index 08140ef35..1d4eb6397 100644 --- a/test/foundry/mocks/licensing/MockParamVerifier.sol +++ b/test/foundry/mocks/licensing/MockLicensingFramework.sol @@ -8,30 +8,37 @@ import { ILinkParamVerifier } from "contracts/interfaces/licensing/ILinkParamVer import { IMintParamVerifier } from "contracts/interfaces/licensing/IMintParamVerifier.sol"; import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; -import { BaseParamVerifier } from "contracts/modules/licensing/parameters/BaseParamVerifier.sol"; +import { BaseLicensingFramework } from "contracts/modules/licensing/BaseLicensingFramework.sol"; +import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; -struct MockParamVerifierConfig { +struct MockLicensingFrameworkConfig { address licenseRegistry; - string name; + string licenseUrl; bool supportVerifyLink; bool supportVerifyMint; bool supportVerifyTransfer; } -contract MockParamVerifier is - ERC165, - BaseParamVerifier, +struct MockPolicy { + bool returnVerifyLink; + bool returnVerifyMint; + bool returnVerifyTransfer; +} + +contract MockLicensingFramework is + BaseLicensingFramework, ILinkParamVerifier, IMintParamVerifier, ITransferParamVerifier { - MockParamVerifierConfig internal config; + MockLicensingFrameworkConfig internal config; - constructor(MockParamVerifierConfig memory conf) BaseParamVerifier(conf.licenseRegistry, conf.name) { + constructor(MockLicensingFrameworkConfig memory conf) + BaseLicensingFramework(conf.licenseRegistry, conf.licenseUrl) { config = conf; } - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, BaseLicensingFramework) returns (bool) { if (interfaceId == type(IParamVerifier).interfaceId) return true; if (interfaceId == type(ILinkParamVerifier).interfaceId) return config.supportVerifyLink; if (interfaceId == type(IMintParamVerifier).interfaceId) return config.supportVerifyMint; @@ -39,24 +46,21 @@ contract MockParamVerifier is return super.supportsInterface(interfaceId); } - function allowsOtherPolicyOnSameIp(bytes memory data) external pure override returns (bool) { - return true; - } - function verifyMint( address, - uint256, bool, address, address, uint256, bytes memory data - ) external pure returns (bool) { - return abi.decode(data, (bool)); + ) external view returns (bool) { + MockPolicy memory policy = abi.decode(data, (MockPolicy)); + return policy.returnVerifyMint; } function verifyLink(uint256, address, address, address, bytes calldata data) external pure override returns (bool) { - return abi.decode(data, (bool)); + MockPolicy memory policy = abi.decode(data, (MockPolicy)); + return policy.returnVerifyLink; } function verifyTransfer( @@ -66,6 +70,12 @@ contract MockParamVerifier is uint256, bytes memory data ) external pure override returns (bool) { - return abi.decode(data, (bool)); + MockPolicy memory policy = abi.decode(data, (MockPolicy)); + return policy.returnVerifyTransfer; } -} + + function policyToJson(bytes memory policyData) public pure returns (string memory) { + return "MockLicensingFramework"; + } + +} \ No newline at end of file diff --git a/test/foundry/modules/ModuleBase.t.sol b/test/foundry/modules/ModuleBase.t.sol index 81386b9bd..dc40e59e8 100644 --- a/test/foundry/modules/ModuleBase.t.sol +++ b/test/foundry/modules/ModuleBase.t.sol @@ -49,7 +49,7 @@ abstract contract ModuleBaseTest is BaseTest { function setUp() public virtual override(BaseTest) { BaseTest.setUp(); governance = new Governance(address(this)); - licenseRegistry = new LicenseRegistry(""); + licenseRegistry = new LicenseRegistry(); accessController = new AccessController(address(governance)); moduleRegistry = new ModuleRegistry(address(governance)); ipAccountRegistry = new IPAccountRegistry( diff --git a/test/foundry/modules/RegistrationModule.t.sol b/test/foundry/modules/RegistrationModule.t.sol index 9b1ac7533..7bcce7ab5 100644 --- a/test/foundry/modules/RegistrationModule.t.sol +++ b/test/foundry/modules/RegistrationModule.t.sol @@ -21,7 +21,8 @@ import { IIPRecordRegistry } from "contracts/interfaces/registries/IIPRecordRegi import { IPAccountImpl} from "contracts/IPAccountImpl.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; -import { MockParamVerifier, MockParamVerifierConfig } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; +import { MockLicensingFramework, MockLicensingFrameworkConfig, MockPolicy } + from "test/foundry/mocks/licensing/MockLicensingFramework.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; import { IP } from "contracts/lib/IP.sol"; import { Errors } from "contracts/lib/Errors.sol"; @@ -208,40 +209,31 @@ contract RegistrationModuleTest is ModuleBaseTest { return "REGISTRATION_MODULE"; } - // TODO: put this in the base test function _initLicensing() private { - IParamVerifier[] memory mintingVerifiers = new IParamVerifier[](1); - MockParamVerifier verifier = new MockParamVerifier(MockParamVerifierConfig({ - licenseRegistry: address(licenseRegistry), - name: "MockParamVerifier", - supportVerifyLink: true, - supportVerifyMint: true, - supportVerifyTransfer: true - })); - mintingVerifiers[0] = verifier; - bytes[] memory mintingDefaultValues = new bytes[](1); - mintingDefaultValues[0] = abi.encode(true); - - IParamVerifier[] memory parameters; - bytes[] memory defaultValues; + MockLicensingFramework licensingFramework = new MockLicensingFramework( + MockLicensingFrameworkConfig({ + licenseRegistry: address(licenseRegistry), + licenseUrl: "https://example.com", + supportVerifyLink: true, + supportVerifyMint: true, + supportVerifyTransfer: true + }) + ); + + licensingFramework.register(); - Licensing.FrameworkCreationParams memory fwParams = Licensing.FrameworkCreationParams({ - parameters: mintingVerifiers, - defaultValues: mintingDefaultValues, - licenseUrl: "https://example.com" - }); - licenseRegistry.addLicenseFramework(fwParams); Licensing.Policy memory policy = Licensing.Policy({ frameworkId: 1, - commercialUse: true, - derivatives: true, - paramNames: new bytes32[](1), - paramValues: new bytes[](1) + data: abi.encode( + MockPolicy({ + returnVerifyLink: true, + returnVerifyMint: true, + returnVerifyTransfer: true + }) + ) }); - policy.paramNames[0] = verifier.name(); - policy.paramValues[0] = abi.encode(true); + vm.prank(address(licensingFramework)); (uint256 polId) = licenseRegistry.addPolicy(policy); - policyId = polId; } diff --git a/test/foundry/modules/licensing/LicensingFrameworkUML.t.sol b/test/foundry/modules/licensing/LicensingFrameworkUML.t.sol new file mode 100644 index 000000000..4d4abf150 --- /dev/null +++ b/test/foundry/modules/licensing/LicensingFrameworkUML.t.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import { Test } from "forge-std/Test.sol"; +import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; +import { Licensing } from "contracts/lib/Licensing.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { Errors } from "contracts/lib/Errors.sol"; + +import { LicensingFrameworkUML, UMLv1Policy } from "contracts/modules/licensing/LicensingFrameworkUML.sol"; + +import "forge-std/console2.sol"; + +contract LicensingFrameworkUMLTest is Test { + + LicenseRegistry public registry; + Licensing.Framework public framework; + + LicensingFrameworkUML public umlFramework; + uint256 public frameworkId; + + string public licenseUrl = "https://example.com/license"; + address public ipId1 = address(0x111); + address public ipId2 = address(0x222); + address public licenseHolder = address(0x101); + string[] public emptyStringArray = new string[](0); + + function setUp() public { + registry = new LicenseRegistry(); + umlFramework = new LicensingFrameworkUML(address(registry), licenseUrl); + frameworkId = umlFramework.register(); + } + + function test_ParamVerifier_commercialUse_disallowed_revert_settingIncompatibleTerms() public { + // TODO + } + + function test_ParamVerifier_commercialUse_setAllowedCommercializers() public { + // TODO + } + + function test_ParamVerifier_commercialUse_setAllowedWithAttribution() public { + // TODO + } + + function test_ParamVerifier_commercialUse_revenueShareSetOnLinking() public { + // TODO + } + + function test_ParamVerifier_derivatives_notAllowed_revert_creating2ndDerivative() public { + // TODO + } + + function test_ParamVerifier_derivatives_notAllowed_revert_settingIncompatibleTerms() public { + // TODO + } + + function test_ParamVerifier_derivatives_setAllowedWithAttribution() public { + // TODO + } + + function test_LicensingFrameworkUML_derivatives_setReciprocal() public { + // TODO + } + + function test_LicensingFrameworkUML_derivatives_setRevenueShareValue() public { + // TODO + } + + function test_LicensingFrameworkUML_derivatives_setRevenueShareWhenLinking2ndDerivative() public { + // TODO + } + + function test_LicensingFrameworkUML_derivativesWithApproval_revert_linkNotApproved() public { + uint256 policyId = umlFramework.addPolicy(UMLv1Policy({ + attribution: false, + commercialUse: false, + commercialAttribution: false, + commercializers: emptyStringArray, + commercialRevShare: 0, + derivativesAllowed: true, + derivativesAttribution: false, + derivativesApproval: true, + derivativesReciprocal: false, + derivativesRevShare: 0, + territories: emptyStringArray, + distributionChannels: emptyStringArray + })); + console2.log("policyId", policyId); + registry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); + assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); + umlFramework.setApproval(licenseId, ipId2, false); + assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); + + vm.expectRevert(Errors.LicenseRegistry__LinkParentParamFailed.selector); + registry.linkIpToParent(licenseId, ipId2, licenseHolder); + } + + function test_LicensingFrameworkUML_derivatives_withApproval_linkApprovedIpId() public { + uint256 policyId = umlFramework.addPolicy(UMLv1Policy({ + attribution: false, + commercialUse: false, + commercialAttribution: false, + commercializers: emptyStringArray, + commercialRevShare: 0, + derivativesAllowed: true, + derivativesAttribution: false, + derivativesApproval: true, + derivativesReciprocal: false, + derivativesRevShare: 0, + territories: emptyStringArray, + distributionChannels: emptyStringArray + })); + registry.addPolicyToIp(ipId1, policyId); + uint256 licenseId = registry.mintLicense(policyId, ipId1, 1, licenseHolder); + + assertFalse(umlFramework.isDerivativeApproved(licenseId, ipId2)); + umlFramework.setApproval(licenseId, ipId2, true); + assertTrue(umlFramework.isDerivativeApproved(licenseId, ipId2)); + + registry.linkIpToParent(licenseId, ipId2, licenseHolder); + assertTrue(registry.isParent(ipId1, ipId2)); + } + + function test_LicensingFrameworkUML_derivatives_withApproval_revert_approverNotLicensor() public { + // TODO + } + + function test_LicensingFrameworkUML_setTerritory() public { + // TODO + } + + function test_LicensingFrameworkUML_setChannelsOfDistribution() public { + // TODO + } + + function test_LicensingFrameworkUML_setAttributionInReproduction() public { + // TODO + } + + function test_LicensingFrameworkUML_setContentStandards() public { + // TODO + } + + function test_LicensingFrameworkUML_transferrable() public { + // TODO + } + + function test_LicensingFrameworkUML_nonTransferrable_revertIfTransferExceptFromLicensor() public { + // TODO + } + + function test_LicensingFrameworkUML_mintFee() public { + // TODO + } + + function test_tokenUri() public { + // TODO + } + + +} \ No newline at end of file diff --git a/test/foundry/LicenseRegistry.t.sol b/test/foundry/registries/LicenseRegistry.t.sol similarity index 59% rename from test/foundry/LicenseRegistry.t.sol rename to test/foundry/registries/LicenseRegistry.t.sol index 206c8760a..d8c015e6d 100644 --- a/test/foundry/LicenseRegistry.t.sol +++ b/test/foundry/registries/LicenseRegistry.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.13; import { Test } from "forge-std/Test.sol"; import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; -import { MockParamVerifier, MockParamVerifierConfig } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; -import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; +import { MockLicensingFramework, MockLicensingFrameworkConfig, MockPolicy } + from "test/foundry/mocks/licensing/MockLicensingFramework.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; @@ -18,9 +18,7 @@ contract LicenseRegistryTest is Test { LicenseRegistry public registry; Licensing.Framework public framework; - MockParamVerifier public verifier1; - MockParamVerifier public verifier2; - Licensing.FrameworkCreationParams public fwParams; + MockLicensingFramework public module1; string public licenseUrl = "https://example.com/license"; address public ipId1 = address(0x111); @@ -28,96 +26,77 @@ contract LicenseRegistryTest is Test { address public licenseHolder = address(0x101); function setUp() public { - registry = new LicenseRegistry("https://example.com/{id}.json"); - verifier1 = new MockParamVerifier(MockParamVerifierConfig({ + registry = new LicenseRegistry(); + module1 = new MockLicensingFramework(MockLicensingFrameworkConfig({ licenseRegistry: address(registry), - name: "MockForVerifyingMint", - supportVerifyLink: false, + licenseUrl: licenseUrl, + supportVerifyLink: true, supportVerifyMint: true, - supportVerifyTransfer: false - })); - verifier2 = new MockParamVerifier(MockParamVerifierConfig({ - licenseRegistry: address(registry), - name: "MockForVerifyingTransfer", - supportVerifyLink: false, - supportVerifyMint: false, supportVerifyTransfer: true })); } // TODO: add parameter config for initial framework for 100% test modifier withFrameworkParams() { - _initFwParams(); - registry.addLicenseFramework(fwParams); // framework ID = 1 + module1.register(); _; } - // TODO: use ModuleBaseTest for this - function _initFwParams() private { - IParamVerifier[] memory parameters = new IParamVerifier[](2); - parameters[0] = verifier1; - parameters[1] = verifier2; - bytes[] memory values = new bytes[](2); - values[0] = abi.encode(false); - values[0] = abi.encode(false); - - fwParams = Licensing.FrameworkCreationParams({ - parameters: parameters, - defaultValues: values, - licenseUrl: licenseUrl - }); - } - - function _createPolicy() internal view returns (Licensing.Policy memory pol) { + function _createPolicy() internal pure returns (Licensing.Policy memory pol) { pol = Licensing.Policy({ frameworkId: 1, - commercialUse: true, - derivatives: true, - paramNames: new bytes32[](2), - paramValues: new bytes[](2) + data: abi.encode( + MockPolicy({ + returnVerifyLink: true, + returnVerifyMint: true, + returnVerifyTransfer: true + }) + ) }); - pol.paramNames[0] = verifier1.name(); - pol.paramNames[1] = verifier2.name(); - pol.paramValues[0] = abi.encode(true); - pol.paramValues[1] = abi.encode(true); return pol; } function test_LicenseRegistry_addLicenseFramework() public { - _initFwParams(); - uint256 fwId = registry.addLicenseFramework(fwParams); + uint256 fwId = module1.register(); assertEq(fwId, 1, "not incrementing fw id"); - assertTrue(fwParams.licenseUrl.equal(registry.frameworkUrl(fwId)), "licenseUrl not set"); + assertTrue(licenseUrl.equal(registry.frameworkUrl(fwId)), "licenseUrl not set"); assertEq(registry.totalFrameworks(), 1, "totalFrameworks not incremented"); - string memory paramName = fwParams.parameters[0].nameString(); - Licensing.Parameter memory storedParam = registry.frameworkParam(1, paramName); - assertEq(address(storedParam.verifier), address(fwParams.parameters[0]), "verifier not equal"); - // assertEq(address(storedParam.verifier), address(fwParams.parameters[0]), "verifier not equal"); - assertEq(storedParam.defaultValue, fwParams.defaultValues[0], "defaultValue not equal"); + Licensing.Framework memory storedFw = registry.framework(fwId); + assertEq(storedFw.licenseUrl, licenseUrl, "licenseUrl not equal"); + assertEq(storedFw.licensingFramework, address(module1), "licensingFramework not equal"); } - function test_LicenseRegistry_addPolicy() public withFrameworkParams { + function test_LicenseRegistry_addPolicy() public { + module1.register(); Licensing.Policy memory policy = _createPolicy(); + vm.prank(address(module1)); uint256 polId = registry.addPolicy(policy); assertEq(polId, 1, "polId not 1"); } - function test_LicenseRegistry_addPolicy_revert_policyAlreadyAdded() public withFrameworkParams { + function test_LicenseRegistry_addPolicy_revert_policyAlreadyAdded() public { + module1.register(); Licensing.Policy memory policy = _createPolicy(); + vm.startPrank(address(module1)); registry.addPolicy(policy); vm.expectRevert(Errors.LicenseRegistry__PolicyAlreadyAdded.selector); registry.addPolicy(policy); + vm.stopPrank(); } function test_LicenseRegistry_addPolicy_revert_frameworkNotFound() public { Licensing.Policy memory policy = _createPolicy(); vm.expectRevert(Errors.LicenseRegistry__FrameworkNotFound.selector); + vm.prank(address(module1)); registry.addPolicy(policy); } - function test_LicenseRegistry_addPolicyToIpId() public withFrameworkParams { + function test_LicenseRegistry_addPolicyToIpId() public { + module1.register(); Licensing.Policy memory policy = _createPolicy(); - (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + uint256 indexOnIpId = registry.addPolicyToIp(ipId1, policyId); assertEq(policyId, 1, "policyId not 1"); assertEq(indexOnIpId, 0, "indexOnIpId not 0"); assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); @@ -125,29 +104,32 @@ contract LicenseRegistryTest is Test { assertEq(keccak256(abi.encode(storedPolicy)), keccak256(abi.encode(policy)), "policy not stored properly"); } - function test_LicenseRegistry_addSamePolicyReusesPolicyId() public withFrameworkParams { + function test_LicenseRegistry_addSamePolicyReusesPolicyId() public { + module1.register(); Licensing.Policy memory policy = _createPolicy(); - (uint256 policyId, bool isNew1, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); - assertTrue(isNew1, "not new"); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + uint256 indexOnIpId = registry.addPolicyToIp(ipId1, policyId); assertEq(indexOnIpId, 0); assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); - (uint256 policyId2, bool isNew2, uint256 indexOnIpId2) = registry.addPolicyToIp(ipId2, policy); - assertFalse(isNew2, "new"); + + uint256 indexOnIpId2 = registry.addPolicyToIp(ipId2, policyId); assertEq(indexOnIpId2, 0); assertFalse(registry.isPolicySetByLinking(ipId2, policyId)); - assertEq(policyId, policyId2, "policyId not reused"); } //function test_LicenseRegistry_revert_policyAlreadyAddedToIpId() - function test_LicenseRegistry_add2PoliciesToIpId() public withFrameworkParams { + function test_LicenseRegistry_add2PoliciesToIpId() public { + module1.register(); assertEq(registry.totalPolicies(), 0); assertEq(registry.totalPoliciesForIp(ipId1), 0); Licensing.Policy memory policy = _createPolicy(); // First time adding a policy - (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); - assertTrue(isNew, "not new"); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + uint256 indexOnIpId = registry.addPolicyToIp(ipId1, policyId); assertEq(policyId, 1, "policyId not 1"); assertEq(indexOnIpId, 0, "indexOnIpId not 0"); assertEq(registry.totalPolicies(), 1, "totalPolicies not incremented"); @@ -156,9 +138,16 @@ contract LicenseRegistryTest is Test { assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); // Adding different policy to same ipId - policy.paramValues[0] = abi.encode("test2"); - (uint256 policyId2, bool isNew2, uint256 indexOnIpId2) = registry.addPolicyToIp(ipId1, policy); - assertTrue(isNew2, "not new"); + policy.data = abi.encode( + MockPolicy({ + returnVerifyLink: true, + returnVerifyMint: true, + returnVerifyTransfer: false + }) + ); + vm.prank(address(module1)); + uint256 policyId2 = registry.addPolicy(policy); + uint256 indexOnIpId2 = registry.addPolicyToIp(ipId1, policyId2); assertEq(policyId2, 2, "policyId not 2"); assertEq(indexOnIpId2, 1, "indexOnIpId not 1"); assertEq(registry.totalPolicies(), 2, "totalPolicies not incremented"); @@ -167,10 +156,12 @@ contract LicenseRegistryTest is Test { assertFalse(registry.isPolicySetByLinking(ipId1, policyId2)); } - function test_LicenseRegistry_mintLicense() public withFrameworkParams returns (uint256 licenseId) { + function test_LicenseRegistry_mintLicense() public returns (uint256 licenseId) { + module1.register(); Licensing.Policy memory policy = _createPolicy(); - // solhint-disable-next-line no-unused-vars - (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + uint256 indexOnIpId = registry.addPolicyToIp(ipId1, policyId); assertEq(policyId, 1); assertTrue(registry.isPolicyIdSetForIp(ipId1, policyId)); @@ -189,6 +180,7 @@ contract LicenseRegistryTest is Test { } function test_LicenseRegistry_linkIpToParent() public { + // TODO: something cleaner than this uint256 licenseId = test_LicenseRegistry_mintLicense(); @@ -213,44 +205,59 @@ contract LicenseRegistryTest is Test { assertEq(parents[0], ipId1, "parent not ipId1"); } - function test_LicenseRegistry_singleTransfer_paramVerifyTrue() public withFrameworkParams { + function test_LicenseRegistry_singleTransfer_paramVerifyTrue() public { + module1.register(); Licensing.Policy memory policy = _createPolicy(); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + registry.addPolicyToIp(ipId1, policyId); - (uint256 policyId, , ) = registry.addPolicyToIp(ipId1, policy); + uint256 licenseId = registry.mintLicense(policyId, ipId1, 2, licenseHolder); + address licenseHolder2 = address(0x102); + vm.prank(licenseHolder); + registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); + assertEq(registry.balanceOf(licenseHolder, licenseId), 1, "not burnt"); + } + + function test_LicenseRegistry_singleTransfer_verifyOk() public { + module1.register(); + Licensing.Policy memory policy = _createPolicy(); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + registry.addPolicyToIp(ipId1, policyId); uint256 licenseId = registry.mintLicense(policyId, ipId1, 2, licenseHolder); + address licenseHolder2 = address(0x102); + assertEq(registry.balanceOf(licenseHolder, licenseId), 2); + assertEq(registry.balanceOf(licenseHolder2, licenseId), 0); vm.prank(licenseHolder); registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); assertEq(registry.balanceOf(licenseHolder, licenseId), 1, "not burnt"); + assertEq(registry.balanceOf(licenseHolder2, licenseId), 1, "not minted"); } - function test_LicenseRegistry_revert_singleTransfer_transferParamVerifyFalse() public withFrameworkParams { + function test_LicenseRegistry_singleTransfer_revert_verifyFalse() public { + module1.register(); Licensing.Policy memory policy = Licensing.Policy({ frameworkId: 1, - commercialUse: true, - derivatives: true, - paramNames: new bytes32[](2), - paramValues: new bytes[](2) + data: abi.encode( + MockPolicy({ + returnVerifyLink: true, + returnVerifyMint: true, + returnVerifyTransfer: false + }) + ) }); - policy.paramNames[0] = verifier1.name(); - policy.paramValues[0] = abi.encode(true); - policy.paramNames[1] = verifier2.name(); - policy.paramValues[1] = abi.encode(false); - - (uint256 policyId, , ) = registry.addPolicyToIp(ipId1, policy); + vm.prank(address(module1)); + uint256 policyId = registry.addPolicy(policy); + registry.addPolicyToIp(ipId1, policyId); uint256 licenseId = registry.mintLicense(policyId, ipId1, 2, licenseHolder); address licenseHolder2 = address(0x102); + vm.expectRevert(Errors.LicenseRegistry__TransferParamFailed.selector); vm.prank(licenseHolder); - vm.expectRevert( - abi.encodeWithSelector( - Errors.LicenseRegistry__ParamVerifierFailed.selector, - uint8(Licensing.ParamVerifierType.Transfer), - address(verifier2) - ) - ); registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); } -} +} \ No newline at end of file diff --git a/test/foundry/registries/metadata/IPAssetRenderer.t.sol b/test/foundry/registries/metadata/IPAssetRenderer.t.sol index a436a9830..4209c7a24 100644 --- a/test/foundry/registries/metadata/IPAssetRenderer.t.sol +++ b/test/foundry/registries/metadata/IPAssetRenderer.t.sol @@ -83,7 +83,7 @@ contract IPAssetRendererTest is BaseTest { BaseTest.setUp(); governance = new Governance(address(this)); // TODO: Create an IP record registry mock instead. - licenseRegistry = new LicenseRegistry(""); + licenseRegistry = new LicenseRegistry(); accessController = new AccessController(address(governance)); moduleRegistry = new ModuleRegistry(address(governance)); MockERC721 erc721 = new MockERC721();