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

Deploy with CREATE3 for Deterministic Addresses to Resolve Cyclical Dependencies in Contract Deployment #124

Merged
merged 7 commits into from
Apr 18, 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
47 changes: 8 additions & 39 deletions contracts/LicenseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ import { ILicenseTemplate } from "./interfaces/modules/licensing/ILicenseTemplat
contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManagedUpgradeable, UUPSUpgradeable {
using Strings for *;

ILicensingModule public immutable LICENSING_MODULE;
IDisputeModule public immutable DISPUTE_MODULE;

/// @notice Emitted for metadata updates, per EIP-4906
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);

/// @dev Storage structure for the LicenseToken
/// @custom:storage-location erc7201:story-protocol.LicenseToken
struct LicenseTokenStorage {
string imageUrl;
ILicensingModule licensingModule;
IDisputeModule disputeModule;
uint256 totalMintedTokens;
mapping(uint256 tokenId => LicenseTokenMetadata) licenseTokenMetadatas;
}
Expand All @@ -37,14 +38,16 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
0x62a0d75e37bea0c3e666dc72a74112fc6af15ce635719127e380d8ca1e555d00;

modifier onlyLicensingModule() {
if (msg.sender != address(_getLicenseTokenStorage().licensingModule)) {
if (msg.sender != address(LICENSING_MODULE)) {
revert Errors.LicenseToken__CallerNotLicensingModule();
}
_;
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
constructor(address licensingModule, address disputeModule) {
LICENSING_MODULE = ILicensingModule(licensingModule);
DISPUTE_MODULE = IDisputeModule(disputeModule);
_disableInitializers();
}

Expand All @@ -59,28 +62,6 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
_getLicenseTokenStorage().imageUrl = imageUrl;
}

/// @notice Sets the LicensingModule address.
/// @dev Enforced to be only callable by the protocol admin
/// @param newLicensingModule The address of the LicensingModule
function setLicensingModule(address newLicensingModule) external restricted {
if (newLicensingModule == address(0)) {
revert Errors.LicenseToken__ZeroLicensingModule();
}
LicenseTokenStorage storage $ = _getLicenseTokenStorage();
$.licensingModule = ILicensingModule(newLicensingModule);
}

/// @notice Sets the DisputeModule address.
/// @dev Enforced to be only callable by the protocol admin
/// @param newDisputeModule The address of the DisputeModule
function setDisputeModule(address newDisputeModule) external restricted {
if (newDisputeModule == address(0)) {
revert Errors.LicenseToken__ZeroDisputeModule();
}
LicenseTokenStorage storage $ = _getLicenseTokenStorage();
$.disputeModule = IDisputeModule(newDisputeModule);
}

/// @dev Sets the Licensing Image URL.
/// @dev Enforced to be only callable by the protocol admin
/// @param url The URL of the Licensing Image
Expand Down Expand Up @@ -207,24 +188,12 @@ contract LicenseToken is ILicenseToken, ERC721EnumerableUpgradeable, AccessManag
return _getLicenseTokenStorage().licenseTokenMetadatas[tokenId].licenseTemplate;
}

/// @notice Returns the canonical protocol-wide DisputeModule
/// @return The DisputeModule instance
function disputeModule() external view returns (IDisputeModule) {
return _getLicenseTokenStorage().disputeModule;
}

/// @notice Returns the canonical protocol-wide LicensingModule
/// @return The LicensingModule instance
function licensingModule() external view returns (ILicensingModule) {
return _getLicenseTokenStorage().licensingModule;
}

/// @notice Returns true if the license has been revoked (licensor IP tagged after a dispute in
/// the dispute module). If the tag is removed, the license is not revoked anymore.
/// @return isRevoked True if the license is revoked
function isLicenseTokenRevoked(uint256 tokenId) public view returns (bool) {
LicenseTokenStorage storage $ = _getLicenseTokenStorage();
return $.disputeModule.isIpTagged($.licenseTokenMetadatas[tokenId].licensorIpId);
return DISPUTE_MODULE.isIpTagged($.licenseTokenMetadatas[tokenId].licensorIpId);
}

/// @notice ERC721 OpenSea metadata JSON representation of the LNFT parameters
Expand Down
31 changes: 11 additions & 20 deletions contracts/access/AccessController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import { Errors } from "../lib/Errors.sol";
contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUPSUpgradeable {
using IPAccountChecker for IIPAccountRegistry;

IIPAccountRegistry public immutable IP_ACCOUNT_REGISTRY;
IModuleRegistry public immutable MODULE_REGISTRY;

/// @dev The storage struct of AccessController.
/// @param encodedPermissions tracks the permission granted to an encoded permission path, where the
/// encoded permission path = keccak256(abi.encodePacked(ipAccount, signer, to, func))
Expand All @@ -40,8 +43,6 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP
/// @custom:storage-location erc7201:story-protocol.AccessController
struct AccessControllerStorage {
mapping(bytes32 => uint8) encodedPermissions;
address ipAccountRegistry;
address moduleRegistry;
}

// keccak256(abi.encode(uint256(keccak256("story-protocol.AccessController")) - 1)) & ~bytes32(uint256(0xff));
Expand All @@ -50,7 +51,11 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP

/// Constructor
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
constructor(address ipAccountRegistry, address moduleRegistry) {
if (ipAccountRegistry == address(0)) revert Errors.AccessController__ZeroIPAccountRegistry();
if (moduleRegistry == address(0)) revert Errors.AccessController__ZeroModuleRegistry();
IP_ACCOUNT_REGISTRY = IIPAccountRegistry(ipAccountRegistry);
MODULE_REGISTRY = IModuleRegistry(moduleRegistry);
_disableInitializers();
}

Expand All @@ -64,16 +69,6 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP
__UUPSUpgradeable_init();
}

/// @notice Sets the addresses of the IP account registry and the module registry
/// @dev TODO: Set these addresses in the constructor to make them immutable
/// @param ipAccountRegistry address of the IP account registry
/// @param moduleRegistry address of the module registry
function setAddresses(address ipAccountRegistry, address moduleRegistry) external restricted {
AccessControllerStorage storage $ = _getAccessControllerStorage();
$.ipAccountRegistry = ipAccountRegistry;
$.moduleRegistry = moduleRegistry;
}

/// @notice Sets a batch of permissions in a single transaction.
/// @dev This function allows setting multiple permissions at once. Pausable via setPermission.
/// @param permissions An array of `Permission` structs, each representing the permission to be set.
Expand Down Expand Up @@ -121,7 +116,7 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP
revert Errors.AccessController__SignerIsZeroAddress();
}
AccessControllerStorage storage $ = _getAccessControllerStorage();
if (!IIPAccountRegistry($.ipAccountRegistry).isIpAccount(ipAccount)) {
if (!IP_ACCOUNT_REGISTRY.isIpAccount(ipAccount)) {
revert Errors.AccessController__IPAccountIsNotValid(ipAccount);
}
// permission must be one of ABSTAIN, ALLOW, DENY
Expand Down Expand Up @@ -149,7 +144,7 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP
function checkPermission(address ipAccount, address signer, address to, bytes4 func) external view {
AccessControllerStorage storage $ = _getAccessControllerStorage();
// Must be a valid IPAccount
if (!IIPAccountRegistry($.ipAccountRegistry).isIpAccount(ipAccount)) {
if (!IP_ACCOUNT_REGISTRY.isIpAccount(ipAccount)) {
revert Errors.AccessController__IPAccountIsNotValid(ipAccount);
}
// Owner can call any contracts either registered module or unregistered/external contracts
Expand All @@ -160,11 +155,7 @@ contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUP
// If the caller (signer) is not the Owner, IPAccount is limited to interactions with only registered modules.
// These interactions can be either initiating calls to these modules or receiving calls from them.
// The IP account can also modify its own Permissions settings.
if (
to != address(this) &&
!IModuleRegistry($.moduleRegistry).isRegistered(to) &&
!IModuleRegistry($.moduleRegistry).isRegistered(signer)
) {
if (to != address(this) && !MODULE_REGISTRY.isRegistered(to) && !MODULE_REGISTRY.isRegistered(signer)) {
revert Errors.AccessController__BothCallerAndRecipientAreNotRegisteredModule(signer, to);
}

Expand Down
11 changes: 0 additions & 11 deletions contracts/interfaces/ILicenseToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ pragma solidity 0.8.23;
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import { IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

import { IDisputeModule } from "./modules/dispute/IDisputeModule.sol";
import { ILicensingModule } from "./modules/licensing/ILicensingModule.sol";

/// @title ILicenseToken
/// @notice Interface for the License Token (ERC721) NFT collection that manages License Tokens representing
/// License Terms.
Expand Down Expand Up @@ -86,14 +83,6 @@ interface ILicenseToken is IERC721Metadata, IERC721Enumerable {
/// @return A `LicenseTokenMetadata` struct containing the metadata of the specified License Token.
function getLicenseTokenMetadata(uint256 tokenId) external view returns (LicenseTokenMetadata memory);

/// @notice Returns the canonical protocol-wide DisputeModule
/// @return The DisputeModule instance
function disputeModule() external view returns (IDisputeModule);

/// @notice Returns the canonical protocol-wide LicensingModule
/// @return The LicensingModule instance
function licensingModule() external view returns (ILicensingModule);

/// @notice Validates License Tokens for registering a derivative IP.
/// @dev This function checks if the License Tokens are valid for the derivative IP registration process.
/// The function will be called by LicensingModule when registering a derivative IP with license tokens.
Expand Down
8 changes: 0 additions & 8 deletions contracts/interfaces/modules/royalty/IRoyaltyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@ interface IRoyaltyModule is IModule {
/// @param amount The amount paid
event LicenseMintingFeePaid(address receiverIpId, address payerAddress, address token, uint256 amount);

/// @notice Sets the licensing module
/// @dev Enforced to be only callable by the protocol admin
/// @param licensing The address of the license module
function setLicensingModule(address licensing) external;

/// @notice Returns the licensing module address
function licensingModule() external view returns (address);

/// @notice Indicates if a royalty policy is whitelisted
/// @param royaltyPolicy The address of the royalty policy
/// @return isWhitelisted True if the royalty policy is whitelisted
Expand Down
18 changes: 18 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ library Errors {
/// @notice Zero address provided for Access Manager in initializer.
error LicenseRegistry__ZeroAccessManager();

/// @notice Zero address provided for IP Asset Registry.
error LicensingModule__ZeroRoyaltyModule();

/// @notice Zero address provided for Licensing Module.
error LicensingModule__ZeroLicenseRegistry();

/// @notice Zero address provided for Dispute Module.
error LicensingModule__ZeroDisputeModule();

/// @notice Zero address provided for License Token.
error LicensingModule__ZeroLicenseToken();

/// @notice Zero address provided for Licensing Module.
error LicenseRegistry__ZeroLicensingModule();

Expand Down Expand Up @@ -448,6 +460,12 @@ library Errors {
/// @notice Zero address provided for Access Manager in initializer.
error AccessController__ZeroAccessManager();

/// @notice Zero address provided for IP Account Registry.
error AccessController__ZeroIPAccountRegistry();

/// @notice Zero address provided for Module Registry.
error AccessController__ZeroModuleRegistry();

/// @notice IP Account is zero address.
error AccessController__IPAccountIsZeroAddress();

Expand Down
6 changes: 6 additions & 0 deletions contracts/lib/PILicenseTemplateErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ library PILicenseTemplateErrors {

/// @notice Cannot add derivative reciprocal when derivative use is disabled.
error PILicenseTemplate__DerivativesDisabled_CantAddReciprocal();

/// @notice Zero address provided for License Registry at initialization.
error PILicenseTemplate__ZeroLicenseRegistry();

/// @notice Zero address provided for Royalty Module at initialization.
error PILicenseTemplate__ZeroRoyaltyModule();
}
10 changes: 7 additions & 3 deletions contracts/modules/licensing/LicensingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,23 @@ contract LicensingModule is
/// @param accessController The address of the AccessController contract
/// @param ipAccountRegistry The address of the IPAccountRegistry contract
/// @param royaltyModule The address of the RoyaltyModule contract
/// @param registry The address of the LicenseRegistry contract
/// @param licenseRegistry The address of the LicenseRegistry contract
/// @param disputeModule The address of the DisputeModule contract
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(
address accessController,
address ipAccountRegistry,
address royaltyModule,
address registry,
address licenseRegistry,
address disputeModule,
address licenseToken
) AccessControlled(accessController, ipAccountRegistry) {
if (royaltyModule == address(0)) revert Errors.LicensingModule__ZeroRoyaltyModule();
if (licenseRegistry == address(0)) revert Errors.LicensingModule__ZeroLicenseRegistry();
if (disputeModule == address(0)) revert Errors.LicensingModule__ZeroDisputeModule();
if (licenseToken == address(0)) revert Errors.LicensingModule__ZeroLicenseToken();
ROYALTY_MODULE = RoyaltyModule(royaltyModule);
LICENSE_REGISTRY = ILicenseRegistry(registry);
LICENSE_REGISTRY = ILicenseRegistry(licenseRegistry);
DISPUTE_MODULE = IDisputeModule(disputeModule);
LICENSE_NFT = ILicenseToken(licenseToken);
_disableInitializers();
Expand Down
2 changes: 2 additions & 0 deletions contracts/modules/licensing/PILicenseTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ contract PILicenseTemplate is
address licenseRegistry,
address royaltyModule
) LicensorApprovalChecker(accessController, ipAccountRegistry) {
if (licenseRegistry == address(0)) revert PILicenseTemplateErrors.PILicenseTemplate__ZeroLicenseRegistry();
if (royaltyModule == address(0)) revert PILicenseTemplateErrors.PILicenseTemplate__ZeroRoyaltyModule();
LICENSE_REGISTRY = ILicenseRegistry(licenseRegistry);
ROYALTY_MODULE = IRoyaltyModule(royaltyModule);
_disableInitializers();
Expand Down
27 changes: 9 additions & 18 deletions contracts/modules/royalty/RoyaltyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IRoyaltyModule } from "../../interfaces/modules/royalty/IRoyaltyModule.
import { IRoyaltyPolicy } from "../../interfaces/modules/royalty/policies/IRoyaltyPolicy.sol";
import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.sol";
import { ILicenseRegistry } from "../../interfaces/registries/ILicenseRegistry.sol";
import { ILicensingModule } from "../../interfaces/modules/licensing/ILicensingModule.sol";
import { Errors } from "../../lib/Errors.sol";
import { ROYALTY_MODULE_KEY } from "../../lib/modules/Module.sol";
import { BaseModule } from "../BaseModule.sol";
Expand All @@ -28,6 +29,10 @@ contract RoyaltyModule is
{
using ERC165Checker for address;

/// @notice Returns the canonical protocol-wide licensing module
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ILicensingModule public immutable LICENSING_MODULE;

/// @notice Returns the canonical protocol-wide LicenseRegistry
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ILicenseRegistry public immutable LICENSE_REGISTRY;
Expand All @@ -37,13 +42,11 @@ contract RoyaltyModule is
IDisputeModule public immutable DISPUTE_MODULE;

/// @dev Storage structure for the RoyaltyModule
/// @param licensingModule The address of the licensing module
/// @param isWhitelistedRoyaltyPolicy Indicates if a royalty policy is whitelisted
/// @param isWhitelistedRoyaltyToken Indicates if a royalty token is whitelisted
/// @param royaltyPolicies Indicates the royalty policy for a given IP asset
/// @custom:storage-location erc7201:story-protocol.RoyaltyModule
struct RoyaltyModuleStorage {
address licensingModule;
mapping(address royaltyPolicy => bool isWhitelisted) isWhitelistedRoyaltyPolicy;
mapping(address token => bool) isWhitelistedRoyaltyToken;
mapping(address ipId => address royaltyPolicy) royaltyPolicies;
Expand All @@ -59,10 +62,12 @@ contract RoyaltyModule is
/// @param disputeModule The address of the dispute module
/// @param licenseRegistry The address of the license registry
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address disputeModule, address licenseRegistry) {
constructor(address licensingModule, address disputeModule, address licenseRegistry) {
if (disputeModule == address(0)) revert Errors.RoyaltyModule__ZeroDisputeModule();
if (licenseRegistry == address(0)) revert Errors.RoyaltyModule__ZeroLicenseRegistry();
if (licensingModule == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule();

LICENSING_MODULE = ILicensingModule(licensingModule);
DISPUTE_MODULE = IDisputeModule(disputeModule);
LICENSE_REGISTRY = ILicenseRegistry(licenseRegistry);
_disableInitializers();
Expand All @@ -81,19 +86,10 @@ contract RoyaltyModule is

/// @notice Modifier to enforce that the caller is the licensing module
modifier onlyLicensingModule() {
RoyaltyModuleStorage storage $ = _getRoyaltyModuleStorage();
if (msg.sender != $.licensingModule) revert Errors.RoyaltyModule__NotAllowedCaller();
if (msg.sender != address(LICENSING_MODULE)) revert Errors.RoyaltyModule__NotAllowedCaller();
_;
}

/// @notice Sets the licensing module
/// @dev Enforced to be only callable by the protocol admin
/// @param licensing The address of the license module
function setLicensingModule(address licensing) external restricted {
if (licensing == address(0)) revert Errors.RoyaltyModule__ZeroLicensingModule();
_getRoyaltyModuleStorage().licensingModule = licensing;
}

/// @notice Whitelist a royalty policy
/// @dev Enforced to be only callable by the protocol admin
/// @param royaltyPolicy The address of the royalty policy
Expand Down Expand Up @@ -239,11 +235,6 @@ contract RoyaltyModule is
emit LicenseMintingFeePaid(receiverIpId, payerAddress, token, amount);
}

/// @notice Returns the licensing module address
function licensingModule() external view returns (address) {
return _getRoyaltyModuleStorage().licensingModule;
}

/// @notice Indicates if a royalty policy is whitelisted
/// @param royaltyPolicy The address of the royalty policy
/// @return isWhitelisted True if the royalty policy is whitelisted
Expand Down
Loading
Loading