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

Multi license linking #56

Merged
merged 4 commits into from
Feb 1, 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
4 changes: 2 additions & 2 deletions contracts/interfaces/modules/IRegistrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.23;
interface IRegistrationModule {
event RootIPRegistered(address indexed caller, address indexed ipId, uint256 indexed policyId);

event DerivativeIPRegistered(address indexed caller, address indexed ipId, uint256 licenseId);
event DerivativeIPRegistered(address indexed caller, address indexed ipId, uint256[] licenseIds);

function registerRootIp(
uint256 policyId,
Expand All @@ -14,7 +14,7 @@ interface IRegistrationModule {
) external returns (address);

function registerDerivativeIp(
uint256 licenseId,
uint256[] calldata licenseIds,
address tokenContract,
uint256 tokenId,
string memory ipName,
Expand Down
13 changes: 7 additions & 6 deletions contracts/interfaces/registries/ILicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ interface ILicenseRegistry {
/// @notice Emitted when an IP is linked to its parent by burning a license
/// @param caller The address that called the function
/// @param ipId The id of the IP
/// @param parentIpId The id of the parent IP
event IpIdLinkedToParent(address indexed caller, address indexed ipId, address indexed parentIpId);
/// @param parentIpIds The ids of the parent IP
event IpIdLinkedToParents(address indexed caller, address indexed ipId, address[] indexed parentIpIds);

/// @notice Registers a policy framework manager into the contract, so it can add policy data for
/// licenses.
Expand Down Expand Up @@ -95,11 +95,12 @@ interface ILicenseRegistry {
address receiver
) external returns (uint256 licenseId);

/// @notice Links an IP to its parent IP, burning the license NFT and the policy allows it
/// @param licenseId The id of the license to burn
/// @param childIpId The id of the child IP
/// @notice Links an IP to the licensors (parent IP IDs) listed in the License NFTs, if their policies allow it,
/// burning the NFTs in the proccess. The caller must be the owner of the NFTs and the IP owner.
/// @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 linkIpToParent(uint256 licenseId, address childIpId, address holder) external;
function linkIpToParents(uint256[] calldata licenseIds, address childIpId, address holder) external;

///
/// Getters
Expand Down
11 changes: 5 additions & 6 deletions contracts/modules/RegistrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
return ipId;
}

/// @notice Registers an IP derivative into the protocol.
/// @param licenseId The license to incorporate for the new IP.
/// @notice Registers IP derivatives into the protocol.
/// @param licenseIds The licenses to incorporate for the new IP.
/// @param tokenContract The address of the NFT bound to the derivative IP.
/// @param tokenId The token id of the NFT bound to the derivative IP.
/// @param ipName The name assigned to the new IP.
Expand All @@ -92,7 +92,7 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
/// TODO: Replace all metadata with a generic bytes parameter type, and do
/// encoding on the periphery contract level instead.
function registerDerivativeIp(
uint256 licenseId,
uint256[] calldata licenseIds,
address tokenContract,
uint256 tokenId,
string memory ipName,
Expand Down Expand Up @@ -125,10 +125,9 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
);

// Perform core IP derivative licensing - the license must be owned by the caller.
// TODO: return resulting policy index
LICENSE_REGISTRY.linkIpToParent(licenseId, ipId, msg.sender);
LICENSE_REGISTRY.linkIpToParents(licenseIds, ipId, msg.sender);

emit DerivativeIPRegistered(msg.sender, ipId, licenseId);
emit DerivativeIPRegistered(msg.sender, ipId, licenseIds);
}

/// @notice Gets the protocol-wide module identifier for this module.
Expand Down
11 changes: 3 additions & 8 deletions contracts/registries/IPAssetRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract IPAssetRegistry is IIPAssetRegistry, IPAccountRegistry {
IMetadataProviderMigratable internal _metadataProvider;

/// @notice Ensures only protocol governance owner may call a function.
modifier onlyOwner {
modifier onlyOwner() {
if (msg.sender != owner) {
revert Errors.IPAssetRegistry__Unauthorized();
}
Expand Down Expand Up @@ -82,11 +82,7 @@ contract IPAssetRegistry is IIPAssetRegistry, IPAccountRegistry {
revert Errors.IPAssetRegistry__AlreadyRegistered();
}

if (
id.code.length == 0 &&
createAccount &&
id != registerIpAccount(chainId, tokenContract, tokenId)
) {
if (id.code.length == 0 && createAccount && id != registerIpAccount(chainId, tokenContract, tokenId)) {
revert Errors.IPAssetRegistry__InvalidAccount();
}
_setResolver(id, resolverAddr);
Expand Down Expand Up @@ -155,7 +151,7 @@ contract IPAssetRegistry is IIPAssetRegistry, IPAccountRegistry {
/// @param id The canonical ID of the IP.
/// @param data Canonical metadata to associate with the IP.
function setMetadata(address id, address provider, bytes calldata data) external {
// Metadata is set on registration and immutable thereafter, with new fields
// Metadata is set on registration and immutable thereafter, with new fields
// only added during a migration to new protocol-approved metadata provider.
if (address(_records[id].metadataProvider) != msg.sender) {
revert Errors.IPAssetRegistry__Unauthorized();
Expand Down Expand Up @@ -195,5 +191,4 @@ contract IPAssetRegistry is IIPAssetRegistry, IPAccountRegistry {
provider.setMetadata(id, data);
emit MetadataSet(id, address(provider), data);
}

}
103 changes: 47 additions & 56 deletions contracts/registries/LicenseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,6 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
/// This tracks the number of licenses registered in the protocol, it will not decrease when a license is burnt.
uint256 private _totalLicenses;

modifier onlyLicensee(uint256 licenseId, address holder) {
// Should ERC1155 operator count? IMO is a security risk. Better use ACL
if (balanceOf(holder, licenseId) == 0) {
revert Errors.LicenseRegistry__NotLicensee();
}
_;
}

constructor(address accessController, address ipAccountRegistry) ERC1155("") {
ACCESS_CONTROLLER = IAccessController(accessController);
IP_ACCOUNT_REGISTRY = IIPAccountRegistry(ipAccountRegistry);
Expand Down Expand Up @@ -106,7 +98,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
if (!isPolicyDefined(polId)) {
revert Errors.LicenseRegistry__PolicyNotFound();
}
return _addPolictyIdToIp(ipId, polId, false);
return _addPolicyIdToIp(ipId, polId, false);
}

/// @notice Registers a policy into the contract. MUST be called by a registered
Expand Down Expand Up @@ -191,58 +183,57 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
return licenseId;
}

/// Relates an IP ID with its parents (licensors), by burning the License NFT the holder owns
/// Licensing parameters related to linking IPAs must be verified in order to succeed, reverts otherwise.
/// The child IP ID will have the policy that the license represent added to it's own, if it's compatible with
/// existing child policies.
/// The child IP ID will be linked to the parent (if it wasn't before).
/// @param licenseId license NFT to be burned
/// @param childIpId that will receive the policy defined by licenseId
/// @param holder of the license NFT
function linkIpToParent(
uint256 licenseId,
address childIpId,
address holder
) external onlyLicensee(licenseId, holder) {
/// @notice Links an IP to the licensors (parent IP IDs) listed in the License NFTs, if their policies allow it,
/// burning the NFTs in the proccess. The caller must be the owner of the NFTs and the IP owner.
/// @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 {
// check the caller is owner or authorized by the childIp
if (
msg.sender != childIpId &&
!ACCESS_CONTROLLER.checkPermission(childIpId, msg.sender, address(this), this.linkIpToParent.selector)
!ACCESS_CONTROLLER.checkPermission(childIpId, msg.sender, address(this), this.linkIpToParents.selector)
) {
revert Errors.LicenseRegistry__UnauthorizedAccess();
}
// TODO: check if childIpId exists and is owned by holder
Licensing.License memory licenseData = _licenses[licenseId];
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);
if (ERC165Checker.supportsInterface(pol.policyFramework, type(ILinkParamVerifier).interfaceId)) {
if (
!ILinkParamVerifier(pol.policyFramework).verifyLink(
licenseId,
msg.sender,
childIpId,
parentIpId,
pol.data
)
) {
revert Errors.LicenseRegistry__LinkParentParamFailed();
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) {
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
if (licensors[i] == childIpId) {
revert Errors.LicenseRegistry__ParentIdEqualThanChild();
}
// 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
)
) {
revert Errors.LicenseRegistry__LinkParentParamFailed();
}
}
// Add policy to kid
_addPolicyIdToIp(childIpId, licenseData.policyId, true);
// Set parent
_ipIdParents[childIpId].add(licensors[i]);
values[i] = 1;
}
// Add policy to kid
_addPolictyIdToIp(childIpId, licenseData.policyId, true);
// Set parent
_ipIdParents[childIpId].add(parentIpId);
emit IpIdLinkedToParent(msg.sender, childIpId, parentIpId);

// Burn license
_burn(holder, licenseId, 1);
emit IpIdLinkedToParents(msg.sender, childIpId, licensors);
// Burn licenses
_burnBatch(holder, licenseIds, values);
}

/// @notice True if the framework address is registered in LicenseRegistry
Expand Down Expand Up @@ -373,7 +364,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
super._update(from, to, ids, values);
}

function _verifyRegisteredFramework(address policyFramework) internal view returns (address) {
function _verifyRegisteredFramework(address policyFramework) internal view {
if (!_registeredFrameworkManagers[policyFramework]) {
revert Errors.LicenseRegistry__FrameworkNotFound();
}
Expand All @@ -385,7 +376,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
/// @param policyId id of the policy data
/// @param inheritedPolicy true if set in linkIpToParent, false otherwise
/// @return index of the policy added to the set
function _addPolictyIdToIp(address ipId, uint256 policyId, bool inheritedPolicy) internal returns (uint256 index) {
function _addPolicyIdToIp(address ipId, uint256 policyId, bool inheritedPolicy) internal returns (uint256 index) {
EnumerableSet.UintSet storage _pols = _policiesPerIpId[ipId];
if (!_pols.add(policyId)) {
revert Errors.LicenseRegistry__PolicyAlreadySetForIpId();
Expand Down Expand Up @@ -431,7 +422,7 @@ contract LicenseRegistry is ERC1155, ILicenseRegistry {
return (id, true);
}

function _verifyPolicy(Licensing.Policy memory pol) private view {
function _verifyPolicy(Licensing.Policy memory pol) private pure {
if (pol.policyFramework == address(0)) {
revert Errors.LicenseRegistry__PolicyNotFound();
}
Expand Down
8 changes: 5 additions & 3 deletions script/foundry/deployment/Main.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,11 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
2,
deployer
);
uint256[] memory licenseIds = new uint256[](1);
licenseIds[0] = licenseId1;

registrationModule.registerDerivativeIp(
licenseId1,
licenseIds,
address(mockNft),
nftIds[3],
"best derivative ip",
Expand All @@ -368,8 +370,8 @@ contract Main is Script, BroadcastManager, JsonDeploymentHandler {
// /*///////////////////////////////////////////////////////////////
// LINK IPACCOUNTS TO PARENTS USING LICENSES
// ////////////////////////////////////////////////////////////////*/

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

function getIpId(MockERC721 mnft, uint256 tokenId) public view returns (address ipId) {
Expand Down
Loading
Loading