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

Commit

Permalink
feat: Internalize setRoyaltyPolicy call into LicenseRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
jdubpark committed Feb 3, 2024
1 parent 752c003 commit 0c740d6
Show file tree
Hide file tree
Showing 18 changed files with 357 additions and 155 deletions.
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: policy.derivativesAllowed,
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
98 changes: 72 additions & 26 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,15 +185,18 @@ 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++) {
uint256 licenseId = licenseIds[i];
if (balanceOf(holder, licenseId) == 0) {
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++) {
if (balanceOf(holder, licenseIds[i]) == 0) {
revert Errors.LicenseRegistry__NotLicensee();
}
Licensing.License memory licenseData = _licenses[licenseId];
Licensing.License memory licenseData = _licenses[licenseIds[i]];
licensors[i] = licenseData.licensorIpId;
// TODO: check licensor not part of a branch tagged by disputer
if (licensors[i] == childIpId) {
Expand All @@ -197,25 +205,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(licenseIds[i], 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 +412,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
20 changes: 11 additions & 9 deletions script/foundry/deployment/Main.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,18 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
ipAssetRegistry = new IPAssetRegistry(address(accessController), ERC6551_REGISTRY, address(implementation));
_postdeploy(contractKey, address(ipAssetRegistry));

contractKey = "RoyaltyModule";
_predeploy(contractKey);
royaltyModule = new RoyaltyModule();
_postdeploy(contractKey, address(royaltyModule));

contractKey = "LicenseRegistry";
_predeploy(contractKey);
licenseRegistry = new LicenseRegistry(address(accessController), address(ipAssetRegistry));
licenseRegistry = new LicenseRegistry(
address(accessController),
address(ipAssetRegistry),
address(royaltyModule)
);
_postdeploy(contractKey, address(licenseRegistry));

contractKey = "IPResolver";
Expand All @@ -156,11 +165,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
taggingModule = new TaggingModule();
_postdeploy(contractKey, address(taggingModule));

contractKey = "RoyaltyModule";
_predeploy(contractKey);
royaltyModule = new RoyaltyModule();
_postdeploy(contractKey, address(royaltyModule));

contractKey = "RoyaltyPolicyLS";
_predeploy(contractKey);
royaltyPolicyLS = new RoyaltyPolicyLS(
Expand Down Expand Up @@ -297,7 +301,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
address(accessController),
address(ipAssetRegistry),
address(licenseRegistry),
address(royaltyModule),
"UML_ALL_TRUE",
"https://very-nice-verifier-license.com/{id}.json"
);
Expand All @@ -306,7 +309,6 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
address(accessController),
address(ipAssetRegistry),
address(licenseRegistry),
address(royaltyModule),
"UML_MINT_PAYMENT",
"https://expensive-minting-license.com/{id}.json"
);
Expand Down Expand Up @@ -389,7 +391,7 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
// /*///////////////////////////////////////////////////////////////
// LINK IPACCOUNTS TO PARENTS USING LICENSES
// ////////////////////////////////////////////////////////////////*/

licenseRegistry.linkIpToParents(licenseIds, getIpId(mockNft, nftIds[4]), deployer);
}

Expand Down
Loading

0 comments on commit 0c740d6

Please sign in to comment.