Skip to content

Commit

Permalink
Finalizes registration module (adds necessary FEs needed for SPG inte…
Browse files Browse the repository at this point in the history
…gration)
  • Loading branch information
leeren committed Nov 15, 2023
1 parent 11d8710 commit 1921a37
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 41 deletions.
90 changes: 88 additions & 2 deletions contracts/StoryProtocol.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { Errors } from "contracts/lib/Errors.sol";
import { IPOrgParams } from "contracts/lib/IPOrgParams.sol";
import { ModuleRegistry } from "contracts/modules/ModuleRegistry.sol";
import { LibRelationship } from "contracts/lib/modules/LibRelationship.sol";
import { Registration } from "contracts/lib/modules/Registration.sol";
import { ModuleRegistryKeys } from "contracts/lib/modules/ModuleRegistryKeys.sol";

contract StoryProtocol {
// TODO: should this be immutable, or should the protocol be able to change factory

IIPOrgController public immutable IP_ORG_CONTROLLER;
ModuleRegistry public immutable MODULE_REGISTRY;

Expand All @@ -26,18 +27,103 @@ contract StoryProtocol {
MODULE_REGISTRY = moduleRegistry_;
}

////////////////////////////////////////////////////////////////////////////
// IPOrg //
////////////////////////////////////////////////////////////////////////////

/// @notice Sets the metadata for an IP Org.
/// @param ipOrg_ The address of the IP Org being configured.
/// @param baseURI_ The base token metadata URI for the IP Org.
/// @param contractURI_ The contract URI associated with the IP Org.
function setMetadata(
address ipOrg_,
string calldata baseURI_,
string calldata contractURI_
) public {
bytes memory encodedParams = abi.encode(
Registration.SET_IP_ORG_METADATA,
abi.encode(baseURI_, contractURI_)
);
MODULE_REGISTRY.configure(
IIPOrg(ipOrg_),
msg.sender,
ModuleRegistryKeys.REGISTRATION_MODULE,
encodedParams
);
}

/// @notice Registers a new IP Org
/// @param owner_ The address of the IP Org to be registered.
/// @param name_ A name to associate with the IP Org.
/// @param symbol_ A symbol to associate with the IP Org.
/// TODO: Add module configurations to the IP Org registration process.
/// TODO: Add permissions for IP Org registration.
function registerIpOrg(
address owner_,
string calldata name_,
string calldata symbol_
) external returns (address) {
) external returns (address ipOrg_) {
return IP_ORG_CONTROLLER.registerIpOrg(
owner_,
name_,
symbol_
);
}

/// @notice Transfers an IP asset to another owner.
/// @param ipOrg_ The governing IP Org under which the IP asset is registered.
/// @param params_ The registration params, including owner, name, hash.
/// @param preHooksData_ Hooks to embed with the registration pre-call.
/// @param postHooksData_ Hooks to embed with the registration post-call.
/// @return The global IP asset and local IP Org asset id.
function registerIPAsset(
address ipOrg_,
Registration.RegisterIPAssetParams calldata params_,
bytes[] calldata preHooksData_,
bytes[] calldata postHooksData_
) public returns (uint256, uint256) {
bytes memory encodedParams = abi.encode(
Registration.REGISTER_IP_ASSET,
abi.encode(params_)
);
bytes memory result = MODULE_REGISTRY.execute(
IIPOrg(ipOrg_),
msg.sender,
ModuleRegistryKeys.REGISTRATION_MODULE,
abi.encode(params_),
preHooksData_,
postHooksData_
);
return abi.decode(result, (uint256, uint256));
}

/// @notice Transfers an IP asset to another owner.
/// @param ipOrg_ The IP Org which the IP asset is associated with.
/// @param from_ The address of the current owner of the IP asset.
/// @param to_ The address of the new owner of the IP asset.
/// @param ipAssetId_ The global id of the IP asset being transferred.
function transferIPAsset(
address ipOrg_,
address from_,
address to_,
uint256 ipAssetId_,
bytes[] calldata preHooksData_,
bytes[] calldata postHooksData_
) public {
bytes memory encodedParams = abi.encode(
Registration.TRANSFER_IP_ASSET,
abi.encode(from_, to_, ipAssetId_)
);
bytes memory result = MODULE_REGISTRY.execute(
IIPOrg(ipOrg_),
msg.sender,
ModuleRegistryKeys.REGISTRATION_MODULE,
encodedParams,
preHooksData_,
postHooksData_
);
}

////////////////////////////////////////////////////////////////////////////
// Relationships //
////////////////////////////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions contracts/ip-org/IPOrgController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ contract IPOrgController is
/// @param name_ The name to associated with the new IP Org.
/// @param symbol_ The symbol to associate with the new IP Org.
/// TODO: Add module configurations to the IP Org registration process.
/// TODO: Add authorization for IP Org registration.
function registerIpOrg(
address owner_,
string calldata name_,
Expand Down
6 changes: 6 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ library Errors {
/// @notice The registration module for the IP Org was not yet configured.
error RegistrationModule_IPOrgNotConfigured();

/// @notice The registration configuration action is not valid.
error RegistrationModule_InvalidConfigOperation();

/// @notice The registration execution action is not valid.
error RegistrationModule_InvalidExecutionOperation();

////////////////////////////////////////////////////////////////////////////
// RelationshipModule //
////////////////////////////////////////////////////////////////////////////
Expand Down
12 changes: 11 additions & 1 deletion contracts/lib/modules/Registration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ library Registration {
string contractURI;
}

struct RegisterIPAParams {
/// @notice Struct used for IP asset registration.
struct RegisterIPAssetParams {
address owner;
string name;
uint64 ipAssetType;
bytes32 hash;
}

// TODO(leeren): Change in favor of granular function-selector based auth.

// Constants used for determining module configuration logic.
bytes32 public constant SET_IP_ORG_METADATA = keccak256("SET_IP_ORG_METADATA");

// Constants used for determining module execution logic.
bytes32 public constant REGISTER_IP_ASSET = keccak256("REGISTER_IP_ASSET");
bytes32 public constant TRANSFER_IP_ASSET = keccak256("TRANSFER_IP_ASSET");

}
120 changes: 82 additions & 38 deletions contracts/modules/registration/RegistrationModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ contract RegistrationModule is BaseModule, IRegistrationModule, AccessControlled
/// @notice Maps global IP asset Ids to IP Org wrapped assets.
mapping(uint256 => IPOrgAsset) ipOrgAssets;

/// @notice Reverse mapping of IP Orgs to their IPA configuration settings.
/// @notice Maps IP Orgs to their IPA configuration settings.
mapping(address => Registration.IPOrgConfig) ipOrgConfigs;

/// @notice Reverse lookup from IP Org asset to GIPR asset ids.
/// @notice Reverse lookup from IP Org asset to global IP asset ids.
mapping(address => mapping(uint256 => uint256)) public ipAssetId;

/// @notice Initializes the registration module.
Expand Down Expand Up @@ -115,10 +115,20 @@ contract RegistrationModule is BaseModule, IRegistrationModule, AccessControlled
/// @param ipOrg_ IPOrg address or zero address for protocol level relationships
/// @param params_ encoded params for module action
function _verifyExecution(IIPOrg ipOrg_, address caller_, bytes calldata params_) virtual override internal {
Registration.RegisterIPAParams memory params = abi.decode(params_, (Registration.RegisterIPAParams));
(bytes32 executionType, bytes memory executionData) = abi.decode(params_, (bytes32, bytes));

if (params.owner != caller_) {
revert Errors.RegistrationModule_InvalidCaller();
if (executionType == Registration.TRANSFER_IP_ASSET) {
(address from, address to, uint256 id) = abi.decode(executionData, (address, address, uint256));
if (caller_ != from || ownerOf(id) != caller_) {
revert Errors.RegistrationModule_InvalidCaller();
}
} else if (executionType == Registration.REGISTER_IP_ASSET) {
Registration.RegisterIPAssetParams memory params = abi.decode(executionData, (Registration.RegisterIPAssetParams));
if (params.owner != caller_) {
revert Errors.RegistrationModule_InvalidCaller();
}
} else {
revert Errors.RegistrationModule_InvalidExecutionOperation();
}

// TODO(leeren): Perform additional vetting on name, IP type, and CID.
Expand All @@ -129,25 +139,31 @@ contract RegistrationModule is BaseModule, IRegistrationModule, AccessControlled
/// @param caller_ The caller authorized to perform configuration.
/// @param params_ Parameters passed for registration configuration.
function _configure(IIPOrg ipOrg_, address caller_, bytes calldata params_) virtual override internal {
if (ipOrg_.owner() != caller_) {
revert Errors.RegistrationModule_CallerNotAuthorized();
_verifyConfigCaller(ipOrg_, caller_);
(bytes32 configType, bytes memory configData) = abi.decode(params_, (bytes32, bytes));
if (configType == Registration.SET_IP_ORG_METADATA) {
(string memory baseURI, string memory contractURI) = abi.decode(configData, (string, string));
_setMetadata(address(ipOrg_), baseURI, contractURI);
} else {
revert Errors.RegistrationModule_InvalidConfigOperation();
}
Registration.IPOrgConfig memory config = abi.decode(params_, (Registration.IPOrgConfig));
}

/// @notice Registers an IP Asset.
/// @param params_ encoded RegisterIPAParams for module action
/// @return encoded registry and IP Org id of the IP asset.
function _performAction(IIPOrg ipOrg_, address caller_, bytes calldata params_) virtual override internal returns (bytes memory) {
Registration.RegisterIPAParams memory params = abi.decode(params_, (Registration.RegisterIPAParams));
(uint256 globalIpAssetId, uint256 localIpAssetId) = _registerIPAsset(
ipOrg_,
params.owner,
params.name,
params.ipAssetType,
params.hash
);
return abi.encode(globalIpAssetId, localIpAssetId);
(bytes32 executionType, bytes memory executionData) = abi.decode(params_, (bytes32, bytes));
if (executionType == Registration.TRANSFER_IP_ASSET) {
(address from, address to, uint256 id) = abi.decode(executionData, (address, address, uint256));
_transferIPAsset(ipOrg_, id, from, to);
return "";
} else if (executionType == Registration.REGISTER_IP_ASSET) {
Registration.RegisterIPAssetParams memory params = abi.decode(executionData, (Registration.RegisterIPAssetParams));
(uint256 ipAssetId, uint256 ipOrgAssetId) = _registerIPAsset(ipOrg_, params.owner, params.name, params.ipAssetType, params.hash);
return abi.encode(ipAssetId, ipOrgAssetId);
}
return "";
}

/// @dev Registers a new IP asset and wraps it under the provided IP Org.
Expand Down Expand Up @@ -184,14 +200,39 @@ contract RegistrationModule is BaseModule, IRegistrationModule, AccessControlled
);
}

/// @dev Transfers ownership of an IP asset to a new owner.
/// @param ipOrg_ The address of the currently governing IP Org.
/// @param ipOrgAssetId_ The local id of the IP asset within the IP Org.
/// @param from_ The current owner of the IP asset within the IP Org.
/// @param to_ The new owner of the IP asset within the IP Org.
function _transferIPAsset(
address ipOrg_,
uint256 ipOrgAssetId_,
address from_,
address to_
) internal {
ipOrg_.transferFrom(from_, to_, ipOrgAssetId_);
uint256 id = ipAssetId[address(ipOrg_)][ipOrgAssetId_];
emit IPAssetTransferred(
id,
address(ipOrg_),
ipOrgAssetId_,
from_,
to_
);
}

/// @dev Transfers an IP asset to a new governing IP Org.
/// @param fromIpOrg_ The address of the original governing IP Org.
/// @param fromIpOrgAssetId_ The existing id of the IP asset within the IP Org.
/// @param toIpOrg_ The address of the new governing IP Org.
function _transferIPOrg(
/// TODO(leeren) Expose this function to FE once IP Orgs are finalized.
function _transferIPAssetToIPOrg(
address fromIpOrg_,
uint256 fromIpOrgAssetId_,
address toIpOrg_
address toIpOrg_,
address from_,
address to_
) internal returns (uint256 ipAssetId_, uint256 ipOrgAssetId_) {
uint256 id = ipAssetId[address(fromIpOrg_)][fromIpOrgAssetId_];

Expand All @@ -210,26 +251,29 @@ contract RegistrationModule is BaseModule, IRegistrationModule, AccessControlled
ipAssetId[address(toIpOrg_)][ipOrgAssetId_] = id;
}

/// @dev Transfers ownership of an IP asset to a new owner.
/// @param ipOrg_ The address of the governing IP Org.
/// @param ipOrgAssetId_ The local id of the IP asset within the IP Org.
/// @param from_ The current owner of the IP asset within the IP Org.
/// @param to_ The new owner of the IP asset within the IP Org.
function _transferIPAsset(
IIPOrg ipOrg_,
uint256 ipOrgAssetId_,
address from_,
address to_

/// @dev Sets the IPOrg token and contract metadata.
/// @param ipOrg_ The address of the IP Org whose metadata is changing.
/// @param baseURI_ The new base URI to assign for the IP Org.
/// @param contractURI_ The new base contract URI to assign for the IP Org.
function _setMetadata(
address ipOrg_,
string memory baseURI_,
string memory contractURI_
) internal {
ipOrg_.transferFrom(from_, to_, ipOrgAssetId_);
uint256 id = ipAssetId[address(ipOrg_)][ipOrgAssetId_];
emit IPAssetTransferred(
id,
address(ipOrg_),
ipOrgAssetId_,
from_,
to_
);
ipOrgConfigs[ipOrg_] = Registration.IPOrgConfig({
baseURI: baseURI_,
contractURI: contractURI_
});
emit MetadataUpdated(ipOrg_, baseURI_, contractURI_);
}

/// @dev Verifies the caller of a configuration action.
/// TODO(leeren): Deprecate in favor of policy-based function auth.
function _verifyConfigCaller(IIPOrg ipOrg_, address caller_) private view {
if (ipOrg_.owner() != caller_) {
revert Errors.Unauthorized();
}
}

/// @dev Returns the administrator for the registration module hooks.
Expand Down

0 comments on commit 1921a37

Please sign in to comment.