Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
leeren committed Nov 9, 2023
1 parent 5e299a0 commit f40f880
Show file tree
Hide file tree
Showing 33 changed files with 409 additions and 126 deletions.
24 changes: 18 additions & 6 deletions contracts/IPAssetRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { IPAsset } from "contracts/lib/IPAsset.sol";

/// @title Global IP Asset Registry
/// @notice The source of truth for IP on Story Protocol.
// TO-DO(@leeren): Migrate from consecutive ids to a global namehashing scheme.
contract IPAssetRegistry is IIPAssetRegistry {

/// @notice Core attributes that make up an IP Asset.
Expand All @@ -24,18 +23,25 @@ contract IPAssetRegistry is IIPAssetRegistry {
bytes data; // Any additional data to be tied to the IP asset.
}

uint256 public immutable IP_ORG_CONTROLLER;

/// @notice Mapping from IP asset ids to registry records.
mapping(uint256 => IPA) public ipAssets;

/// @notice Tracks the total number of IP Assets in existence.
uint256 numIPAssets = 0;

/// @notice Restricts calls to only being from the owner or IPOrg of an IP asset.
/// TODO(leeren): Add more cohesive authorization once the core alpha refactor is completed.
modifier onlyAuthorized(uint256 id) {
/// @notice Restricts calls to only authorized IP Orgs of the IP Asset.
/// TODO(leeren): Add authorization around IP owner once IP Orgs are done.
modifier onlyIPOrg(uint256 id) {
// Ensure the caller is an enrolled IPOrg.
if (!IP_ORG_CONTROLLER.isIPOrg(msg.sender)) {
revert Errors.Unauthorized();
}

// If the IP is already enrolled, ensure the caller is its IP Org.
address ipOrg = ipAssets[id].ipOrg;
address owner = ipAssets[id].owner;
if (msg.sender != owner || msg.sender != ipOrg) {
if (ipOrg != address(0) && ipOrg != msg.sender) {
revert Errors.Unauthorized();
}
_;
Expand All @@ -47,6 +53,12 @@ contract IPAssetRegistry is IIPAssetRegistry {
_;
}

/// @notice Initializes the Global IP Asset Registry.
/// @param ipOrgController Address of the IP Org Controller contract.
constructor(address ipOrgController_) {
IP_ORG_CONTROLLER = ipOrgController_;
}

/// @notice Registers a new IP Asset.
/// @param params_ The IP asset registration parameters.
// TODO(ramarti): Add registration authorization via registration module.
Expand Down
10 changes: 5 additions & 5 deletions contracts/StoryProtocol.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { IIPOrgFactory } from "contracts/interfaces/ip-org/IIPOrgFactory.sol";
import { IIPOrgController } from "contracts/interfaces/ip-org/IIPOrgController.sol";
import { IIPOrg } from "contracts/interfaces/ip-org/IIPOrg.sol";
import { IPOrgParams } from "contracts/lib/IPOrgParams.sol";
import { Errors } from "contracts/lib/Errors.sol";
Expand All @@ -12,17 +12,17 @@ import { ModuleRegistryKeys } from "contracts/lib/modules/ModuleRegistryKeys.sol

contract StoryProtocol {
// TODO: should this be immutable, or should the protocol be able to change factory
IIPOrgFactory public immutable FACTORY;
IIPOrgController public immutable FACTORY;
ModuleRegistry public immutable MODULE_REGISTRY;

constructor(IIPOrgFactory ipOrgFactory_, ModuleRegistry moduleRegistry_) {
constructor(IIPOrgController ipOrgController_, ModuleRegistry moduleRegistry_) {
if (
address(ipOrgFactory_) == address(0) ||
address(ipOrgController_) == address(0) ||
address(moduleRegistry_) == address(0)
) {
revert Errors.ZeroAddress();
}
FACTORY = ipOrgFactory_;
FACTORY = ipOrgController_;
MODULE_REGISTRY = moduleRegistry_;
}

Expand Down
20 changes: 20 additions & 0 deletions contracts/interfaces/ip-org/IIPOrgController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { IVersioned } from "../utils/IVersioned.sol";
import { IPOrgParams } from "contracts/lib/IPOrgParams.sol";

interface IIPOrgController {

event IPOrgRegistered(
address owner_,
address ipAssetOrg_,
string name_,
string symbol_,
string tokenURI_
);

function registerIpOrg(IPOrgParams.RegisterIPOrgParams calldata params_) external returns(address);

function isIpOrg(address ipAssetOrg_) external view returns (bool);
}
2 changes: 1 addition & 1 deletion contracts/interfaces/ip-org/IIPOrgFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.19;
import { IVersioned } from "../utils/IVersioned.sol";
import { IPOrgParams } from "contracts/lib/IPOrgParams.sol";

interface IIPOrgFactory is IVersioned {
interface IIPOrgController is IVersioned {

event IPOrgRegistered(
address owner_,
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/modules/base/IModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ interface IModule {
/// @param params_ encoded params for module configuration
function configure(IIPOrg ipOrg_, address caller_, bytes calldata params_) external;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ interface ILicenseRegistry is IERC721 {
function symbol() external view returns (string memory);
function getRightsManager() external view returns (address);

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import { ZeroAddress, Unauthorized } from "contracts/errors/General.sol";
import { IPOrgFactory } from "contracts/ip-org/IPOrgFactory.sol";
import { IPOrgController } from "contracts/ip-org/IPOrgController.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { AccessControlledUpgradeable } from "contracts/access-control/AccessControlledUpgradeable.sol";
import { ITermsProcessor } from "./terms/ITermsProcessor.sol";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ interface ITermsProcessor is IERC165 {
/// returns true if the terms have been executed successfully or they don't need to be executed, false otherwise
function termsExecutedSuccessfully(bytes calldata data_) external view returns(bool);

}
}
2 changes: 1 addition & 1 deletion contracts/interfaces/modules/royalties/IRoyaltyPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ interface IRoyaltyPolicy {
/// @param account_ IP Account associated with the policy.
/// @param token_ The ERC20 token for royalty.
function distribute(address account_, address token_) external;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ interface IRoyaltyPolicy {
/// @param account_ IP Account associated with the policy.
/// @param token_ The ERC20 token for royalty.
function distribute(address account_, address token_) external;
}
}
69 changes: 48 additions & 21 deletions contracts/ip-org/IPOrg.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,73 @@ import { IPAssetRegistry } from "contracts/IPAssetRegistry.sol";
import { Errors } from "contracts/lib/Errors.sol";

/// @notice IP Asset Organization
/// TODO(leeren): Deprecate upgradeability once the IPOrg contracts is finalized.
contract IPOrg is
IIPOrg,
ERC721Upgradeable,
MulticallUpgradeable,
OwnableUpgradeable
MulticallUpgradeable
{

struct IPOrgAsset {
uint256 ipAssetId;
string name;
string description;
}

/// @custom:storage-location erc7201:story-protocol.ip-asset-org.storage
// TODO: Refactor IP asset types to be specified through the IP Asset Registry or one of its modules.
struct IPOrgStorage {
uint256 placeholder;
}

IPAssetRegistry public REGISTRY;
// Address of the IP Org Controller.
address private immutable controller;
// Address of the Global IP Asset Registry
address private immutable registry;

// keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-org-registry.storage")) - 1)))
bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378;
string private constant _VERSION = "0.1.0";

// TODO(ramarti): Refactor to configure IP Asset types via registry modules.
uint256 private constant _ROOT_IP_ASSET = 0;
bytes32 private constant _STORAGE_LOCATION = bytes32(uint256(keccak256("story-protocol.ip-org.storage")) - 1);

/// @notice Returns the current version of the IP asset org contract.
function version() external pure virtual returns (string memory) {
return _VERSION;
/// @notice Creates the IP Org implementation contract.
/// @param ipAssetRegistry_ Address of the Global IP Asset Registry.
constructor(
address ipAssetRegistry_
) initializer {
controller = msg.sender;
registry = ipAssetRegistry;
}

function initialize(IPOrgParams.InitIPOrgParams memory params_) public initializer {
/// @notice Initializes an IP Org.
/// @param name_ Name to assign to the IP Org.
/// @param symbol_ Symbol to assign to the IP Org.
/// @param renderer_ Renderer used for IPOrg-localized metadata of IP.
/// @param rendererInitData_ Initialization data to pass to the renderer.
function initialize(
string name_,
string symbol_,
IIPOrgMetadataRenderer renderer_,
bytes memory rendererInitData_
) public initializer {

if (msg.sender != controller) {
revert Errors.Unauthorized();
}

// TODO(ramarti) Decouple IPOrg from the RightsManager and make sure to move `__ERC721_init` here.
__ERC721_init(params_.name, params_.symbol);

__Multicall_init();
__Ownable_init();
// TODO: Weird bug does not allow OZ to specify owner in init...
_transferOwnership(params_.owner);

if (params_.registry == address(0)) revert Errors.ZeroAddress();
REGISTRY = IPAssetRegistry(params_.registry);
}

function createIpAsset(IPAsset.CreateIpAssetParams calldata params_) public returns (uint256, uint256) {
if (params_.ipAssetType == IPAsset.IPAssetType.UNDEFINED) revert Errors.IPAsset_InvalidType(IPAsset.IPAssetType.UNDEFINED);
// TODO: Add module and other relevant configuration for registration.
uint256 ipAssetId = REGISTRY.register(msg.sender, address(this));
uint256 ipAssetOrgId = _mintBlock(params_.to, params_.ipAssetType);
_writeIPAsset(ipAssetId, ipAssetOrgId, params_.name, params_.description, params_.mediaUrl);
IPAssetOrgStorage storage $ = _getIPAssetOrgStorage();

return (ipAssetId, ipAssetOrgId);
}

/// @notice Retrieves the token URI for an IP Asset within the IP Asset Org.
/// @param tokenId_ The id of the IP Asset within the IP Asset Org.
Expand All @@ -62,8 +88,9 @@ contract IPOrg is
return "TODO";
}

function owner() public view override(IIPOrg, OwnableUpgradeable) returns (address) {
return super.owner();
/// @notice Retrieves the current owner of the IP Org.
function owner() external {
return IP_ASSET_CONTROLLER.ownerOf(msg.sender);
}

/// @dev Gets the storage associated with the IPOrg contract.
Expand Down
Loading

0 comments on commit f40f880

Please sign in to comment.