Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Licensing Module #76

Merged
merged 45 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6c2000b
first commit for licensing
Aug 11, 2023
f7900b1
WIP
Aug 12, 2023
99972f8
grant license refactor
Aug 12, 2023
53c6158
grant license
Aug 12, 2023
53ab00d
license module upgradeable
Aug 12, 2023
7437821
upgraded franchise to include license module
Aug 12, 2023
04c7568
fix
Aug 12, 2023
753d851
WIP
Aug 12, 2023
2caa838
IPAssetRegistry as RightsManager, and LicenseRegistry
Aug 17, 2023
350d4d4
Merge branch 'main' into licensing_erc_5218
Aug 17, 2023
b3f46db
licensing module
Aug 18, 2023
60139cd
create initial rights
Aug 18, 2023
57bb946
parameter interpreters first commit
Aug 18, 2023
7d57049
terms processor
Aug 22, 2023
991a3fe
link terms to licensing contracts
Aug 22, 2023
5576cb8
wip
Aug 24, 2023
fa821b1
licenseholder fix and time relationship processors
Aug 24, 2023
ed5511b
comment fix
Aug 25, 2023
ccb713c
passing tests, fixing access
Aug 28, 2023
f053ed8
old test passing
Aug 28, 2023
7dc3e10
WIP Licensing module
Aug 28, 2023
d405dda
fix root license creation, config, license registry transfer
Aug 29, 2023
b3df8a0
WIP: test_revert_rootLicenseNotActiveCommercial not working, isLicens…
Aug 29, 2023
126526f
wip
Aug 30, 2023
b0b9e1e
fix
Aug 30, 2023
627cd0b
moar test
Aug 30, 2023
44a4554
wip: prepare new tests
Aug 30, 2023
8d3b167
more tests and fixes
Sep 4, 2023
731fcfe
fix tests
Sep 4, 2023
f98972c
test license registry
Sep 4, 2023
2a7a433
todo: test terms
Sep 4, 2023
89e339b
Merge branch 'main' into licensing_erc_5218
Sep 4, 2023
ed11489
timing terms
Sep 5, 2023
d2b235a
fix compilation
Sep 5, 2023
d7b643d
docs
Sep 6, 2023
69b0611
licenseRegistry decoupling
Sep 6, 2023
a1683ad
comment fix
Sep 6, 2023
7999353
remove log
Sep 6, 2023
575bc5c
comment protocol roles
Sep 11, 2023
ef9e2e9
1 space inside brackets in imports
Sep 11, 2023
dc297c8
fix typo terms
Sep 11, 2023
cd1aca6
refactor magic number
Sep 11, 2023
4154562
comment time processor
Sep 11, 2023
b1e43a9
Merge branch 'main' into licensing_erc_5218
Sep 11, 2023
f10a04f
Fixes collect tests
leeren Sep 12, 2023
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
61 changes: 61 additions & 0 deletions broadcast/DevDeployments.s.sol/5/run-1691868638.json

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions broadcast/DevDeployments.s.sol/5/run-1691868669.json

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions broadcast/DevDeployments.s.sol/5/run-1691868693.json

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions broadcast/DevDeployments.s.sol/5/run-latest.json

Large diffs are not rendered by default.

81 changes: 81 additions & 0 deletions broadcast/DevUpgrades.s.sol/5/run-1691870521.json

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions broadcast/DevUpgrades.s.sol/5/run-1691870540.json

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions broadcast/DevUpgrades.s.sol/5/run-1691870567.json

Large diffs are not rendered by default.

105 changes: 71 additions & 34 deletions broadcast/DevUpgrades.s.sol/5/run-latest.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion contracts/FranchiseRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IIPAssetRegistry } from "./ip-assets/IIPAssetRegistry.sol";
import { LibIPAssetId } from "./ip-assets/LibIPAssetId.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract FranchiseRegistry is
UUPSUpgradeable,
Expand Down Expand Up @@ -49,7 +50,6 @@ contract FranchiseRegistry is
uint256 public constant PROTOCOL_ROOT_ID = 0;
address public constant PROTOCOL_ROOT_ADDRESS = address(0);
string private constant _VERSION = "0.1.0";


constructor(address _factory) {
if (_factory == address(0)) revert ZeroAddress();
Expand Down Expand Up @@ -85,6 +85,7 @@ contract FranchiseRegistry is
$.ipAssetRegistries[nextId] = ipAssetRegistry;
$.tokenURIs[nextId] = params.tokenURI;
_safeMint(msg.sender, nextId);
// TODO: set licensing restrictions per franchise, maybe grant commercial root license to the franchise NFT

emit FranchiseRegistered(msg.sender, nextId, ipAssetRegistry, params.name, params.symbol, params.tokenURI);

Expand All @@ -98,6 +99,7 @@ contract FranchiseRegistry is
return $.ipAssetRegistries[franchiseId];
}


/**
* @notice checks if an address is a valid SP IPAssetRegistry.
* @param ipAssetRegistry the address to check
Expand All @@ -111,6 +113,7 @@ contract FranchiseRegistry is
}
}


function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
FranchiseStorage storage $ = _getFranchiseStorage();
Expand Down
7 changes: 7 additions & 0 deletions contracts/access-control/ProtocolRoles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

pragma solidity ^0.8.13;

// This roles are used by the AccessControlSingleton, accessed by AccessControlled contracts.

// Default admin role as per OZ AccessControl system. All other roles stem from this.
bytes32 constant PROTOCOL_ADMIN_ROLE = bytes32(0);
// Role that can upgrade UUPS contracts or Beacon Proxies
bytes32 constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
// Role that can perform admin tasks on the Protocol Relationship Module contract (e.g. adding new protocol-wide links)
bytes32 constant RELATIONSHIP_MANAGER_ROLE = keccak256("RELATIONSHIP_MANAGER_ROLE");
// Role that can perform admin tasks on the Licensing Module contracts (setNonCommercialLicenseURI)
bytes32 constant LICENSING_MANAGER_ROLE = keccak256("LICENSING_MANAGER_ROLE");
4 changes: 3 additions & 1 deletion contracts/errors/General.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ error ZeroAddress();
error ZeroAmount();
error UnsupportedInterface(string name);
error Unauthorized();
error NonExistentID(uint256 id);
error NonExistentID(uint256 id);
error EmptyArray();
error LengthMismatch();
32 changes: 18 additions & 14 deletions contracts/ip-assets/IIPAssetRegistry.sol
Ramarti marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@
pragma solidity ^0.8.13;

import { IVersioned } from "../utils/IVersioned.sol";
import { IIPAssetDataManager } from './storage/IIPAssetDataManager.sol';
import { IIPAssetDataManager } from "./storage/IIPAssetDataManager.sol";
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import { IPAsset } from "contracts/IPAsset.sol";
import { IERC5218 } from "../modules/licensing/IERC5218.sol";
import { IPAsset } from "../IPAsset.sol";

interface IIPAssetRegistry is
interface IIPAssetRegistry is
IVersioned,
IERC165Upgradeable,
IERC721Upgradeable,
IERC5218,
IIPAssetDataManager
{
function createIPAsset(
IPAsset sb,
string calldata name,
string calldata _description,
string calldata mediaUrl
) external returns (uint256);
function franchiseId() external view returns (uint256);
}
{
function franchiseId() external view returns (uint256);

function createIPAsset(
IPAsset sb,
string calldata name,
string calldata _description,
string calldata mediaUrl,
address to,
uint256 parentIpAssetId
Ramarti marked this conversation as resolved.
Show resolved Hide resolved
) external returns (uint256);

}
131 changes: 113 additions & 18 deletions contracts/ip-assets/IPAssetRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
//import "forge-std/console.sol";

import { IIPAssetRegistry } from "./IIPAssetRegistry.sol";
import { LibIPAssetId } from "./LibIPAssetId.sol";
import { Unauthorized, ZeroAmount, ZeroAddress } from "../errors/General.sol";
import { IPAsset } from "contracts/IPAsset.sol";
import { IIPAssetEventEmitter } from "./events/IIPAssetEventEmitter.sol";
import { IIPAssetDataManager } from "./storage/IIPAssetDataManager.sol";
import { IPAssetDataManager } from "./storage/IPAssetDataManager.sol";
import { ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import { RightsManager } from "../modules/licensing/RightsManager.sol";
import { ILicensingModule } from "../modules/licensing/ILicensingModule.sol";

contract IPAssetRegistry is
IIPAssetRegistry,
IPAssetDataManager,
ERC721Upgradeable,
RightsManager,
MulticallUpgradeable
{

error IdOverBounds();
error LicensingNotConfigured();

/// @custom:storage-location erc7201:story-protocol.ip-assets-registry.storage
struct IPAssetRegistryStorage {
Expand All @@ -30,13 +30,19 @@ contract IPAssetRegistry is
}

IIPAssetEventEmitter public immutable EVENT_EMITTER;
ILicensingModule public immutable LICENSING_MODULE;
// keccak256(bytes.concat(bytes32(uint256(keccak256("story-protocol.ip-assets-registry.storage")) - 1)))
bytes32 private constant _STORAGE_LOCATION = 0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378;
bytes32 private constant _STORAGE_LOCATION =
0x1a0b8fa444ff575656111a4368b8e6a743b70cbf31ffb9ee2c7afe1983f0e378;
string private constant _VERSION = "0.1.0";
uint256 private constant _ROOT_IP_ASSET = 0;

constructor(address _eventEmitter) {
constructor(address _eventEmitter, address _licensingModule, address _franchiseRegistry) RightsManager(_franchiseRegistry) {
// TODO: should Franchise owner be able to change this?
if (_eventEmitter == address(0)) revert ZeroAddress();
EVENT_EMITTER = IIPAssetEventEmitter(_eventEmitter);
if (_licensingModule == address(0)) revert ZeroAddress();
LICENSING_MODULE = ILicensingModule(_licensingModule);
_disableInitializers();
}

Expand All @@ -46,7 +52,7 @@ contract IPAssetRegistry is
string calldata _symbol,
string calldata _description
) public initializer {
__ERC721_init(_name, _symbol);
__RightsManager_init(_name, _symbol);
__Multicall_init();
if (_franchiseId == 0) revert ZeroAmount();
IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage();
Expand All @@ -64,24 +70,110 @@ contract IPAssetRegistry is
}
}

function version() external pure virtual override returns (string memory) {
function version() external pure virtual returns (string memory) {
return _VERSION;
}

/**
* Creates a new IPAsset, and assigns licenses (rights) to it, according to the Franchise config in LicensingModule.
* A Non commercial license is always assigned, and if the IPAsset is a root IPAsset, a commercial license may also be assigned.
* @dev reverts if LicensingModule is not configured for the Franchise.
* Logs to IPAssetEventEmitter, common contract for all IPAsset registries.
* @param ipAssetType the type of IPAsset to create
* @param name IPAsset name
* @param _description short description of the IPAsset
* @param mediaUrl url to the IPAsset media and metadata
* @param to holder of the IPAsset (and thus the licenses)
* @param parentIpAssetId 0 if this is a root IPAsset, if it is a derivative, set the parent IPAsset id
* @return the created IPAsset id
*/
function createIPAsset(
IPAsset sb,
IPAsset ipAssetType,
string calldata name,
string calldata _description,
string calldata mediaUrl
) external virtual override returns (uint256) {
if (sb == IPAsset.UNDEFINED) revert InvalidBlockType();
uint256 sbId = _mintBlock(msg.sender, sb);
_writeIPAsset(sbId, name, _description, mediaUrl);
string calldata mediaUrl,
address to,
uint256 parentIpAssetId
)
public
returns (uint256)
{
if (ipAssetType == IPAsset.UNDEFINED) revert InvalidBlockType();
uint256 ipAssetId = _mintBlock(to, ipAssetType);
_writeIPAsset(ipAssetId, name, _description, mediaUrl);
IPAssetRegistryStorage storage $ = _getIPAssetRegistryStorage();
EVENT_EMITTER.emitIPAssetCreation($.franchiseId, sbId);
return sbId;
EVENT_EMITTER.emitIPAssetCreation($.franchiseId, ipAssetId);

// Non commercial
ILicensingModule.FranchiseConfig memory config = LICENSING_MODULE.getFranchiseConfig($.franchiseId);
if (config.revoker == address(0)) revert LicensingNotConfigured();

_setNonCommercialRights(ipAssetId, parentIpAssetId, to, config.revoker, config.nonCommercialConfig, config.nonCommercialTerms);
// If non derivative IpAsset, then franchise config may dictate commercial rights
// Derivative works do not have commercial rights unless a deal with the relevant licensor is made
if (config.rootIpAssetHasCommercialRights && parentIpAssetId == 0) {
// Commercial
_setCommercialRights(ipAssetId, _ROOT_IP_ASSET, to, config.revoker, config.commercialLicenseUri, config.commercialConfig, config.commercialTerms);
}

return ipAssetId;
}

/**
* Sets the non commercial rights for an IPAsset, with terms from the Franchise config in LicensingModule.
* If no parent asset id is provided, the root IPAsset id is used if it exists in the Franchise config.
* @param ipAssetId the IPAsset id
* @param parentIpAssetId in case this is a derivative IPAsset, set the parent IPAsset id, 0 otherwise
* @param holder of the IPAsset and licenses
* @param revoker of the license. Can't be zero or changed later
* @param config Franchise config
* @param terms for the license to be active
*/
function _setNonCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal {
uint256 parentLicenseId = parentIpAssetId == 0 ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, false);
_createLicense(
ipAssetId,
parentLicenseId,
holder,
LICENSING_MODULE.getNonCommercialLicenseURI(),
revoker,
false,
config.canSublicense,
terms,
false
);
}

/**
* Sets the commercial rights for an IPAsset, with terms from the Franchise config in LicensingModule.
* If no parent asset id is provided, the root IPAsset id is used if it exists in the Franchise config.
* @param ipAssetId the IPAsset id
* @param parentIpAssetId in case this is a derivative IPAsset, set the parent IPAsset id, 0 otherwise
* @param holder of the IPAsset and licenses
* @param revoker of the license. Can't be zero or changed later
* @param config Franchise config
* @param terms for the license to be active
*/
function _setCommercialRights(uint256 ipAssetId, uint256 parentIpAssetId, address holder, address revoker, string memory licenseUri, ILicensingModule.IpAssetConfig memory config, TermsProcessorConfig memory terms) internal {
uint256 parentLicenseId = parentIpAssetId == _ROOT_IP_ASSET ? config.franchiseRootLicenseId : getLicenseIdByTokenId(parentIpAssetId, true);
_createLicense(
ipAssetId,
parentLicenseId,
holder,
licenseUri,
revoker,
true,
config.canSublicense,
terms,
false
);
}

/**
* mints the IPAsset block, and assigns the next id to it.
* @param to holder
* @param sb ip asset type
*/
function _mintBlock(address to, IPAsset sb) private returns (uint256) {
uint256 nextId = currentIdFor(sb) + 1;
if (nextId > LibIPAssetId._lastId(sb)) revert IdOverBounds();
Expand Down Expand Up @@ -111,7 +203,10 @@ contract IPAssetRegistry is
return $.franchiseId;
}

function tokenURI(uint256 tokenId) public view override returns (string memory) {
function tokenURI(
uint256 tokenId
) public view override returns (string memory) {
// TODO: should this reference the license too?
return readIPAsset(tokenId).mediaUrl;
}

Expand Down
4 changes: 4 additions & 0 deletions contracts/ip-assets/IPAssetRegistryFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IPAssetRegistry } from "./IPAssetRegistry.sol";
import { ZeroAddress } from "../errors/General.sol";
import { IVersioned } from "../utils/IVersioned.sol";
import { UnsupportedInterface } from "../errors/General.sol";
import { LicenseRegistry } from "../modules/licensing/LicenseRegistry.sol";
import { RevertingIPAssetRegistry } from "contracts/utils/RevertingIPAssetRegistry.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
Expand Down Expand Up @@ -40,6 +41,9 @@ contract IPAssetRegistryFactory is Ownable {
description
);
address proxy = address(new BeaconProxy(address(BEACON), data));
LicenseRegistry licenseRegistry = new LicenseRegistry(proxy, string.concat("Licenses for ", name), string.concat("sl", symbol));
IPAssetRegistry(proxy).setLicenseRegistry(address(licenseRegistry));

emit FranchiseCreated(proxy, name, symbol);
return proxy;
}
Expand Down
Loading