diff --git a/contracts/interfaces/licensing/ILinkParamVerifier.sol b/contracts/interfaces/licensing/ILinkParamVerifier.sol new file mode 100644 index 000000000..16f6f4d6d --- /dev/null +++ b/contracts/interfaces/licensing/ILinkParamVerifier.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; + +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; + +interface ILinkParamVerifier is IParamVerifier { + function verifyLink( + uint256 licenseId, + address caller, + address ipId, + address parentIpId, + bytes calldata data + ) external returns (bool); +} \ No newline at end of file diff --git a/contracts/interfaces/licensing/IMintParamVerifier.sol b/contracts/interfaces/licensing/IMintParamVerifier.sol new file mode 100644 index 000000000..137a8de73 --- /dev/null +++ b/contracts/interfaces/licensing/IMintParamVerifier.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; + +interface IMintParamVerifier is IParamVerifier { + function verifyMint( + address caller, + uint256 policyId, + bool policyAddedByLinking, + address licensor, + address receiver, + uint256 mintAmount, + bytes memory data + ) external returns (bool); +} + diff --git a/contracts/interfaces/licensing/IParamVerifier.sol b/contracts/interfaces/licensing/IParamVerifier.sol index 9add0b562..7dfe2ca90 100644 --- a/contracts/interfaces/licensing/IParamVerifier.sol +++ b/contracts/interfaces/licensing/IParamVerifier.sol @@ -1,10 +1,15 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +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); -interface IParamVerifier { - function verifyMinting(address caller, uint256 amount, bytes memory data) external returns (bool); - function verifyTransfer(address caller, uint256 amount, bytes memory data) external returns (bool); - function verifyLinkParent(address caller, bytes memory data) external returns (bool); function json() external view returns (string memory); + + function allowsOtherPolicyOnSameIp(bytes memory data) external view returns (bool); } diff --git a/contracts/interfaces/licensing/ITransferParamVerifier.sol b/contracts/interfaces/licensing/ITransferParamVerifier.sol new file mode 100644 index 000000000..1edb2c4d0 --- /dev/null +++ b/contracts/interfaces/licensing/ITransferParamVerifier.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.23; + +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; + +interface ITransferParamVerifier is IParamVerifier { + function verifyTransfer( + uint256 licenseId, + address from, + address to, + uint256 amount, + bytes memory data + ) external returns (bool); +} \ No newline at end of file diff --git a/contracts/interfaces/registries/ILicenseRegistry.sol b/contracts/interfaces/registries/ILicenseRegistry.sol index 8f4d47199..fd886ce67 100644 --- a/contracts/interfaces/registries/ILicenseRegistry.sol +++ b/contracts/interfaces/registries/ILicenseRegistry.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; import { Licensing } from "contracts/lib/Licensing.sol"; @@ -12,7 +12,13 @@ interface ILicenseRegistry { event PolicyCreated(address indexed creator, uint256 indexed policyId, Licensing.Policy policy); - event PolicyAddedToIpId(address indexed caller, address indexed ipId, uint256 indexed policyId, bool setByLinking); + event PolicyAddedToIpId( + address indexed caller, + address indexed ipId, + uint256 indexed policyId, + uint256 index, + bool setByLinking + ); event LicenseMinted( address indexed creator, @@ -39,7 +45,7 @@ interface ILicenseRegistry { function mintLicense( uint256 policyId, - address[] calldata licensorIpIds, + address licensorIpId, uint256 amount, address receiver ) external returns (uint256 licenseId); @@ -52,10 +58,9 @@ interface ILicenseRegistry { function totalFrameworks() external view returns (uint256); - function frameworkParams(uint256 frameworkId, Licensing.ParamVerifierType pvt) external view returns (Licensing.Parameter[] memory); + function frameworkParam(uint256 frameworkId, string calldata name) external view returns (Licensing.Parameter memory); function frameworkUrl(uint256 frameworkId) external view returns (string memory); - function totalPolicies() external view returns (uint256); function policy(uint256 policyId) external view returns (Licensing.Policy memory pol); @@ -72,10 +77,14 @@ interface ILicenseRegistry { function policyForIpAtIndex(address ipId, uint256 index) external view returns (Licensing.Policy memory); - function isPolicyIdAtIndexSetByLinking(address ipId, uint256 index) external view returns (bool); + function indexOfPolicyForIp(address ipId, uint256 policyId) external view returns (uint256 index); + + function isPolicySetByLinking(address ipId, uint256 policyId) external view returns (bool); function isLicensee(uint256 licenseId, address holder) external view returns (bool); + function licensorIpId(uint256 licenseId) external view returns (address); + function license(uint256 licenseId) external view returns (Licensing.License memory); function isParent(address parentIpId, address childIpId) external view returns (bool); function parentIpIds(address ipId) external view returns (address[] memory); diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index 596ae535b..1f88a4ec8 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -73,8 +73,19 @@ library Errors { error LicenseRegistry__LicensorDoesntHaveThisPolicy(); error LicenseRegistry__ParamVerifierFailed(uint8 verifierType, address verifier); error LicenseRegistry__LinkParentParamFailed(); - error LicenseRegistry__LicenseMustHaveLicensors(); error LicenseRegistry__InvalidLicensor(); + error LicenseRegistry__ParamVerifierAlreadySet(); + + //////////////////////////////////////////////////////////////////////////// + // BaseParamVerifier // + //////////////////////////////////////////////////////////////////////////// + error BaseParamVerifier__Unauthorized(); + + //////////////////////////////////////////////////////////////////////////// + // DerivativesParamVerifier // + //////////////////////////////////////////////////////////////////////////// + error DerivativesParamVerifier__InvalidDerivativesConfig(); + error DerivativesParamVerifier__ZeroShare(); //////////////////////////////////////////////////////////////////////////// // Dispute Module // diff --git a/contracts/lib/Licensing.sol b/contracts/lib/Licensing.sol index 3ded9c5bd..f80c5443e 100644 --- a/contracts/lib/Licensing.sol +++ b/contracts/lib/Licensing.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import { IParamVerifier } from "../interfaces/licensing/IParamVerifier.sol"; @@ -6,6 +6,7 @@ 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 @@ -27,61 +28,39 @@ library Licensing { /// 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 license lifetime - /// ParamVerifierType.Mint --> These parameters need to be verified when minting a license - /// ParamVerifierType.LinkParent --> Verified before the owner of a license links to a parent ipId/policy, - /// burning the license and setting the policy for the ipId. - /// ParamVerifierType.Transfer -> verified when transfering NFT - mapping(ParamVerifierType => Parameter[]) parameters; + /// @notice Stores the parameters that need to be verified in each moment of the licensing lifetime + mapping(bytes32 => Parameter) parameters; /// @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[] mintingVerifiers; - bytes[] mintingDefaultValues; - IParamVerifier[] linkParentVerifiers; - bytes[] linkParentDefaultValues; - IParamVerifier[] transferVerifiers; - bytes[] transferDefaultValues; + IParamVerifier[] parameters; + bytes[] defaultValues; string licenseUrl; } - - /// A particular configuration of a Licensing Framework, setting (or not) values fo the licensing + + /// A particular configuration of a Licensing Framework, setting (or not) values for the licensing /// terms (parameters) of the framework. - /// The lengths of the param value arrays must correspond to the Parameter[] 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; - /// Array with values for parameters verifying conditions to mint a license. Empty bytes for index if - /// this policy wants to use the default value for the paremeter. - bytes[] mintingParamValues; - /// Array with values for parameters verifying conditions to link a license to a parent. Empty bytes for index if - /// this policy wants to use the default value for the paremeter. - bytes[] linkParentParamValues; - /// Array with values for parameters verifying conditions to transfer a license. Empty bytes for index if - /// this policy wants to use the default value for the paremeter. - bytes[] transferParamValues; - } - - function getValues(Policy memory policy, ParamVerifierType pvt) internal returns(bytes[] memory) { - if (pvt == ParamVerifierType.Mint) { - return policy.mintingParamValues; - } else if (pvt == ParamVerifierType.LinkParent) { - return policy.linkParentParamValues; - } else if (pvt == ParamVerifierType.Transfer) { - return policy.transferParamValues; - } else { - revert Errors.LicenseRegistry__InvalidParamVerifierType(); - } + /// 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; } /// Data that define a License Agreement NFT struct License { /// the id for the Policy this License will set to the desired derivative IP after being burned. uint256 policyId; - /// Ids for the licensors, meaning the Ip Ids of the parents of the derivative to be created - address[] licensorIpIds; + /// Id for the licensor of the Ip Id + address licensorIpId; } } diff --git a/contracts/modules/licensing/parameters/BaseParamVerifier.sol b/contracts/modules/licensing/parameters/BaseParamVerifier.sol new file mode 100644 index 000000000..b62ed7fdf --- /dev/null +++ b/contracts/modules/licensing/parameters/BaseParamVerifier.sol @@ -0,0 +1,56 @@ +// 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 new file mode 100644 index 000000000..7da519561 --- /dev/null +++ b/contracts/modules/licensing/parameters/DerivativesParamVerifier.sol @@ -0,0 +1,168 @@ +// // 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 eef3f622a..870a6a819 100644 --- a/contracts/registries/LicenseRegistry.sol +++ b/contracts/registries/LicenseRegistry.sol @@ -1,12 +1,18 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; // external import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; +import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + // contracts 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 { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransferParamVerifier.sol"; import { ILicenseRegistry } from "contracts/interfaces/registries/ILicenseRegistry.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; @@ -16,18 +22,28 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using Strings for *; + using ShortStrings for *; using Licensing for *; + struct PolicySetup { + uint256 index; + bool isSet; + bool active; + bool setByLinking; + } + mapping(uint256 => Licensing.Framework) private _frameworks; uint256 private _totalFrameworks; mapping(bytes32 => uint256) private _hashedPolicies; mapping(uint256 => Licensing.Policy) private _policies; uint256 private _totalPolicies; - // DO NOT remove policies, that rugs derivatives and breaks ordering assumptions in set + /// @notice internal mapping to track if a policy was set by linking or minting, and the + /// index of the policy in the ipId policy set + /// Policies can't be removed, but they can be deactivated by setting active to false + /// @dev ipId => policyId => PolicySetup + mapping(address => mapping(uint256 => PolicySetup)) private _policySetups; mapping(address => EnumerableSet.UintSet) private _policiesPerIpId; - // - mapping(address => bool[]) private _policyPerIpIdSetByLinking; mapping(address => EnumerableSet.AddressSet) private _ipIdParents; @@ -63,49 +79,22 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { ++_totalFrameworks; Licensing.Framework storage fw = _frameworks[_totalFrameworks]; fw.licenseUrl = fwCreation.licenseUrl; - _setParamArray( - fw, - Licensing.ParamVerifierType.Mint, - fwCreation.mintingVerifiers, - fwCreation.mintingDefaultValues - ); - _setParamArray( - fw, - Licensing.ParamVerifierType.LinkParent, - fwCreation.linkParentVerifiers, - fwCreation.linkParentDefaultValues - ); - _setParamArray( - fw, - Licensing.ParamVerifierType.Transfer, - fwCreation.transferVerifiers, - fwCreation.transferDefaultValues - ); - emit LicenseFrameworkCreated(msg.sender, _totalFrameworks, fwCreation); - return _totalFrameworks; - } - - /// Convenience method to convert IParamVerifier[] + bytes[] into Parameter[] - /// After conversion, it stores it in a Framework storage ref - /// (Parameter[] can be in storage but not in calldata) - /// @param fw storage ref to framework - /// @param pvt ParamVerifierType, to know which parameters the arrays correspond to - /// @param paramVerifiers verifier array contracts - /// @param paramDefaultValues default values for the verifiers. Must be equal in length with paramVerifiers - function _setParamArray( - Licensing.Framework storage fw, - Licensing.ParamVerifierType pvt, - IParamVerifier[] calldata paramVerifiers, - bytes[] calldata paramDefaultValues - ) private { - if (paramVerifiers.length != paramDefaultValues.length) { + uint256 paramLength = fwCreation.parameters.length; + if (paramLength != fwCreation.defaultValues.length) { revert Errors.LicenseRegistry__ParamVerifierLengthMismatch(); } - // TODO: check pvt is valid - Licensing.Parameter[] storage params = fw.parameters[pvt]; - for (uint256 i = 0; i < paramVerifiers.length; i++) { - params.push(Licensing.Parameter({ verifier: paramVerifiers[i], defaultValue: paramDefaultValues[i] })); + 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] }); } + + emit LicenseFrameworkCreated(msg.sender, _totalFrameworks, fwCreation); + return _totalFrameworks; } /// Gets total frameworks supported by LicenseRegistry @@ -114,12 +103,12 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { } /// Returns framework for id. Reverts if not found - function frameworkParams( + function frameworkParam( uint256 frameworkId, - Licensing.ParamVerifierType pvt - ) public view returns (Licensing.Parameter[] memory) { + string calldata name + ) public view returns (Licensing.Parameter memory) { Licensing.Framework storage fw = _framework(frameworkId); - return fw.parameters[pvt]; + return fw.parameters[ShortString.unwrap(name.toShortString())]; } function _framework(uint256 frameworkId) internal view returns (Licensing.Framework storage fw) { @@ -218,14 +207,22 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { /// @param setByLinking true if set in linkIpToParent, false otherwise /// @return index of the policy added to the set function _addPolictyIdToIp(address ipId, uint256 policyId, bool setByLinking) internal returns (uint256 index) { - EnumerableSet.UintSet storage policySet = _policiesPerIpId[ipId]; - // TODO: check if policy is compatible with the others - if (!policySet.add(policyId)) { + EnumerableSet.UintSet storage _pols = _policiesPerIpId[ipId]; + if (!_pols.add(policyId)) { revert Errors.LicenseRegistry__PolicyAlreadySetForIpId(); } - _policyPerIpIdSetByLinking[ipId].push(setByLinking); - emit PolicyAddedToIpId(msg.sender, ipId, policyId, setByLinking); - return policySet.length() - 1; + index = _pols.length() - 1; + PolicySetup storage setup = _policySetups[ipId][policyId]; + // This should not happen, but just in case + if (setup.isSet) { + revert Errors.LicenseRegistry__PolicyAlreadySetForIpId(); + } + setup.index = index; + setup.isSet = true; + setup.active = true; + setup.setByLinking = setByLinking; + emit PolicyAddedToIpId(msg.sender, ipId, policyId, index, setByLinking); + return index; } /// Returns amount of distinct licensing policies in LicenseRegistry @@ -269,8 +266,12 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { return _policies[_policiesPerIpId[ipId].at(index)]; } - function isPolicyIdAtIndexSetByLinking(address ipId, uint256 index) external view returns (bool) { - return _policyPerIpIdSetByLinking[ipId][index]; + function indexOfPolicyForIp(address ipId, uint256 policyId) external view returns (uint256 index) { + return _policySetups[ipId][policyId].index; + } + + function isPolicySetByLinking(address ipId, uint256 policyId) external view returns (bool) { + return _policySetups[ipId][policyId].setByLinking; } /// Mints license NFTs representing a policy granted by a set of ipIds (licensors). This NFT needs to be burned @@ -281,38 +282,56 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { /// The licensing terms that regulate creating new licenses will be verified to allow minting. /// Reverts if caller is not authorized by licensors. /// @param policyId id of the policy to be minted - /// @param licensorIpIds array of IP Ids that are granting the license + /// @param licensorIp IP Id granting the license /// @param amount of licenses to be minted. License NFT is fungible for same policy and same licensors /// @param receiver of the License NFT(s). /// @return licenseId of the NFT(s). function mintLicense( uint256 policyId, - address[] memory licensorIpIds, - uint256 amount, + address licensorIp, + uint256 amount, // mint amount address receiver ) external returns (uint256 licenseId) { - uint256 licensorAmount = licensorIpIds.length; - if (licensorAmount == 0) { - revert Errors.LicenseRegistry__LicenseMustHaveLicensors(); + // TODO: check if licensor are valid IP Ids + // TODO: check if licensor has been tagged by disputer + // TODO: check if licensor allowed sender to mint in their behalf + // TODO: licensor == msg.sender, expect if derivatives && withReciprocal + if (licensorIp == address(0)) { + revert Errors.LicenseRegistry__InvalidLicensor(); } - for (uint256 i = 0; i < licensorAmount; i++) { - address licensor = licensorIpIds[i]; - // TODO: check duplicates - // TODO: check if licensors are valid IP Ids - // TODO: check if licensors they have been tagged by disputer - // TODO: check if licensor allowed sender to mint in their behalf - if (licensor == address(0)) { - revert Errors.LicenseRegistry__InvalidLicensor(); - } - if (!_policiesPerIpId[licensor].contains(policyId)) { - revert Errors.LicenseRegistry__LicensorDoesntHaveThisPolicy(); - } + if (!_policiesPerIpId[licensorIp].contains(policyId)) { + revert Errors.LicenseRegistry__LicensorDoesntHaveThisPolicy(); } + // Verify minting param Licensing.Policy memory pol = policy(policyId); - _verifyParams(Licensing.ParamVerifierType.Mint, pol, receiver, amount); - Licensing.License memory licenseData = Licensing.License({ policyId: policyId, licensorIpIds: licensorIpIds }); - (uint256 lId, bool isNew) = _addIdOrGetExisting(abi.encode(licenseData), _hashedLicenses, _totalLicenses); - licenseId = lId; + 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) + ); + } + } + } + + Licensing.License memory licenseData = Licensing.License({ policyId: policyId, licensorIpId: licensorIp }); + bool isNew; + (licenseId, isNew) = _addIdOrGetExisting(abi.encode(licenseData), _hashedLicenses, _totalLicenses); if (isNew) { _totalLicenses = licenseId; _licenses[licenseId] = licenseData; @@ -350,31 +369,43 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { ) external onlyLicensee(licenseId, holder) { // TODO: check if childIpId exists and is owned by holder Licensing.License memory licenseData = _licenses[licenseId]; - address[] memory parents = licenseData.licensorIpIds; - for (uint256 i = 0; i < parents.length; i++) { - // TODO: check licensor exist - // TODO: check licensor not part of a branch tagged by disputer + address parentIpId = licenseData.licensorIpId; + if (parentIpId == childIpId) { + revert Errors.LicenseRegistry__ParentIdEqualThanChild(); } + // TODO: check licensor exist + // TODO: check licensor not part of a branch tagged by disputer + // Verify linking params Licensing.Policy memory pol = policy(licenseData.policyId); - _verifyParams(Licensing.ParamVerifierType.LinkParent, pol, holder, 1); + 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) + ); + } + } + } // Add policy to kid - // TODO: return index of policy in ipId? _addPolictyIdToIp(childIpId, licenseData.policyId, true); // Set parent - for (uint256 i = 0; i < parents.length; i++) { - // We don't care if it was already a parent, because there might be a case such as: - // 1. IP2 is created from IP1 with L1(non commercial) - // 2. IP1 releases L2 with commercial terms, and IP2 wants permission to commercially exploit - // 3. IP2 gets L2, burns it to set commercial policy - address parent = parents[i]; - if (parent == childIpId) { - revert Errors.LicenseRegistry__ParentIdEqualThanChild(); - } - _ipIdParents[childIpId].add(parent); - emit IpIdLinkedToParent(msg.sender, childIpId, parent); - } + _ipIdParents[childIpId].add(parentIpId); + emit IpIdLinkedToParent(msg.sender, childIpId, parentIpId); // Burn license _burn(holder, licenseId, 1); @@ -397,6 +428,11 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { return _licenses[licenseId]; } + function licensorIpId(uint256 licenseId) external view returns (address) { + return _licenses[licenseId].licensorIpId; + } + + /// @dev Pre-hook for ERC1155's _update() called on transfers. function _update( address from, address to, @@ -404,47 +440,40 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry { uint256[] memory values ) internal virtual override { // We are interested in transfers, minting and burning are checked in mintLicense and linkIpToParent respectively. - if (from != address(0) && to != address(0)) { - uint256 length = ids.length; - for (uint256 i = 0; i < length; i++) { - _verifyParams(Licensing.ParamVerifierType.Transfer, policyForLicense(ids[i]), to, values[i]); + for (uint256 i = 0; i < ids.length; i++) { + // 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) + ); + } + } + } } } super._update(from, to, ids, values); } - function _verifyParams( - Licensing.ParamVerifierType pvt, - Licensing.Policy memory pol, - address holder, - uint256 amount - ) internal { - Licensing.Framework storage fw = _framework(pol.frameworkId); - Licensing.Parameter[] storage params = fw.parameters[pvt]; - uint256 paramsLength = params.length; - bytes[] memory values = pol.getValues(pvt); - - for (uint256 i = 0; i < paramsLength; i++) { - Licensing.Parameter memory param = params[i]; - // Empty bytes => use default value specified in license framework creation params. - bytes memory data = values[i].length == 0 ? param.defaultValue : values[i]; - bool verificationOk = false; - if (pvt == Licensing.ParamVerifierType.Mint) { - verificationOk = param.verifier.verifyMinting(holder, amount, data); - } else if (pvt == Licensing.ParamVerifierType.LinkParent) { - verificationOk = param.verifier.verifyLinkParent(holder, data); - } else if (pvt == Licensing.ParamVerifierType.Transfer) { - verificationOk = param.verifier.verifyTransfer(holder, amount, data); - } else { - // This should never happen since getValues checks for pvt validity - revert Errors.LicenseRegistry__InvalidParamVerifierType(); - } - if (!verificationOk) { - revert Errors.LicenseRegistry__ParamVerifierFailed(uint8(pvt), address(param.verifier)); - } - } - } - // TODO: tokenUri from parameters, from a metadata resolver contract } diff --git a/contracts/utils/ShortStringOps.sol b/contracts/utils/ShortStringOps.sol index 71257444f..e29c5a30b 100644 --- a/contracts/utils/ShortStringOps.sol +++ b/contracts/utils/ShortStringOps.sol @@ -42,5 +42,3 @@ library ShortStringOps { return ShortString.unwrap(s.toShortString()); } } - -library EnumerableShortStringSet {} diff --git a/test/foundry/LicenseRegistry.t.sol b/test/foundry/LicenseRegistry.t.sol index b55a72c0f..206c8760a 100644 --- a/test/foundry/LicenseRegistry.t.sol +++ b/test/foundry/LicenseRegistry.t.sol @@ -4,171 +4,143 @@ 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 { MockIParamVerifier } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; +import { MockParamVerifier, MockParamVerifierConfig } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import "forge-std/console2.sol"; import { Errors } from "contracts/lib/Errors.sol"; +import { ShortString, ShortStrings } from "@openzeppelin/contracts/utils/ShortStrings.sol"; +import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; contract LicenseRegistryTest is Test { using Strings for *; + using ShortStrings for *; LicenseRegistry public registry; Licensing.Framework public framework; - MockIParamVerifier public verifier; + MockParamVerifier public verifier1; + MockParamVerifier public verifier2; + Licensing.FrameworkCreationParams public fwParams; + string public licenseUrl = "https://example.com/license"; - Licensing.FrameworkCreationParams fwParams; address public ipId1 = address(0x111); address public ipId2 = address(0x222); address public licenseHolder = address(0x101); + function setUp() public { + registry = new LicenseRegistry("https://example.com/{id}.json"); + verifier1 = new MockParamVerifier(MockParamVerifierConfig({ + licenseRegistry: address(registry), + name: "MockForVerifyingMint", + supportVerifyLink: false, + 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); + registry.addLicenseFramework(fwParams); // framework ID = 1 _; } // TODO: use ModuleBaseTest for this function _initFwParams() private { - IParamVerifier[] memory mintingVerifiers = new IParamVerifier[](1); - mintingVerifiers[0] = verifier; - bytes[] memory mintingDefaultValues = new bytes[](1); - mintingDefaultValues[0] = abi.encode(true); - IParamVerifier[] memory linkParentVerifiers = new IParamVerifier[](1); - linkParentVerifiers[0] = verifier; - bytes[] memory linkParentDefaultValues = new bytes[](1); - linkParentDefaultValues[0] = abi.encode(true); - IParamVerifier[] memory transferVerifiers = new IParamVerifier[](1); - transferVerifiers[0] = verifier; - bytes[] memory transferDefaultValues = new bytes[](1); - transferDefaultValues[0] = abi.encode(true); + 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({ - mintingVerifiers: mintingVerifiers, - mintingDefaultValues: mintingDefaultValues, - linkParentVerifiers: linkParentVerifiers, - linkParentDefaultValues: linkParentDefaultValues, - transferVerifiers: transferVerifiers, - transferDefaultValues: transferDefaultValues, + parameters: parameters, + defaultValues: values, licenseUrl: licenseUrl }); } - function _createPolicy() pure internal returns(Licensing.Policy memory pol) { + function _createPolicy() internal view returns (Licensing.Policy memory pol) { pol = Licensing.Policy({ frameworkId: 1, - mintingParamValues: new bytes[](1), - linkParentParamValues: new bytes[](1), - transferParamValues: new bytes[](1) + commercialUse: true, + derivatives: true, + paramNames: new bytes32[](2), + paramValues: new bytes[](2) }); - pol.mintingParamValues[0] = abi.encode(true); - pol.linkParentParamValues[0] = abi.encode(true); - pol.transferParamValues[0] = abi.encode(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 setUp() public { - verifier = new MockIParamVerifier(); - registry = new LicenseRegistry("https://example.com/{id}.json"); - } - function test_LicenseRegistry_addLicenseFramework() public { _initFwParams(); uint256 fwId = registry.addLicenseFramework(fwParams); assertEq(fwId, 1, "not incrementing fw id"); assertTrue(fwParams.licenseUrl.equal(registry.frameworkUrl(fwId)), "licenseUrl not set"); assertEq(registry.totalFrameworks(), 1, "totalFrameworks not incremented"); - assertEq(registry.frameworkParams(fwId, Licensing.ParamVerifierType.Mint).length, 1); - assertEq(registry.totalFrameworks(), 1, "total frameworks not updated"); - _assertEqualParams( - registry.frameworkParams(fwId, Licensing.ParamVerifierType.Mint), - fwParams.mintingVerifiers, - fwParams.mintingDefaultValues - ); - _assertEqualParams( - registry.frameworkParams(fwId, Licensing.ParamVerifierType.LinkParent), - fwParams.linkParentVerifiers, - fwParams.linkParentDefaultValues - ); - _assertEqualParams( - registry.frameworkParams(fwId, Licensing.ParamVerifierType.Transfer), - fwParams.transferVerifiers, - fwParams.transferDefaultValues - ); - } - - function _assertEqualParams( - Licensing.Parameter[] memory params1, - IParamVerifier[] memory verifiers, - bytes[] memory defaultValues - ) private { - assertEq(params1.length, verifiers.length, "length mismatch"); - assertEq(params1.length, defaultValues.length, "length mismatch"); - for (uint256 i = 0; i < params1.length; i++) { - assertEq(address(params1[i].verifier), address(verifiers[i]), "verifier mismatch"); - assertEq(params1[i].defaultValue, defaultValues[i], "default value mismatch"); - } + 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"); } - function test_LicenseRegistry_addPolicy() - withFrameworkParams - public { + function test_LicenseRegistry_addPolicy() public withFrameworkParams { Licensing.Policy memory policy = _createPolicy(); uint256 polId = registry.addPolicy(policy); assertEq(polId, 1, "polId not 1"); } - function test_LicenseRegistry_addPolicy_revert_policyAlreadyAdded() - withFrameworkParams - public { + function test_LicenseRegistry_addPolicy_revert_policyAlreadyAdded() public withFrameworkParams { Licensing.Policy memory policy = _createPolicy(); registry.addPolicy(policy); vm.expectRevert(Errors.LicenseRegistry__PolicyAlreadyAdded.selector); registry.addPolicy(policy); } - function test_LicenseRegistry_addPolicy_revert_frameworkNotFound() - public { + function test_LicenseRegistry_addPolicy_revert_frameworkNotFound() public { Licensing.Policy memory policy = _createPolicy(); vm.expectRevert(Errors.LicenseRegistry__FrameworkNotFound.selector); registry.addPolicy(policy); } - - function test_LicenseRegistry_addPolicyToIpId() - withFrameworkParams - public { + function test_LicenseRegistry_addPolicyToIpId() public withFrameworkParams { Licensing.Policy memory policy = _createPolicy(); (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); assertEq(policyId, 1, "policyId not 1"); assertEq(indexOnIpId, 0, "indexOnIpId not 0"); - assertFalse(registry.isPolicyIdAtIndexSetByLinking(ipId1, 0)); + assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); Licensing.Policy memory storedPolicy = registry.policy(policyId); assertEq(keccak256(abi.encode(storedPolicy)), keccak256(abi.encode(policy)), "policy not stored properly"); } - function test_LicenseRegistry_addSamePolicyReusesPolicyId() - withFrameworkParams - public { + function test_LicenseRegistry_addSamePolicyReusesPolicyId() public withFrameworkParams { Licensing.Policy memory policy = _createPolicy(); (uint256 policyId, bool isNew1, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); assertTrue(isNew1, "not new"); assertEq(indexOnIpId, 0); - assertFalse(registry.isPolicyIdAtIndexSetByLinking(ipId1, 0)); + assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); (uint256 policyId2, bool isNew2, uint256 indexOnIpId2) = registry.addPolicyToIp(ipId2, policy); assertFalse(isNew2, "new"); assertEq(indexOnIpId2, 0); - assertFalse(registry.isPolicyIdAtIndexSetByLinking(ipId2, 0)); + assertFalse(registry.isPolicySetByLinking(ipId2, policyId)); assertEq(policyId, policyId2, "policyId not reused"); } //function test_LicenseRegistry_revert_policyAlreadyAddedToIpId() - function test_LicenseRegistry_add2PoliciesToIpId() - withFrameworkParams - public { + function test_LicenseRegistry_add2PoliciesToIpId() public withFrameworkParams { assertEq(registry.totalPolicies(), 0); assertEq(registry.totalPoliciesForIp(ipId1), 0); Licensing.Policy memory policy = _createPolicy(); @@ -181,10 +153,10 @@ contract LicenseRegistryTest is Test { assertEq(registry.totalPolicies(), 1, "totalPolicies not incremented"); assertEq(registry.totalPoliciesForIp(ipId1), 1, "totalPoliciesForIp not incremented"); assertEq(registry.policyIdForIpAtIndex(ipId1, 0), 1, "policyIdForIpAtIndex not 1"); - assertFalse(registry.isPolicyIdAtIndexSetByLinking(ipId1, 0)); + assertFalse(registry.isPolicySetByLinking(ipId1, policyId)); // Adding different policy to same ipId - policy.mintingParamValues[0] = abi.encode("test2"); + policy.paramValues[0] = abi.encode("test2"); (uint256 policyId2, bool isNew2, uint256 indexOnIpId2) = registry.addPolicyToIp(ipId1, policy); assertTrue(isNew2, "not new"); assertEq(policyId2, 2, "policyId not 2"); @@ -192,12 +164,10 @@ contract LicenseRegistryTest is Test { assertEq(registry.totalPolicies(), 2, "totalPolicies not incremented"); assertEq(registry.totalPoliciesForIp(ipId1), 2, "totalPoliciesForIp not incremented"); assertEq(registry.policyIdForIpAtIndex(ipId1, 1), 2, "policyIdForIpAtIndex not 2"); - assertFalse(registry.isPolicyIdAtIndexSetByLinking(ipId1, 1)); + assertFalse(registry.isPolicySetByLinking(ipId1, policyId2)); } - function test_LicenseRegistry_mintLicense() - withFrameworkParams - public returns (uint256 licenseId) { + function test_LicenseRegistry_mintLicense() public withFrameworkParams returns (uint256 licenseId) { Licensing.Policy memory policy = _createPolicy(); // solhint-disable-next-line no-unused-vars (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); @@ -208,16 +178,13 @@ contract LicenseRegistryTest is Test { assertEq(policyIds.length, 1); assertEq(policyIds[indexOnIpId], policyId); - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = ipId1; - licenseId = registry.mintLicense(policyId, licensorIpIds, 2, licenseHolder); + licenseId = registry.mintLicense(policyId, ipId1, 2, licenseHolder); assertEq(licenseId, 1); Licensing.License memory license = registry.license(licenseId); assertEq(registry.balanceOf(licenseHolder, licenseId), 2); assertEq(registry.isLicensee(licenseId, licenseHolder), true); assertEq(license.policyId, policyId); - assertEq(license.licensorIpIds.length, 1); - assertEq(license.licensorIpIds[0], ipId1); + assertEq(license.licensorIpId, ipId1); return licenseId; } @@ -234,7 +201,7 @@ contract LicenseRegistryTest is Test { "policy not copied" ); assertEq(registry.policyIdForIpAtIndex(ipId2, 0), 1); - assertTrue(registry.isPolicyIdAtIndexSetByLinking(ipId2, 0), "not set by linking?"); + assertTrue(registry.isPolicySetByLinking(ipId2, 1)); address[] memory parents = registry.parentIpIds(ipId2); assertEq(parents.length, 1, "not 1 parent"); @@ -246,38 +213,34 @@ contract LicenseRegistryTest is Test { assertEq(parents[0], ipId1, "parent not ipId1"); } - - function test_LicenseRegistry_singleTransfer_paramVerifyTrue() - withFrameworkParams - public { + function test_LicenseRegistry_singleTransfer_paramVerifyTrue() public withFrameworkParams { Licensing.Policy memory policy = _createPolicy(); - // solhint-disable-next-line no-unused-vars - (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = ipId1; - uint256 licenseId = registry.mintLicense(policyId, licensorIpIds, 2, licenseHolder); + (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_revert_singleTransfer_transferParamVerifyFalse() - withFrameworkParams - public { - Licensing.Policy memory policy = _createPolicy(); - policy.transferParamValues[0] = abi.encode(false); - console2.logBytes(policy.transferParamValues[0]); - console2.log("policy.transferParamValues.length", policy.transferParamValues.length); - - // solhint-disable-next-line no-unused-vars - (uint256 policyId, bool isNew, uint256 indexOnIpId) = registry.addPolicyToIp(ipId1, policy); + function test_LicenseRegistry_revert_singleTransfer_transferParamVerifyFalse() public withFrameworkParams { + Licensing.Policy memory policy = Licensing.Policy({ + frameworkId: 1, + commercialUse: true, + derivatives: true, + paramNames: new bytes32[](2), + paramValues: new bytes[](2) + }); + 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); - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = ipId1; - uint256 licenseId = registry.mintLicense(policyId, licensorIpIds, 2, licenseHolder); + uint256 licenseId = registry.mintLicense(policyId, ipId1, 2, licenseHolder); address licenseHolder2 = address(0x102); vm.prank(licenseHolder); @@ -285,7 +248,7 @@ contract LicenseRegistryTest is Test { abi.encodeWithSelector( Errors.LicenseRegistry__ParamVerifierFailed.selector, uint8(Licensing.ParamVerifierType.Transfer), - address(verifier) + address(verifier2) ) ); registry.safeTransferFrom(licenseHolder, licenseHolder2, licenseId, 1, ""); diff --git a/test/foundry/integration/Integration.t.sol b/test/foundry/integration/Integration.t.sol index 7e4fc5637..03c074c8f 100644 --- a/test/foundry/integration/Integration.t.sol +++ b/test/foundry/integration/Integration.t.sol @@ -22,7 +22,7 @@ import { MockAccessController } from "test/foundry/mocks/MockAccessController.so import { MockERC20 } from "test/foundry/mocks/MockERC20.sol"; import { MockERC721 } from "test/foundry/mocks/MockERC721.sol"; import { MockModule } from "test/foundry/mocks/MockModule.sol"; -import { MockIParamVerifier } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; +import { MockParamVerifier, MockParamVerifierConfig } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; import { MintPaymentVerifier } from "test/foundry/mocks/licensing/MintPaymentVerifier.sol"; import { Users, UsersLib } from "test/foundry/utils/Users.sol"; @@ -35,20 +35,20 @@ struct MockERC721s { contract IntegrationTest is Test { using EnumerableSet for EnumerableSet.UintSet; - ERC6551Registry public erc6551Registry = new ERC6551Registry(); + ERC6551Registry internal erc6551Registry = new ERC6551Registry(); IPAccountImpl internal ipacctImpl; IPAccountRegistry internal ipacctRegistry; LicenseRegistry internal licenseRegistry; - DisputeModule public disputeModule; - ArbitrationPolicySP public arbitrationPolicySP; - RoyaltyModule public royaltyModule; - RoyaltyPolicyLS public royaltyPolicyLS; + DisputeModule internal disputeModule; + ArbitrationPolicySP internal arbitrationPolicySP; + RoyaltyModule internal royaltyModule; + RoyaltyPolicyLS internal royaltyPolicyLS; MockAccessController internal accessController = new MockAccessController(); MockERC20 internal mockToken = new MockERC20(); MockERC721s internal nft; - MockIParamVerifier public mockLicenseVerifier = new MockIParamVerifier(); + MockParamVerifier internal mockLicenseVerifier; MintPaymentVerifier internal mintPaymentVerifier; // MockModule internal module = new MockModule(address(registry), address(moduleRegistry), "MockModule"); @@ -100,6 +100,13 @@ contract IntegrationTest is Test { arbitrationPolicySP = new ArbitrationPolicySP(address(disputeModule), USDC, ARBITRATION_PRICE); royaltyModule = new RoyaltyModule(); royaltyPolicyLS = new RoyaltyPolicyLS(address(royaltyModule), LIQUID_SPLIT_FACTORY, LIQUID_SPLIT_MAIN); + mockLicenseVerifier = new MockParamVerifier(MockParamVerifierConfig({ + licenseRegistry: address(licenseRegistry), + name: "MockLicenseVerifier", + supportVerifyLink: true, + supportVerifyMint: true, + supportVerifyTransfer: true + })); nft = MockERC721s({ a: new MockERC721(), b: new MockERC721(), c: new MockERC721() }); u = UsersLib.createMockUsers(vm); @@ -111,7 +118,11 @@ contract IntegrationTest is Test { mockToken.mint(u.carl, 1000 * 10 ** mockToken.decimals()); // 1 mock token payment per mint - mintPaymentVerifier = new MintPaymentVerifier(address(mockToken), 1 * 10 ** mockToken.decimals()); + mintPaymentVerifier = new MintPaymentVerifier( + address(licenseRegistry), + address(mockToken), + 1 * 10 ** mockToken.decimals() + ); vm.label(address(disputeModule), "disputeModule"); vm.label(address(arbitrationPolicySP), "arbitrationPolicySP"); @@ -191,9 +202,7 @@ contract IntegrationTest is Test { uint256 amount ) public returns (uint256 licenseId) { (uint256 policyId, bool isNew, uint256 indexOnIpId) = attachPolicyToIPID(ipId, policyName); - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = ipId; - licenseId = licenseRegistry.mintLicense(policyId, licensorIpIds, amount, licensee); + licenseId = licenseRegistry.mintLicense(policyId, ipId, amount, licensee); licenseIds[licensee][policyId] = licenseId; } @@ -269,32 +278,20 @@ contract IntegrationTest is Test { // All trues for MockVerifier means it will always return true on condition checks bytes[] memory byteValueTrue = new bytes[](1); byteValueTrue[0] = abi.encode(true); - Licensing.FrameworkCreationParams memory fwAllTrue = Licensing.FrameworkCreationParams({ - mintingVerifiers: new IParamVerifier[](1), - mintingDefaultValues: byteValueTrue, - linkParentVerifiers: new IParamVerifier[](1), - linkParentDefaultValues: byteValueTrue, - transferVerifiers: new IParamVerifier[](1), - transferDefaultValues: byteValueTrue, + parameters: new IParamVerifier[](1), + defaultValues: byteValueTrue, licenseUrl: "https://very-nice-verifier-license.com" }); - - fwAllTrue.mintingVerifiers[0] = mockLicenseVerifier; - fwAllTrue.linkParentVerifiers[0] = mockLicenseVerifier; - fwAllTrue.transferVerifiers[0] = mockLicenseVerifier; + fwAllTrue.parameters[0] = mockLicenseVerifier; Licensing.FrameworkCreationParams memory fwMintPayment = Licensing.FrameworkCreationParams({ - mintingVerifiers: new IParamVerifier[](1), - mintingDefaultValues: byteValueTrue, // value here doesn't matter for MintPaymentVerifier - linkParentVerifiers: new IParamVerifier[](0), - linkParentDefaultValues: new bytes[](0), - transferVerifiers: new IParamVerifier[](0), - transferDefaultValues: new bytes[](0), + parameters: new IParamVerifier[](1), + defaultValues: byteValueTrue, licenseUrl: "https://expensive-minting-license.com" }); - fwMintPayment.mintingVerifiers[0] = mintPaymentVerifier; + fwMintPayment.parameters[0] = mintPaymentVerifier; addLicenseFramework("all_true", fwAllTrue); addLicenseFramework("mint_payment", fwMintPayment); @@ -305,23 +302,27 @@ contract IntegrationTest is Test { policy["test_true"] = Licensing.Policy({ frameworkId: licenseFwIds["all_true"], - mintingParamValues: new bytes[](1), - linkParentParamValues: new bytes[](1), - transferParamValues: new bytes[](1) + commercialUse: true, + derivatives: true, + paramNames: new bytes32[](1), + paramValues: new bytes[](1) }); - // TODO: test failure (verifier condition check fails) by setting to false - policy["test_true"].mintingParamValues[0] = abi.encode(true); - policy["test_true"].linkParentParamValues[0] = abi.encode(true); - policy["test_true"].transferParamValues[0] = abi.encode(true); + policy["test_true"].paramNames[0] = mockLicenseVerifier.name(); + policy["test_true"].paramValues[0] = abi.encode(true); policy["expensive_mint"] = Licensing.Policy({ frameworkId: licenseFwIds["mint_payment"], - mintingParamValues: new bytes[](1), // empty value to use default value, which doesn't matter - linkParentParamValues: new bytes[](0), - transferParamValues: new bytes[](0) + commercialUse: true, + derivatives: true, + paramNames: new bytes32[](1), + paramValues: new bytes[](1) }); + policy["expensive_mint"].paramNames[0] = mintPaymentVerifier.name(); + // value doesn't matter bc mintPaymentVerifier ignores data (just requires payment) + policy["expensive_mint"].paramValues[0] = abi.encode(true); + licenseRegistry.addPolicy(policy["test_true"]); licenseRegistry.addPolicy(policy["expensive_mint"]); @@ -335,15 +336,13 @@ contract IntegrationTest is Test { /*/////////////////////////////////////////////////////////////// MINT LICENSES ON POLICIES ////////////////////////////////////////////////////////////////*/ - + uint256 policyId = policyIds["test_true"][getIpId(u.alice, nft.a, 1)]; - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = getIpId(u.alice, nft.a, 1); // Carl mints 1 license for policy "test_true" on alice's NFT A IPAccount licenseIds[u.carl][policyIds["test_true"][getIpId(u.alice, nft.a, 1)]] = licenseRegistry.mintLicense( policyId, - licensorIpIds, + getIpId(u.alice, nft.a, 1), 1, u.carl ); @@ -385,21 +384,21 @@ contract IntegrationTest is Test { mockToken.approve(address(mintPaymentVerifier), 2 * 10 ** mockToken.decimals()); vm.stopPrank(); - policyId = policyIds["expensive_mint"][getIpId(u.carl, nft.c, 1)]; - licensorIpIds = new address[](1); - licensorIpIds[0] = getIpId(u.carl, nft.c, 1); + uint256 policyId_Carl_NftC_Id1 = policyIds["expensive_mint"][getIpId(u.carl, nft.c, 1)]; // Bob mints 2 license for policy "expensive_mint" on carl's NFT C IPAccount mockTokenBalanceBefore[u.bob] = mockToken.balanceOf(u.bob); mockTokenBalanceBefore[address(mintPaymentVerifier)] = mockToken.balanceOf(address(mintPaymentVerifier)); - licenseIds[u.bob][policyIds["expensive_mint"][getIpId(u.carl, nft.c, 1)]] = licenseRegistry.mintLicense( - policyId, - licensorIpIds, + vm.startPrank(u.bob); + licenseIds[u.bob][policyId_Carl_NftC_Id1] = licenseRegistry.mintLicense( + policyId_Carl_NftC_Id1, + getIpId(u.carl, nft.c, 1), // licensorIpId 2, u.bob ); + vm.stopPrank(); mockTokenBalanceAfter[u.bob] = mockToken.balanceOf(u.bob); mockTokenBalanceAfter[address(mintPaymentVerifier)] = mockToken.balanceOf(address(mintPaymentVerifier)); @@ -407,11 +406,7 @@ contract IntegrationTest is Test { // Bob activates above license on his NFT A IPAccount, linking as child to Carl's NFT C IPAccount vm.prank(u.bob); - licenseRegistry.linkIpToParent( - licenseIds[u.bob][policyIds["expensive_mint"][getIpId(u.carl, nft.c, 1)]], - getIpId(u.bob, nft.a, 2), - u.bob - ); + licenseRegistry.linkIpToParent(licenseIds[u.bob][policyId_Carl_NftC_Id1], getIpId(u.bob, nft.a, 2), u.bob); assertEq( licenseRegistry.balanceOf(u.bob, licenseIds[u.bob][policyIds["expensive_mint"][getIpId(u.carl, nft.c, 1)]]), @@ -427,12 +422,12 @@ contract IntegrationTest is Test { assertEq( mockTokenBalanceBefore[u.bob] - mockTokenBalanceAfter[u.bob], - 2 * 10 ** mockToken.decimals(), + 2 * mintPaymentVerifier.payment(), "Bob didn't pay Carl" ); assertEq( mockTokenBalanceAfter[address(mintPaymentVerifier)] - mockTokenBalanceBefore[address(mintPaymentVerifier)], - 2 * 10 ** mockToken.decimals(), + 2 * mintPaymentVerifier.payment(), "Carl didn't receive payment" ); diff --git a/test/foundry/mocks/licensing/MintPaymentVerifier.sol b/test/foundry/mocks/licensing/MintPaymentVerifier.sol index d5e15bbcf..2eab59d8e 100644 --- a/test/foundry/mocks/licensing/MintPaymentVerifier.sol +++ b/test/foundry/mocks/licensing/MintPaymentVerifier.sol @@ -1,25 +1,55 @@ // SPDX-License-Identifier: UNLICENSED 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 { 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 MintPaymentVerifier is IParamVerifier { IERC20 public token; uint256 public payment; - constructor(address _token, uint256 _payment) { + constructor( + address licenseRegistry, + address _token, + uint256 _payment + ) BaseParamVerifier(licenseRegistry, "MintPaymentVerifier") { token = IERC20(_token); payment = _payment; } + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IParamVerifier).interfaceId || + interfaceId == type(IMintParamVerifier).interfaceId || + super.supportsInterface(interfaceId); + } + /// @dev Mock verifies the param by decoding it as a bool. If you want the verifier /// to return true, pass in abi.encode(true) as the value. - function verifyMinting(address caller, uint256 mintAmount, bytes memory) external returns (bool) { + function verifyMint( + address caller, + uint256 policyId, + bool policyAddedByLinking, + address licensors, + address receiver, + uint256 mintAmount, + bytes memory data + ) external returns (bool) { // TODO: return false on approval or transfer failure - require(token.allowance(caller, address(this)) >= payment * mintAmount, "MintPaymentVerifier: Approval"); - require(token.transferFrom(caller, address(this), payment * mintAmount), "MintPaymentVerifier: Transfer"); + uint256 payment_ = mintAmount * payment; + require(token.allowance(caller, address(this)) >= payment_, "MintPaymentVerifier: Approval"); + require(token.transferFrom(caller, address(this), payment_), "MintPaymentVerifier: Transfer"); return true; } @@ -31,11 +61,11 @@ contract MintPaymentVerifier is IParamVerifier { return true; } - function verifyActivation(address, bytes memory) external pure returns (bool) { - return true; - } + function verifyActivation(address, bytes memory) external pure returns (bool) { + return true; + } - function json() external pure returns (string memory) { - return ""; + function allowsOtherPolicyOnSameIp(bytes memory data) external pure returns (bool) { + return true; } } diff --git a/test/foundry/mocks/licensing/MockParamVerifier.sol b/test/foundry/mocks/licensing/MockParamVerifier.sol index 3c666d1fc..08140ef35 100644 --- a/test/foundry/mocks/licensing/MockParamVerifier.sol +++ b/test/foundry/mocks/licensing/MockParamVerifier.sol @@ -1,26 +1,71 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; +// external +import { ERC165, IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +// 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 { BaseParamVerifier } from "contracts/modules/licensing/parameters/BaseParamVerifier.sol"; -contract MockIParamVerifier is IParamVerifier { - function verifyMinting(address, uint256, bytes memory value) external pure returns (bool) { - return abi.decode(value, (bool)); +struct MockParamVerifierConfig { + address licenseRegistry; + string name; + bool supportVerifyLink; + bool supportVerifyMint; + bool supportVerifyTransfer; +} + +contract MockParamVerifier is + ERC165, + BaseParamVerifier, + ILinkParamVerifier, + IMintParamVerifier, + ITransferParamVerifier +{ + MockParamVerifierConfig internal config; + + constructor(MockParamVerifierConfig memory conf) BaseParamVerifier(conf.licenseRegistry, conf.name) { + config = conf; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) 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; + if (interfaceId == type(ITransferParamVerifier).interfaceId) return config.supportVerifyTransfer; + return super.supportsInterface(interfaceId); } - function verifyLinkParent(address, bytes memory value) external pure returns (bool) { - return abi.decode(value, (bool)); + function allowsOtherPolicyOnSameIp(bytes memory data) external pure override returns (bool) { + return true; } - function verifyActivation(address, bytes memory value) external pure returns (bool) { - return abi.decode(value, (bool)); + function verifyMint( + address, + uint256, + bool, + address, + address, + uint256, + bytes memory data + ) external pure returns (bool) { + return abi.decode(data, (bool)); } - function verifyTransfer(address, uint256, bytes memory value) external pure returns (bool) { - return abi.decode(value, (bool)); + function verifyLink(uint256, address, address, address, bytes calldata data) external pure override returns (bool) { + return abi.decode(data, (bool)); } - function json() external pure returns (string memory) { - return ""; + function verifyTransfer( + uint256, + address, + address, + uint256, + bytes memory data + ) external pure override returns (bool) { + return abi.decode(data, (bool)); } } diff --git a/test/foundry/modules/RegistrationModule.t.sol b/test/foundry/modules/RegistrationModule.t.sol index 364fedf98..9b1ac7533 100644 --- a/test/foundry/modules/RegistrationModule.t.sol +++ b/test/foundry/modules/RegistrationModule.t.sol @@ -21,7 +21,7 @@ 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 { MockIParamVerifier } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; +import { MockParamVerifier, MockParamVerifierConfig } from "test/foundry/mocks/licensing/MockParamVerifier.sol"; import { Licensing } from "contracts/lib/Licensing.sol"; import { IP } from "contracts/lib/IP.sol"; import { Errors } from "contracts/lib/Errors.sol"; @@ -143,9 +143,8 @@ contract RegistrationModuleTest is ModuleBaseTest { ); // Mint license - address[] memory licensorIpIds = new address[](1); - licensorIpIds[0] = ipId; - uint256 licenseId = licenseRegistry.mintLicense(policyId, licensorIpIds, 1, bob); + address licensorIpId = ipId; + uint256 licenseId = licenseRegistry.mintLicense(policyId, licensorIpId, 1, bob); uint256 totalSupply = ipRecordRegistry.totalSupply(); @@ -212,34 +211,35 @@ contract RegistrationModuleTest is ModuleBaseTest { // TODO: put this in the base test function _initLicensing() private { IParamVerifier[] memory mintingVerifiers = new IParamVerifier[](1); - MockIParamVerifier verifier = new MockIParamVerifier(); + 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 linkParentVerifiers = new IParamVerifier[](1); - linkParentVerifiers[0] = verifier; - bytes[] memory linkParentDefaultValues = new bytes[](1); - linkParentDefaultValues[0] = abi.encode(true); + + IParamVerifier[] memory parameters; + bytes[] memory defaultValues; Licensing.FrameworkCreationParams memory fwParams = Licensing.FrameworkCreationParams({ - mintingVerifiers: mintingVerifiers, - mintingDefaultValues: mintingDefaultValues, - linkParentVerifiers: linkParentVerifiers, - linkParentDefaultValues: linkParentDefaultValues, - transferVerifiers: new IParamVerifier[](0), - transferDefaultValues: new bytes[](0), + parameters: mintingVerifiers, + defaultValues: mintingDefaultValues, licenseUrl: "https://example.com" }); licenseRegistry.addLicenseFramework(fwParams); Licensing.Policy memory policy = Licensing.Policy({ frameworkId: 1, - mintingParamValues: new bytes[](1), - linkParentParamValues: new bytes[](1), - transferParamValues: new bytes[](1) + commercialUse: true, + derivatives: true, + paramNames: new bytes32[](1), + paramValues: new bytes[](1) }); - policy.mintingParamValues[0] = abi.encode(true); - policy.linkParentParamValues[0] = abi.encode(true); - policy.transferParamValues[0] = abi.encode(true); + policy.paramNames[0] = verifier.name(); + policy.paramValues[0] = abi.encode(true); (uint256 polId) = licenseRegistry.addPolicy(policy); policyId = polId;