Skip to content
This repository has been archived by the owner on Apr 30, 2024. It is now read-only.

Internalize setRoyaltyPolicy call into LicenseRegistry #65

Merged
merged 2 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion contracts/interfaces/licensing/ILinkParamVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.
/// @notice LicenseRegistry will call this to verify the linking an IP to its parent
/// with the policy referenced by the license in use.
interface ILinkParamVerifier is IPolicyVerifier {
struct VerifyLinkResponse {
bool isLinkingAllowed;
bool isRoyaltyRequired;
address royaltyPolicy;
uint32 royaltyDerivativeRevShare;
}

function verifyLink(
uint256 licenseId,
address caller,
address ipId,
address parentIpId,
bytes calldata policyData
) external returns (bool);
) external returns (VerifyLinkResponse memory);
}
8 changes: 6 additions & 2 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ interface ILicenseRegistry {
/// @param caller The address that called the function
/// @param ipId The id of the IP
/// @param parentIpIds The ids of the parent IP
event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] indexed parentIpIds);
event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] parentIpIds);

/// @notice Registers a policy framework manager into the contract, so it can add policy data for
/// licenses.
Expand Down Expand Up @@ -94,7 +94,11 @@ interface ILicenseRegistry {
/// @param licenseIds The id of the licenses to burn
/// @param childIpId The id of the child IP to be linked
/// @param holder The address that holds the license
function linkIpToParents(uint256[] calldata licenseIds, address childIpId, address holder) external;
function linkIpToParents(
uint256[] calldata licenseIds,
address childIpId,
address holder
) external;

///
/// Getters
Expand Down
1 change: 1 addition & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ library Errors {
error LicenseRegistry__LicensorNotRegistered();
error LicenseRegistry__CallerNotLicensorAndPolicyNotSet();
error LicenseRegistry__DerivativesCannotAddPolicy();
error LicenseRegistry__IncompatibleLicensorRoyaltyPolicy();

////////////////////////////////////////////////////////////////////////////
// LicenseRegistryAware //
Expand Down
39 changes: 16 additions & 23 deletions contracts/modules/licensing/UMLPolicyFrameworkManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ import { ITransferParamVerifier } from "contracts/interfaces/licensing/ITransfer
import { IUMLPolicyFrameworkManager, UMLPolicy, UMLRights } from "contracts/interfaces/licensing/IUMLPolicyFrameworkManager.sol";
import { IPolicyFrameworkManager } from "contracts/interfaces/licensing/IPolicyFrameworkManager.sol";
import { BasePolicyFrameworkManager } from "contracts/modules/licensing/BasePolicyFrameworkManager.sol";
import { IRoyaltyModule } from "contracts/interfaces/modules/royalty/IRoyaltyModule.sol";
import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol";
import { LicensorApprovalChecker } from "contracts/modules/licensing/parameter-helpers/LicensorApprovalChecker.sol";


/// @title UMLPolicyFrameworkManager
/// @notice This is the UML Policy Framework Manager, which implements the UML Policy Framework
/// logic for encoding and decoding UML policies into the LicenseRegistry and verifying
Expand All @@ -36,21 +33,16 @@ contract UMLPolicyFrameworkManager is
ITransferParamVerifier,
LicensorApprovalChecker
{
IRoyaltyModule public immutable ROYALTY_MODULE;

constructor(
address accessController,
address ipAccountRegistry,
address licRegistry,
address royaltyModule,
string memory name_,
string memory licenseUrl_
)
BasePolicyFrameworkManager(licRegistry, name_, licenseUrl_)
LicensorApprovalChecker(accessController, ipAccountRegistry)
{
ROYALTY_MODULE = IRoyaltyModule(royaltyModule);
}
{}

function licenseRegistry()
external
Expand Down Expand Up @@ -80,11 +72,17 @@ contract UMLPolicyFrameworkManager is
uint256 licenseId,
address, // caller
address ipId,
address parentIpId,
address, // parentIpId
bytes calldata policyData
) external override onlyLicenseRegistry returns (bool) {
) external override onlyLicenseRegistry returns (ILinkParamVerifier.VerifyLinkResponse memory) {
UMLPolicy memory policy = abi.decode(policyData, (UMLPolicy));
bool linkingOK = true;
ILinkParamVerifier.VerifyLinkResponse memory response = ILinkParamVerifier.VerifyLinkResponse({
isLinkingAllowed: true, // If you successfully mint and now hold a license, you have the right to link.
isRoyaltyRequired: false,
royaltyPolicy: address(0),
royaltyDerivativeRevShare: 0
});

// If the policy defines commercial revenue sharing, call the royalty module
// to set it for the licensor
if (policy.commercialRevShare > 0) {
Expand All @@ -94,22 +92,17 @@ contract UMLPolicyFrameworkManager is
// to set it for the licensor in future derivatives
if (policy.derivativesRevShare > 0) {
// RoyaltyModule.setRevShareForDerivatives()
address[] memory parentIpIds = new address[](1);
parentIpIds[0] = parentIpId;

ROYALTY_MODULE.setRoyaltyPolicy(
ipId,
policy.royaltyPolicy,
parentIpIds,
abi.encode(policy.derivativesRevShare) // uint32
);
response.isRoyaltyRequired = true;
response.royaltyPolicy = policy.royaltyPolicy;
response.royaltyDerivativeRevShare = policy.derivativesRevShare;
}
// If the policy defines the licensor must approve derivatives, check if the
// derivative is approved by the licensor
if (policy.derivativesApproval) {
linkingOK = linkingOK && isDerivativeApproved(licenseId, ipId);
response.isLinkingAllowed = response.isLinkingAllowed && isDerivativeApproved(licenseId, ipId);
}
return linkingOK;

return response;
}

/// Called by licenseRegistry to verify policy parameters for minting a license
Expand Down
8 changes: 4 additions & 4 deletions contracts/modules/royalty-module/RoyaltyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Errors } from "contracts/lib/Errors.sol";
/// and pay royalties as a derivative ip.
contract RoyaltyModule is IRoyaltyModule, Governable, ReentrancyGuard {
/// @notice registration module address
address public immutable REGISTRATION_MODULE;
address public REGISTRATION_MODULE;

/// @notice Indicates if a royalty policy is whitelisted
mapping(address royaltyPolicy => bool allowed) public isWhitelistedRoyaltyPolicy;
Expand All @@ -32,11 +32,11 @@ contract RoyaltyModule is IRoyaltyModule, Governable, ReentrancyGuard {
}

/// @notice Constructor
/// @param _registrationModule The address of the registration module
/// @param _governance The address of the governance contract
constructor(address _registrationModule, address _governance) Governable(_governance) {
if (_registrationModule == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule();
constructor(address _governance) Governable(_governance) {}

function initialize(address _registrationModule) external onlyProtocolAdmin {
if (_registrationModule == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule();
REGISTRATION_MODULE = _registrationModule;
}

Expand Down
94 changes: 71 additions & 23 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ 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 { AccessControlled } from "contracts/access/AccessControlled.sol";
import { IPolicyVerifier } from "contracts/interfaces/licensing/IPolicyVerifier.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";
import { IPolicyFrameworkManager } from "contracts/interfaces/licensing/IPolicyFrameworkManager.sol";
import { IAccessController } from "contracts/interfaces/IAccessController.sol";
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { Errors } from "contracts/lib/Errors.sol";
import { Licensing } from "contracts/lib/Licensing.sol";
import { IPAccountChecker } from "contracts/lib/registries/IPAccountChecker.sol";
import { AccessControlled } from "contracts/access/AccessControlled.sol";
import { RoyaltyModule } from "contracts/modules/royalty-module/RoyaltyModule.sol";

// TODO: consider disabling operators/approvals on creation
contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
Expand All @@ -39,6 +39,8 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
bool isInherited;
}

RoyaltyModule public immutable ROYALTY_MODULE;

mapping(address framework => bool registered) private _registeredFrameworkManagers;
mapping(bytes32 policyHash => uint256 policyId) private _hashedPolicies;
mapping(uint256 policyId => Licensing.Policy policyData) private _policies;
Expand All @@ -58,8 +60,11 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {

constructor(
address accessController,
address ipAccountRegistry
) ERC1155("") AccessControlled(accessController, ipAccountRegistry) {}
address ipAccountRegistry,
address royaltyModule
) ERC1155("") AccessControlled(accessController, ipAccountRegistry) {
ROYALTY_MODULE = RoyaltyModule(royaltyModule);
}

/// @notice registers a policy framework manager into the contract, so it can add policy data for
/// licenses.
Expand Down Expand Up @@ -89,7 +94,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
if (!isPolicyDefined(polId)) {
revert Errors.LicenseRegistry__PolicyNotFound();
}
return _addPolicyIdToIp(ipId, polId, false);
return _addPolicyIdToIp({ ipId: ipId, policyId: polId, isInherited: false, skipIfDuplicate: false });
}

/// @notice Registers a policy into the contract. MUST be called by a registered
Expand Down Expand Up @@ -180,14 +185,19 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
address childIpId,
address holder
) external verifyPermission(childIpId) {
uint256 licenses = licenseIds.length;
address[] memory licensors = new address[](licenses);
uint256[] memory values = new uint256[](licenses);
for (uint256 i = 0; i < licenses; i++) {
address[] memory licensors = new address[](licenseIds.length);
uint256[] memory values = new uint256[](licenseIds.length);
// If royalty policy address is address(0), this means no royalty policy to set.
// When a child passes in a royalty policy
address royaltyPolicyAddress = address(0);
uint32 royaltyDerivativeRevShare = 0;

for (uint256 i = 0; i < licenseIds.length; i++) {
jdubpark marked this conversation as resolved.
Show resolved Hide resolved
uint256 licenseId = licenseIds[i];
if (balanceOf(holder, licenseId) == 0) {
revert Errors.LicenseRegistry__NotLicensee();
}

Licensing.License memory licenseData = _licenses[licenseId];
licensors[i] = licenseData.licensorIpId;
// TODO: check licensor not part of a branch tagged by disputer
Expand All @@ -197,25 +207,54 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
// Verify linking params
Licensing.Policy memory pol = policy(licenseData.policyId);
if (ERC165Checker.supportsInterface(pol.policyFramework, type(ILinkParamVerifier).interfaceId)) {
if (
!ILinkParamVerifier(pol.policyFramework).verifyLink(
licenseId,
msg.sender,
childIpId,
licensors[i],
pol.data
)
) {
ILinkParamVerifier.VerifyLinkResponse memory response = ILinkParamVerifier(pol.policyFramework)
.verifyLink(licenseId, msg.sender, childIpId, licensors[i], pol.data);

if (!response.isLinkingAllowed) {
revert Errors.LicenseRegistry__LinkParentParamFailed();
}

// Compatibility check: If link says no royalty is required for license (licenseIds[i]) but
// another license requires royalty, revert.
if (!response.isRoyaltyRequired && royaltyPolicyAddress != address(0)) {
revert Errors.LicenseRegistry__IncompatibleLicensorRoyaltyPolicy();
}

// If link says royalty is required for license (licenseIds[i]) and no royalty policy is set, set it.
// But if the index is NOT 0, this is previous licenses didn't set the royalty policy because they don't
// require royalty payment. So, revert in this case.
if (response.isRoyaltyRequired && royaltyPolicyAddress == address(0)) {
if (i != 0) {
revert Errors.LicenseRegistry__IncompatibleLicensorRoyaltyPolicy();
}
royaltyPolicyAddress = response.royaltyPolicy;
royaltyDerivativeRevShare = response.royaltyDerivativeRevShare;
}
}
// Add policy to kid
_addPolicyIdToIp(childIpId, licenseData.policyId, true);
// Add the policy of licenseIds[i] to the child. If the policy's already set from previous parents,
// then the addition will be skipped.
_addPolicyIdToIp({
ipId: childIpId,
policyId: licenseData.policyId,
isInherited: true,
skipIfDuplicate: true
});
// Set parent
_ipIdParents[childIpId].add(licensors[i]);
values[i] = 1;
}
emit IpIdLinkedToParents(msg.sender, childIpId, licensors);

// Licenses unanimously require royalty.
if (royaltyPolicyAddress != address(0)) {
ROYALTY_MODULE.setRoyaltyPolicy(
childIpId,
royaltyPolicyAddress,
licensors,
abi.encode(royaltyDerivativeRevShare)
);
}

// Burn licenses
_burnBatch(holder, licenseIds, values);
}
Expand Down Expand Up @@ -375,12 +414,21 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry, AccessControlled {
/// @param ipId the IP identifier
/// @param policyId id of the policy data
/// @param isInherited true if set in linkIpToParent, false otherwise
/// @param skipIfDuplicate if true, will skip if policyId is already set
/// @return index of the policy added to the set
function _addPolicyIdToIp(address ipId, uint256 policyId, bool isInherited) private returns (uint256 index) {
function _addPolicyIdToIp(
address ipId,
uint256 policyId,
bool isInherited,
bool skipIfDuplicate
) private returns (uint256 index) {
_verifyCanAddPolicy(policyId, ipId, isInherited);
// Try and add the policy into the set.
EnumerableSet.UintSet storage _pols = _policySetPerIpId(isInherited, ipId);
if (!_pols.add(policyId)) {
if (skipIfDuplicate) {
return _policySetups[ipId][policyId].index;
}
revert Errors.LicenseRegistry__PolicyAlreadySetForIpId();
}
index = _pols.length() - 1;
Expand Down
Loading
Loading