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

Commit

Permalink
Merge IPRecordRegistry and IPAccountRegistry into Unified IPAssetRegi…
Browse files Browse the repository at this point in the history
…stry with Permissionless Registration (#46)
  • Loading branch information
kingster-will authored Jan 31, 2024
1 parent da23522 commit b0aee07
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 720 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
// See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf
pragma solidity ^0.8.21;
pragma solidity ^0.8.23;

/// @title IP Record Registry Interface
interface IIPRecordRegistry {
import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";

/// @title Interface for IP Account Registry
/// @notice This interface manages the registration and tracking of IP Accounts
interface IIPAssetRegistry is IIPAccountRegistry {
/// @notice Emits when an IP is officially registered into the protocol.
/// @param ipId The canonical identifier for the IP.
/// @param chainId The chain identifier of where the IP resides.
Expand All @@ -21,129 +23,70 @@ interface IIPRecordRegistry {
address provider
);

/// @notice Emits when an IP account is created for an IP.
/// @param ipId The canonical identifier for the IP.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
event IPAccountSet(
address ipId,
uint256 indexed chainId,
address indexed tokenContract,
uint256 indexed tokenId
);

/// @notice Emits when an IP resolver is bound to an IP.
/// @param ipId The canonical identifier of the specified IP.
/// @param resolver The address of the new resolver bound to the IP.
event IPResolverSet(
address ipId,
address resolver
);
event IPResolverSet(address ipId, address resolver);

/// @notice Emits when a metadata provider is set for an IP.
/// @param ipId The canonical identifier of the specified IP.
/// @param metadataProvider Address of the metadata provider associated with the IP.
event MetadataProviderSet(
address ipId,
address metadataProvider
);
event MetadataProviderSet(address ipId, address metadataProvider);

/// @notice Registers an NFT as IP, creating a corresponding IP asset.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @param resolverAddr The address of the resolver to associate with the IP.
/// @param createAccount Whether to create an IP account in the process.
function register(
uint256 chainId,
address tokenContract,
uint256 tokenId,
address resolverAddr,
bool createAccount
) external returns (address);

/// @notice Sets the resolver for an IP based on its NFT attributes.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
function setResolver(uint256 chainId, address tokenContract, uint256 tokenId, address resolver) external;

/// @notice Gets the canonical IP identifier associated with an IP (NFT).
/// @dev This is the same as the address of the IP account bound to the IP.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @return The address of the associated IP account.
function ipId(
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address);
function ipId(uint256 chainId, address tokenContract, uint256 tokenId) external view returns (address);

/// @notice Checks whether an IP was registered based on its ID.
/// @param id The canonical identifier for the IP.
/// @return Whether the IP was registered into the protocol.
function isRegistered(
address id
) external view returns (bool);
function isRegistered(address id) external view returns (bool);

/// @notice Retrieves whether or not an IP was registered.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @return Whether the IP was registered into the protocol.
function isRegistered(
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (bool);
function isRegistered(uint256 chainId, address tokenContract, uint256 tokenId) external view returns (bool);

/// @notice Gets the resolver bound to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The IP resolver address if registered, else the zero address.
function resolver(
address id
) external view returns (address);
function resolver(address id) external view returns (address);

/// @notice Gets the resolver bound to an IP based on its NFT attributes.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @return The IP resolver address if registered, else the zero address.
function resolver(
uint256 chainId,
address tokenContract,
uint256 tokenId
) external view returns (address);
function resolver(uint256 chainId, address tokenContract, uint256 tokenId) external view returns (address);

/// @notice Gets the metadata provider linked to an IP based on its ID.
/// @param id The canonical identifier for the IP.
/// @return The metadata that was bound to this IP at creation time.
function metadataProvider(address id) external view returns (address);

/// @notice Registers an NFT as IP, creating a corresponding IP record.
/// @dev This is only callable by an authorized registration module.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
/// @param resolverAddr The address of the resolver to associate with the IP.
/// @param createAccount Whether to create an IP account in the process.
/// @param metadataProvider The metadata provider to associate with the IP.
function register(
uint256 chainId,
address tokenContract,
uint256 tokenId,
address resolverAddr,
bool createAccount,
address metadataProvider
) external returns (address);

/// @notice Creates the IP account for the specified IP.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
function createIPAccount(
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address);

/// @notice Sets the resolver for an IP based on its canonical ID.
/// @param id The canonical ID of the IP.
/// @param resolverAddr The address of the resolver being set.
function setResolver(address id, address resolverAddr) external;

/// @notice Sets the resolver for an IP based on its NFT attributes.
/// @dev This is only callable by an authorized registration module.
/// @param chainId The chain identifier of where the IP resides.
/// @param tokenContract The address of the IP.
/// @param tokenId The token identifier of the IP.
function setResolver(
uint256 chainId,
address tokenContract,
uint256 tokenId,
address resolver
) external;

}
27 changes: 19 additions & 8 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,34 @@ library Errors {
error Module_Unauthorized();

////////////////////////////////////////////////////////////////////////////
// IPRecordRegistry //
// IPAccountRegistry //
////////////////////////////////////////////////////////////////////////////
error IPAccountRegistry_InvalidIpAccountImpl();

/// @notice The IP record has already been registered.
error IPRecordRegistry_AlreadyRegistered();
////////////////////////////////////////////////////////////////////////////
// IPAssetRegistry //
////////////////////////////////////////////////////////////////////////////

/// @notice The IP asset has already been registered.
error IPAssetRegistry_AlreadyRegistered();

/// @notice The IP account has already been created.
error IPRecordRegistry_IPAccountAlreadyCreated();
error IPAssetRegistry_IPAccountAlreadyCreated();

/// @notice The IP record has not yet been registered.
error IPRecordRegistry_NotYetRegistered();
/// @notice The IP asset has not yet been registered.
error IPAssetRegistry_NotYetRegistered();

/// @notice The specified IP resolver is not valid.
error IPRecordRegistry_ResolverInvalid();
error IPAssetRegistry_ResolverInvalid();

/// @notice Caller not authorized to perform the IP registry function call.
error IPRecordRegistry_Unauthorized();
error IPAssetRegistry_Unauthorized();

/// @notice The deployed address of account doesn't match with IP ID.
error IPAssetRegistry_InvalidAccount();

/// @notice The metadata provider is not valid.
error IPAssetRegistry_InvalidMetadataProvider();

////////////////////////////////////////////////////////////////////////////
// IPResolver ///
Expand Down
17 changes: 6 additions & 11 deletions contracts/modules/BaseModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.23;

import { IModule } from "contracts/interfaces/modules/base/IModule.sol";
import { IAccessController } from "contracts/interfaces/IAccessController.sol";
import { IPRecordRegistry } from "contracts/registries/IPRecordRegistry.sol";
import { IPAssetRegistry } from "contracts/registries/IPAssetRegistry.sol";
import { IPAccountRegistry } from "contracts/registries/IPAccountRegistry.sol";
import { LicenseRegistry } from "contracts/registries/LicenseRegistry.sol";
import { IResolver } from "contracts/interfaces/resolvers/IResolver.sol";
Expand All @@ -18,11 +18,8 @@ abstract contract BaseModule is IModule {
/// @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 IP asset registry.
IPAssetRegistry public immutable IP_ASSET_REGISTRY;

/// @notice Gets the protocol-wide license registry.
LicenseRegistry public immutable LICENSE_REGISTRY;
Expand All @@ -35,14 +32,12 @@ abstract contract BaseModule is IModule {

/// @notice Initializes the base module contract.
/// @param controller The access controller used for IP authorization.
/// @param recordRegistry The address of the IP record registry.
/// @param accountRegistry The address of the IP account registry.
/// @param assetRegistry The address of the IP asset registry.
/// @param licenseRegistry The address of the license registry.
constructor(address controller, address recordRegistry, address accountRegistry, address licenseRegistry) {
constructor(address controller, address assetRegistry, address licenseRegistry) {
// TODO: Add checks for interface support or at least zero address
ACCESS_CONTROLLER = IAccessController(controller);
IP_RECORD_REGISTRY = IPRecordRegistry(recordRegistry);
IP_ACCOUNT_REGISTRY = IPAccountRegistry(accountRegistry);
IP_ASSET_REGISTRY = IPAssetRegistry(assetRegistry);
LICENSE_REGISTRY = LicenseRegistry(licenseRegistry);
}

Expand Down
44 changes: 10 additions & 34 deletions contracts/modules/RegistrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pragma solidity ^0.8.23;
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import { IPResolver } from "contracts/resolvers/IPResolver.sol";
import { IPMetadataProvider } from "contracts/registries/metadata/IPMetadataProvider.sol";
import { IRegistrationModule } from "contracts/interfaces/modules/IRegistrationModule.sol";
import { REGISTRATION_MODULE_KEY } from "contracts/lib/modules/Module.sol";
import { Errors } from "contracts/lib/Errors.sol";
Expand All @@ -21,26 +20,15 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
/// @notice The metadata resolver used by the registration module.
IPResolver public resolver;

/// @notice Metadata storage provider contract.
IPMetadataProvider public metadataProvider;

/// @notice Initializes the registration module contract.
/// @param controller The access controller used for IP authorization.
/// @param recordRegistry The address of the IP record registry.
/// @param accountRegistry The address of the IP account registry.
/// @param assetRegistry The address of the IP asset registry.
/// @param licenseRegistry The address of the license registry.
/// @param resolverAddr The address of the IP metadata resolver.
constructor(
address controller,
address recordRegistry,
address accountRegistry,
address licenseRegistry,
address resolverAddr,
address metadataProviderAddr
) BaseModule(controller, recordRegistry, accountRegistry, licenseRegistry) {
metadataProvider = IPMetadataProvider(metadataProviderAddr);
resolver = IPResolver(resolverAddr);
}
address assetRegistry,
address licenseRegistry
) BaseModule(controller, assetRegistry, licenseRegistry) {}

/// @notice Registers a root-level IP into the protocol. Root-level IPs can
/// be thought of as organizational hubs for encapsulating policies
Expand All @@ -65,14 +53,7 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
}

// Perform core IP registration and IP account creation.
address ipId = IP_RECORD_REGISTRY.register(
block.chainid,
tokenContract,
tokenId,
address(resolver),
true,
address(metadataProvider)
);
address ipId = IP_ASSET_REGISTRY.register(block.chainid, tokenContract, tokenId, address(resolver), true);

// Perform core IP policy creation.
if (policyId != 0) {
Expand Down Expand Up @@ -110,14 +91,7 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
revert Errors.RegistrationModule__InvalidOwner();
}

address ipId = IP_RECORD_REGISTRY.register(
block.chainid,
tokenContract,
tokenId,
address(resolver),
true,
address(metadataProvider)
);
address ipId = IP_ASSET_REGISTRY.register(block.chainid, tokenContract, tokenId, address(resolver), true);
// ACCESS_CONTROLLER.setPermission(
// ipId,
// address(this),
Expand All @@ -126,7 +100,8 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
// 1
// );

// Perform core IP registration and IP account creation.
// Perform core IP registration and IP account creation.
// solhint-disable-next-line no-unused-vars
bytes memory metadata = abi.encode(
IP.Metadata({
name: ipName,
Expand All @@ -136,7 +111,8 @@ contract RegistrationModule is BaseModule, IRegistrationModule {
uri: externalURL
})
);
metadataProvider.setMetadata(ipId, metadata);
// TODO: handle setting matadata
// metadataProvider.setMetadata(ipId, metadata);

// Perform core IP derivative licensing - the license must be owned by the caller.
// TODO: return resulting policy index
Expand Down
9 changes: 4 additions & 5 deletions contracts/registries/IPAccountRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.23;

import { IIPAccountRegistry } from "contracts/interfaces/registries/IIPAccountRegistry.sol";
import { IERC6551Registry } from "lib/reference/src/interfaces/IERC6551Registry.sol";
import { Errors } from "contracts/lib/Errors.sol";

/// @title IPAccountRegistry
/// @notice This contract is responsible for managing the registration and tracking of IP Accounts.
Expand All @@ -14,14 +15,12 @@ contract IPAccountRegistry is IIPAccountRegistry {
address public immutable ERC6551_PUBLIC_REGISTRY;
address public immutable ACCESS_CONTROLLER;

error NonExistIpAccountImpl();

/// @notice Constructor for the IPAccountRegistry contract.
/// @param erc6551Registry_ The address of the ERC6551 registry.
/// @param accessController_ The address of the access controller.
/// @param ipAccountImpl_ The address of the IP account implementation.
constructor(address erc6551Registry_, address accessController_, address ipAccountImpl_) {
if (ipAccountImpl_ == address(0)) revert NonExistIpAccountImpl();
if (ipAccountImpl_ == address(0)) revert Errors.IPAccountRegistry_InvalidIpAccountImpl();
IP_ACCOUNT_IMPL = ipAccountImpl_;
IP_ACCOUNT_SALT = bytes32(0);
ERC6551_PUBLIC_REGISTRY = erc6551Registry_;
Expand All @@ -37,7 +36,7 @@ contract IPAccountRegistry is IIPAccountRegistry {
uint256 chainId_,
address tokenContract_,
uint256 tokenId_
) external returns (address ipAccountAddress) {
) public returns (address ipAccountAddress) {
bytes memory initData = abi.encodeWithSignature("initialize(address)", ACCESS_CONTROLLER);
ipAccountAddress = IERC6551Registry(ERC6551_PUBLIC_REGISTRY).createAccount(
IP_ACCOUNT_IMPL,
Expand All @@ -60,7 +59,7 @@ contract IPAccountRegistry is IIPAccountRegistry {
/// @param tokenContract_ The address of the token contract associated with the IP Account.
/// @param tokenId_ The ID of the token associated with the IP Account.
/// @return The address of the IP Account associated with the given NFT token.
function ipAccount(uint256 chainId_, address tokenContract_, uint256 tokenId_) external view returns (address) {
function ipAccount(uint256 chainId_, address tokenContract_, uint256 tokenId_) public view returns (address) {
return _get6551AccountAddress(chainId_, tokenContract_, tokenId_);
}

Expand Down
Loading

0 comments on commit b0aee07

Please sign in to comment.