diff --git a/contracts/AccessController.sol b/contracts/AccessController.sol index 6c3b8685..3310cb76 100644 --- a/contracts/AccessController.sol +++ b/contracts/AccessController.sol @@ -85,7 +85,7 @@ contract AccessController is IAccessController { } permissions[ipAccount_][signer_][to_][func_] = permission_; - // TODO: emit event + emit PermissionSet(ipAccount_, signer_, to_, func_, permission_); } /// @notice Returns the permission level for a specific function call. @@ -154,7 +154,5 @@ contract AccessController is IAccessController { return false; } return false; - - // TODO: emit event } } diff --git a/contracts/interfaces/IAccessController.sol b/contracts/interfaces/IAccessController.sol index 87179bc2..91e344cd 100644 --- a/contracts/interfaces/IAccessController.sol +++ b/contracts/interfaces/IAccessController.sol @@ -3,6 +3,14 @@ pragma solidity ^0.8.23; interface IAccessController { + event PermissionSet( + address indexed ipAccount, + address indexed signer, + address indexed to, + bytes4 func, + uint8 permission + ); + /// @notice Sets the permission for a specific function call /// @dev Each policy is represented as a mapping from an IP account address to a signer address to a recipient ///// address to a function selector to a permission level. diff --git a/contracts/interfaces/modules/IRegistrationModule.sol b/contracts/interfaces/modules/IRegistrationModule.sol new file mode 100644 index 00000000..6e632006 --- /dev/null +++ b/contracts/interfaces/modules/IRegistrationModule.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +interface IRegistrationModule { + event RootIPRegistered(address indexed caller, address indexed ipId, uint256 indexed policyId); + + event DerivativeIPRegistered(address indexed caller, address indexed ipId, uint256 licenseId); + + function registerRootIp(uint256 policyId, address tokenContract, uint256 tokenId) external returns (address); + + function registerDerivativeIp( + uint256 licenseId, + address tokenContract, + uint256 tokenId, + string memory ipName, + string memory ipDescription, + bytes32 hash + ) external; +} diff --git a/contracts/interfaces/modules/dispute/IDisputeModule.sol b/contracts/interfaces/modules/dispute/IDisputeModule.sol new file mode 100644 index 00000000..3bdbeb6d --- /dev/null +++ b/contracts/interfaces/modules/dispute/IDisputeModule.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title Dispute Module Interface +interface IDisputeModule { + /// @notice Whitelists a dispute tag + /// @param tag The dispute tag + /// @param allowed Indicates if the dispute tag is whitelisted or not + function whitelistDisputeTags(bytes32 tag, bool allowed) external; + + /// @notice Whitelists an arbitration policy + /// @param arbitrationPolicy The address of the arbitration policy + /// @param allowed Indicates if the arbitration policy is whitelisted or not + function whitelistArbitrationPolicy(address arbitrationPolicy, bool allowed) external; + + /// @notice Whitelists an arbitration relayer for a given arbitration policy + /// @param arbitrationPolicy The address of the arbitration policy + /// @param arbPolicyRelayer The address of the arbitration relayer + /// @param allowed Indicates if the arbitration relayer is whitelisted or not + function whitelistArbitrationRelayer(address arbitrationPolicy, address arbPolicyRelayer, bool allowed) external; + + /// @notice Raises a dispute + /// @param ipId The ipId + /// @param arbitrationPolicy The address of the arbitration policy + /// @param linkToDisputeSummary The link of the dispute summary + /// @param targetTag The target tag of the dispute + /// @param data The data to initialize the policy + /// @return disputeId The dispute id + function raiseDispute( + address ipId, + address arbitrationPolicy, + string memory linkToDisputeSummary, + bytes32 targetTag, + bytes calldata data + ) external returns (uint256 disputeId); + + /// @notice Sets the dispute judgement + /// @param disputeId The dispute id + /// @param decision The decision of the dispute + /// @param data The data to set the dispute judgement + function setDisputeJudgement(uint256 disputeId, bool decision, bytes calldata data) external; + + /// @notice Cancels an ongoing dispute + /// @param disputeId The dispute id + /// @param data The data to cancel the dispute + function cancelDispute(uint256 disputeId, bytes calldata data) external; + + /// @notice Resolves a dispute after it has been judged + /// @param disputeId The dispute id + function resolveDispute(uint256 disputeId) external; + + /// @notice Gets the dispute struct characteristics + function disputes(uint256 disputeId) external view returns ( + address ipId, + address disputeInitiator, + address arbitrationPolicy, + bytes32 linkToDisputeSummary, + bytes32 tag + ); +} \ No newline at end of file diff --git a/contracts/interfaces/modules/dispute/policies/IArbitrationPolicy.sol b/contracts/interfaces/modules/dispute/policies/IArbitrationPolicy.sol new file mode 100644 index 00000000..b6c6ed05 --- /dev/null +++ b/contracts/interfaces/modules/dispute/policies/IArbitrationPolicy.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title ArbitrationPolicy interface +interface IArbitrationPolicy { + /// @notice Executes custom logic on raise dispute + /// @param caller Address of the caller + function onRaiseDispute(address caller, bytes calldata data) external; + + /// @notice Executes custom logic on dispute judgement + /// @param disputeId The dispute id + /// @param decision The decision of the dispute + function onDisputeJudgement(uint256 disputeId, bool decision, bytes calldata data) external; + + /// @notice Executes custom logic on dispute cancel + function onDisputeCancel(address caller, uint256 disputeId, bytes calldata data) external; +} diff --git a/contracts/interfaces/modules/royalty/IRoyaltyModule.sol b/contracts/interfaces/modules/royalty/IRoyaltyModule.sol new file mode 100644 index 00000000..a75f8bb2 --- /dev/null +++ b/contracts/interfaces/modules/royalty/IRoyaltyModule.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title RoyaltyModule interface +interface IRoyaltyModule { + /// @notice Whitelist a royalty policy + /// @param royaltyPolicy The address of the royalty policy + /// @param allowed Indicates if the royalty policy is whitelisted or not + function whitelistRoyaltyPolicy(address royaltyPolicy, bool allowed) external; + + /// @notice Sets the royalty policy for an ipId + /// @param ipId The ipId + /// @param royaltyPolicy The address of the royalty policy + /// @param data The data to initialize the policy + function setRoyaltyPolicy(address ipId, address royaltyPolicy, bytes calldata data) external; + + /// @notice Allows an IPAccount to pay royalties + /// @param ipId The ipId + /// @param token The token to pay the royalties in + /// @param amount The amount to pay + function payRoyalty(address ipId, address token, uint256 amount) external; + + /// @notice Gets the royalty policy for a given ipId + function royaltyPolicies(address ipId) external view returns (address royaltyPolicy); +} \ No newline at end of file diff --git a/contracts/interfaces/modules/royalty/policies/ILiquidSplitClone.sol b/contracts/interfaces/modules/royalty/policies/ILiquidSplitClone.sol new file mode 100644 index 00000000..f8ece07c --- /dev/null +++ b/contracts/interfaces/modules/royalty/policies/ILiquidSplitClone.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title LiquidSplitClone interface +interface ILiquidSplitClone { + /// @notice Distributes funds to the accounts in the LiquidSplitClone contract + /// @param token The token to distribute + /// @param accounts The accounts to distribute to + /// @param distributorAddress The distributor address + function distributeFunds(address token, address[] calldata accounts, address distributorAddress) external; +} \ No newline at end of file diff --git a/contracts/interfaces/modules/royalty/policies/ILiquidSplitFactory.sol b/contracts/interfaces/modules/royalty/policies/ILiquidSplitFactory.sol new file mode 100644 index 00000000..da9a630b --- /dev/null +++ b/contracts/interfaces/modules/royalty/policies/ILiquidSplitFactory.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title LiquidSplitFactory interface +interface ILiquidSplitFactory { + /// @notice Creates a new LiquidSplitClone contract + /// @param accounts The accounts to initialize the LiquidSplitClone contract with + /// @param initAllocations The initial allocations + /// @param _distributorFee The distributor fee + /// @param owner The owner of the LiquidSplitClone contract + function createLiquidSplitClone( + address[] calldata accounts, + uint32[] calldata initAllocations, + uint32 _distributorFee, + address owner + ) external returns (address); +} \ No newline at end of file diff --git a/contracts/interfaces/modules/royalty/policies/ILiquidSplitMain.sol b/contracts/interfaces/modules/royalty/policies/ILiquidSplitMain.sol new file mode 100644 index 00000000..ba138093 --- /dev/null +++ b/contracts/interfaces/modules/royalty/policies/ILiquidSplitMain.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/// @title LiquidSplitMain interface +interface ILiquidSplitMain { + /// @notice Allows an account to withdraw their accrued and distributed pending amount + /// @param account The account to withdraw from + /// @param withdrawETH The amount of ETH to withdraw + /// @param tokens The tokens to withdraw + function withdraw( + address account, + uint256 withdrawETH, + ERC20[] calldata tokens + ) external; +} \ No newline at end of file diff --git a/contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol b/contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol new file mode 100644 index 00000000..ad97f6b6 --- /dev/null +++ b/contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +/// @title RoyaltyPolicy interface +interface IRoyaltyPolicy { + /// @notice Initializes the royalty policy + /// @param ipId The ipId + /// @param data The data to initialize the policy + function initPolicy(address ipId, bytes calldata data) external; + + /// @notice Allows to pay a royalty + /// @param caller The caller + /// @param ipId The ipId + /// @param token The token to pay + /// @param amount The amount to pay + function onRoyaltyPayment(address caller, address ipId, address token, uint256 amount) external; +} \ No newline at end of file diff --git a/contracts/interfaces/registries/ILicenseRegistry.sol b/contracts/interfaces/registries/ILicenseRegistry.sol new file mode 100644 index 00000000..e33a29b2 --- /dev/null +++ b/contracts/interfaces/registries/ILicenseRegistry.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { Licensing } from "contracts/lib/Licensing.sol"; + +interface ILicenseRegistry { + event LicenseFrameworkCreated( + address indexed creator, + uint256 indexed frameworkId, + Licensing.FrameworkCreationParams frameworkCreationParams + ); + + event PolicyCreated(address indexed creator, uint256 indexed policyId, Licensing.Policy policy); + + event PolicyAddedToIpId(address indexed caller, address indexed ipId, uint256 indexed policyId); + + event LicenseMinted( + address indexed creator, + address indexed receiver, + uint256 indexed licenseId, + uint256 amount, + Licensing.License licenseData + ); + + event IpIdLinkedToParent(address indexed caller, address indexed ipId, address indexed parentIpId); + + function addLicenseFramework( + Licensing.FrameworkCreationParams calldata fwCreation + ) external returns (uint256 frameworkId); + + function addPolicyToIp( + address ipId, + Licensing.Policy memory pol + ) external returns (uint256 policyId, bool isNew, uint256 indexOnIpId); + + function addPolicyToIp(address ipId, uint256 polId) external returns (uint256 indexOnIpId); + + function addPolicy(Licensing.Policy memory pol) external returns (uint256 policyId, bool isNew); + + function mintLicense( + Licensing.License calldata licenseData, + uint256 amount, + address receiver + ) external returns (uint256 licenseId); + + function linkIpToParent(uint256 licenseId, address childIpId, address holder) external; + + /// + /// Getters + /// + + function totalFrameworks() external view returns (uint256); + + function framework(uint256 frameworkId) external view returns (Licensing.Framework memory fw); + + function totalPolicies() external view returns (uint256); + + function policy(uint256 policyId) external view returns (Licensing.Policy memory pol); + + function isPolicyDefined(uint256 policyId) external view returns (bool); + + function policyIdsForIp(address ipId) external view returns (uint256[] memory policyIds); + + function totalPoliciesForIp(address ipId) external view returns (uint256); + + function isPolicyIdSetForIp(address ipId, uint256 policyId) external view returns (bool); + + function policyIdForIpAtIndex(address ipId, uint256 index) external view returns (uint256 policyId); + + function policyForIpAtIndex(address ipId, uint256 index) external view returns (Licensing.Policy memory); + + function isLicensee(uint256 licenseId, address holder) external view returns (bool); + + function isParent(address parentIpId, address childIpId) external view returns (bool); + + function parentIpIds(address ipId) external view returns (address[] memory); + + function totalParentsForIpId(address ipId) external view returns (uint256); +} diff --git a/contracts/interfaces/resolvers/IIPMetadataResolver.sol b/contracts/interfaces/resolvers/IIPMetadataResolver.sol index 81a74317..ab5b95c1 100644 --- a/contracts/interfaces/resolvers/IIPMetadataResolver.sol +++ b/contracts/interfaces/resolvers/IIPMetadataResolver.sol @@ -7,6 +7,15 @@ import { IP } from "contracts/lib/IP.sol"; /// @notice Resolver Interface interface IIPMetadataResolver is IResolver { + event IPMetadataResolverSetRecord(address indexed ipId, IP.MetadataRecord data); + + event IPMetadataResolverSetName(address indexed ipId, string name); + + event IPMetadataResolverSetDescription(address indexed ipId, string description); + + event IPMetadataResolverSetHash(address indexed ipId, bytes32 hash); + + event IPMetadataResolverSetURI(address indexed ipId, string uri); /// @notice Fetches core metadata attributed to a specific IP. function metadata(address ipId) external view returns (IP.Metadata memory); @@ -64,5 +73,4 @@ interface IIPMetadataResolver is IResolver { /// @notice Sets an IP owner defined URI to associate with the IP. /// @param ipId The canonical ID of the specified IP. function setURI(address ipId, string calldata uri) external; - } diff --git a/contracts/modules/RegistrationModule.sol b/contracts/modules/RegistrationModule.sol index 39984a96..fefe4778 100644 --- a/contracts/modules/RegistrationModule.sol +++ b/contracts/modules/RegistrationModule.sol @@ -2,20 +2,22 @@ // See https://github.com/storyprotocol/protocol-contracts/blob/main/StoryProtocol-AlphaTestingAgreement-17942166.3.pdf pragma solidity ^0.8.23; +// external import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; - -import { BaseModule } from "contracts/modules/BaseModule.sol"; +// contracts +import { IRegistrationModule } from "contracts/interfaces/modules/IRegistrationModule.sol"; import { IIPMetadataResolver } from "contracts/interfaces/resolvers/IIPMetadataResolver.sol"; import { REGISTRATION_MODULE_KEY } from "contracts/lib/modules/Module.sol"; import { Errors } from "contracts/lib/Errors.sol"; import { IP } from "contracts/lib/IP.sol"; +import { BaseModule } from "contracts/modules/BaseModule.sol"; /// @title Registration Module /// @notice The registration module is responsible for registration of IP into /// the protocol. During registration, this module will register an IP /// into the protocol, create a resolver, and bind to it any licenses /// and terms specified by the IP registrant (IP account owner). -contract RegistrationModule is BaseModule { +contract RegistrationModule is BaseModule, IRegistrationModule { /// @notice The metadata resolver used by the registration module. IIPMetadataResolver public resolver; @@ -67,6 +69,8 @@ contract RegistrationModule is BaseModule { LICENSE_REGISTRY.addPolicyToIp(ipId, policyId); } + emit RootIPRegistered(msg.sender, ipId, policyId); + return ipId; } @@ -76,14 +80,14 @@ contract RegistrationModule is BaseModule { /// @param tokenId The token id of the NFT bound to the derivative IP. /// @param ipName The name assigned to the new IP. /// @param ipDescription A string description to assign to the IP. - /// @param hash The content hash of the IP being registered. + /// @param contentHash The content hash of the IP being registered. function registerDerivativeIp( uint256 licenseId, address tokenContract, uint256 tokenId, string memory ipName, string memory ipDescription, - bytes32 hash + bytes32 contentHash ) external { // Check that the caller is authorized to perform the registration. // TODO: Perform additional registration authorization logic, allowing @@ -94,7 +98,13 @@ contract RegistrationModule is BaseModule { // Perform core IP registration and IP account creation. address ipId = IP_RECORD_REGISTRY.register(block.chainid, tokenContract, tokenId, address(resolver), true); - ACCESS_CONTROLLER.setPermission(ipId, address(this), address(resolver), IIPMetadataResolver.setMetadata.selector, 1); + ACCESS_CONTROLLER.setPermission( + ipId, + address(this), + address(resolver), + IIPMetadataResolver.setMetadata.selector, + 1 + ); // Perform core IP derivative licensing - the license must be owned by the caller. // TODO: return resulting policy index @@ -106,12 +116,14 @@ contract RegistrationModule is BaseModule { IP.MetadataRecord({ name: ipName, description: ipDescription, - hash: hash, + hash: contentHash, registrationDate: uint64(block.timestamp), registrant: msg.sender, uri: "" }) ); + + emit DerivativeIPRegistered(msg.sender, ipId, licenseId); } /// @notice Gets the protocol-wide module identifier for this module. diff --git a/contracts/modules/dispute-module/DisputeModule.sol b/contracts/modules/dispute-module/DisputeModule.sol index 30345a78..6b07724f 100644 --- a/contracts/modules/dispute-module/DisputeModule.sol +++ b/contracts/modules/dispute-module/DisputeModule.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; -import {ShortStringOps} from "../../utils/ShortStringOps.sol"; -import {IArbitrationPolicy} from "../../../interfaces/modules/dispute-module/policies/IArbitrationPolicy.sol"; -import {IDisputeModule} from "../../../interfaces/modules/dispute-module/IDisputeModule.sol"; - -import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; - -import {Errors} from "../../lib/Errors.sol"; +// external +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +// contracts +import { IDisputeModule } from "contracts/interfaces/modules/dispute/IDisputeModule.sol"; +import { IArbitrationPolicy } from "contracts/interfaces/modules/dispute/policies/IArbitrationPolicy.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { ShortStringOps } from "contracts/utils/ShortStringOps.sol"; /// @title Story Protocol Dispute Module /// @notice The Story Protocol dispute module acts as an enforcement layer for @@ -35,8 +35,8 @@ contract DisputeModule is IDisputeModule, ReentrancyGuard { mapping(address arbitrationPolicy => bool allowed) public isWhitelistedArbitrationPolicy; /// @notice Indicates if an arbitration relayer is whitelisted for a given arbitration policy - mapping(address arbitrationPolicy => mapping(address arbitrationRelayer => bool allowed)) public - isWhitelistedArbitrationRelayer; + mapping(address arbitrationPolicy => mapping(address arbitrationRelayer => bool allowed)) + public isWhitelistedArbitrationRelayer; /// @notice Restricts the calls to the governance address modifier onlyGovernance() { @@ -70,10 +70,11 @@ contract DisputeModule is IDisputeModule, ReentrancyGuard { /// @param _arbitrationPolicy The address of the arbitration policy /// @param _arbPolicyRelayer The address of the arbitration relayer /// @param _allowed Indicates if the arbitration relayer is whitelisted or not - function whitelistArbitrationRelayer(address _arbitrationPolicy, address _arbPolicyRelayer, bool _allowed) - external - onlyGovernance - { + function whitelistArbitrationRelayer( + address _arbitrationPolicy, + address _arbPolicyRelayer, + bool _allowed + ) external onlyGovernance { if (_arbitrationPolicy == address(0)) revert Errors.DisputeModule__ZeroArbitrationPolicy(); if (_arbPolicyRelayer == address(0)) revert Errors.DisputeModule__ZeroArbitrationRelayer(); @@ -104,7 +105,7 @@ contract DisputeModule is IDisputeModule, ReentrancyGuard { bytes32 linkToDisputeSummary = ShortStringOps.stringToBytes32(_linkToDisputeSummary); if (linkToDisputeSummary == bytes32(0)) revert Errors.DisputeModule__ZeroLinkToDisputeSummary(); - + disputeId++; disputes[disputeId] = Dispute({ diff --git a/contracts/modules/dispute-module/policies/ArbitrationPolicySP.sol b/contracts/modules/dispute-module/policies/ArbitrationPolicySP.sol index 5cfc3be3..e403c2eb 100644 --- a/contracts/modules/dispute-module/policies/ArbitrationPolicySP.sol +++ b/contracts/modules/dispute-module/policies/ArbitrationPolicySP.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; -import {IArbitrationPolicy} from "../../../../interfaces/modules/dispute-module/policies/IArbitrationPolicy.sol"; -import {IDisputeModule} from "../../../../interfaces/modules/dispute-module/IDisputeModule.sol"; - -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -import {Errors} from "../../../lib/Errors.sol"; +// external +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// contracts +import { IDisputeModule } from "contracts/interfaces/modules/dispute/IDisputeModule.sol"; +import { IArbitrationPolicy } from "contracts/interfaces/modules/dispute/policies/IArbitrationPolicy.sol"; +import { Errors } from "contracts/lib/Errors.sol"; /// @title Story Protocol Arbitration Policy /// @notice The Story Protocol arbitration policy is a simple policy that @@ -63,7 +63,7 @@ contract ArbitrationPolicySP is IArbitrationPolicy { /// @param _decision The decision of the dispute function onDisputeJudgement(uint256 _disputeId, bool _decision, bytes calldata) external onlyDisputeModule { if (_decision) { - (, address disputeInitiator,,,) = IDisputeModule(DISPUTE_MODULE).disputes(_disputeId); + (, address disputeInitiator, , , ) = IDisputeModule(DISPUTE_MODULE).disputes(_disputeId); IERC20(PAYMENT_TOKEN).safeTransfer(disputeInitiator, ARBITRATION_PRICE); } } diff --git a/contracts/modules/royalty-module/RoyaltyModule.sol b/contracts/modules/royalty-module/RoyaltyModule.sol index d866ab4c..63b53ddc 100644 --- a/contracts/modules/royalty-module/RoyaltyModule.sol +++ b/contracts/modules/royalty-module/RoyaltyModule.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; -import {IRoyaltyModule} from "../../../interfaces/modules/royalty-module/IRoyaltyModule.sol"; -import {IRoyaltyPolicy} from "../../../interfaces/modules/royalty-module/policies/IRoyaltyPolicy.sol"; - -import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; - -import {Errors} from "../../lib/Errors.sol"; +// external +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +// contracts +import { IRoyaltyModule } from "contracts/interfaces/modules/royalty/IRoyaltyModule.sol"; +import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol"; +import { Errors } from "contracts/lib/Errors.sol"; /// @title Story Protocol Royalty Module /// @notice The Story Protocol royalty module allows to set royalty policies an ipId @@ -51,11 +51,11 @@ contract RoyaltyModule is IRoyaltyModule, ReentrancyGuard { /// @param _ipId The ipId /// @param _royaltyPolicy The address of the royalty policy /// @param _data The data to initialize the policy - function setRoyaltyPolicy(address _ipId, address _royaltyPolicy, bytes calldata _data) - external - onlyLicenseModule - nonReentrant - { + function setRoyaltyPolicy( + address _ipId, + address _royaltyPolicy, + bytes calldata _data + ) external onlyLicenseModule nonReentrant { // TODO: make call to ensure ipId exists/has been registered if (!isWhitelistedRoyaltyPolicy[_royaltyPolicy]) revert Errors.RoyaltyModule__NotWhitelistedRoyaltyPolicy(); if (royaltyPolicies[_ipId] != address(0)) revert Errors.RoyaltyModule__AlreadySetRoyaltyPolicy(); diff --git a/contracts/modules/royalty-module/policies/RoyaltyPolicyLS.sol b/contracts/modules/royalty-module/policies/RoyaltyPolicyLS.sol index 36ac7267..3a8145ca 100644 --- a/contracts/modules/royalty-module/policies/RoyaltyPolicyLS.sol +++ b/contracts/modules/royalty-module/policies/RoyaltyPolicyLS.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.23; -import {ILiquidSplitFactory} from "../../../../interfaces/modules/royalty-module/policies/ILiquidSplitFactory.sol"; -import {IRoyaltyPolicy} from "../../../../interfaces/modules/royalty-module/policies/IRoyaltyPolicy.sol"; -import {ILiquidSplitClone} from "../../../../interfaces/modules/royalty-module/policies/ILiquidSplitClone.sol"; -import {ILiquidSplitMain} from "../../../../interfaces/modules/royalty-module/policies/ILiquidSplitMain.sol"; - -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -import {Errors} from "../../../lib/Errors.sol"; +// external +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +// contracts +import { ILiquidSplitClone } from "contracts/interfaces/modules/royalty/policies/ILiquidSplitClone.sol"; +import { ILiquidSplitFactory } from "contracts/interfaces/modules/royalty/policies/ILiquidSplitFactory.sol"; +import { ILiquidSplitMain } from "contracts/interfaces/modules/royalty/policies/ILiquidSplitMain.sol"; +import { IRoyaltyPolicy } from "contracts/interfaces/modules/royalty/policies/IRoyaltyPolicy.sol"; +import { Errors } from "contracts/lib/Errors.sol"; /// @title Liquid Split Royalty Policy /// @notice The LiquidSplit royalty policy splits royalties in accordance with @@ -54,15 +54,18 @@ contract RoyaltyPolicyLS is IRoyaltyPolicy { /// @param _ipId The ipId /// @param _data The data to initialize the policy function initPolicy(address _ipId, bytes calldata _data) external onlyRoyaltyModule { - (address[] memory accounts, uint32[] memory initAllocations, uint32 distributorFee, address splitOwner) = - abi.decode(_data, (address[], uint32[], uint32, address)); + (address[] memory accounts, uint32[] memory initAllocations, uint32 distributorFee, address splitOwner) = abi + .decode(_data, (address[], uint32[], uint32, address)); // TODO: input validation: accounts & initAllocations - can we make up to 1000 parents with tx going through - if not alternative may be to create new contract to claim RNFTs // TODO: input validation: distributorFee // TODO: input validation: splitOwner address splitClone = ILiquidSplitFactory(LIQUID_SPLIT_FACTORY).createLiquidSplitClone( - accounts, initAllocations, distributorFee, splitOwner + accounts, + initAllocations, + distributorFee, + splitOwner ); splitClones[_ipId] = splitClone; @@ -73,9 +76,12 @@ contract RoyaltyPolicyLS is IRoyaltyPolicy { /// @param _token The token to distribute /// @param _accounts The accounts to distribute to /// @param _distributorAddress The distributor address - function distributeFunds(address _ipId, address _token, address[] calldata _accounts, address _distributorAddress) - external - { + function distributeFunds( + address _ipId, + address _token, + address[] calldata _accounts, + address _distributorAddress + ) external { ILiquidSplitClone(splitClones[_ipId]).distributeFunds(_token, _accounts, _distributorAddress); } @@ -92,10 +98,12 @@ contract RoyaltyPolicyLS is IRoyaltyPolicy { /// @param _ipId The ipId /// @param _token The token to pay /// @param _amount The amount to pay - function onRoyaltyPayment(address _caller, address _ipId, address _token, uint256 _amount) - external - onlyRoyaltyModule - { + function onRoyaltyPayment( + address _caller, + address _ipId, + address _token, + uint256 _amount + ) external onlyRoyaltyModule { address destination = splitClones[_ipId]; IERC20(_token).safeTransferFrom(_caller, destination, _amount); } diff --git a/contracts/registries/LicenseRegistry.sol b/contracts/registries/LicenseRegistry.sol index 7dc26c7b..ff736f31 100644 --- a/contracts/registries/LicenseRegistry.sol +++ b/contracts/registries/LicenseRegistry.sol @@ -1,16 +1,18 @@ // SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; -pragma solidity ^0.8.20; - -import { Licensing } from "../lib/Licensing.sol"; -import { IParamVerifier } from "../interfaces/licensing/IParamVerifier.sol"; -import { Errors } from "../lib/Errors.sol"; -import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +// external import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +// contracts +import { IParamVerifier } from "contracts/interfaces/licensing/IParamVerifier.sol"; +import { ILicenseRegistry } from "contracts/interfaces/registries/ILicenseRegistry.sol"; +import { Errors } from "contracts/lib/Errors.sol"; +import { Licensing } from "contracts/lib/Licensing.sol"; // TODO: consider disabling operators/approvals on creation -contract LicenseRegistry is ERC1155 { +contract LicenseRegistry is ERC1155, ILicenseRegistry { using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using Strings for *; @@ -76,7 +78,7 @@ contract LicenseRegistry is ERC1155 { fwCreation.linkParentParamDefaultValues ); // Should we add a label? - // TODO: emit + emit LicenseFrameworkCreated(msg.sender, _totalFrameworks, fwCreation); return _totalFrameworks; } @@ -187,7 +189,7 @@ contract LicenseRegistry is ERC1155 { if (newPol) { _totalPolicies = polId; _policies[polId] = pol; - // TODO: emit + emit PolicyCreated(msg.sender, polId, pol); } return (polId, newPol); } @@ -203,7 +205,7 @@ contract LicenseRegistry is ERC1155 { if (!policySet.add(policyId)) { revert Errors.LicenseRegistry__PolicyAlreadySetForIpId(); } - // TODO: emit + emit PolicyAddedToIpId(msg.sender, ipId, policyId); return policySet.length() - 1; } @@ -295,7 +297,7 @@ contract LicenseRegistry is ERC1155 { if (isNew) { _totalLicenses = licenseId; _licenses[licenseId] = licenseData; - // TODO: emit + emit LicenseMinted(msg.sender, receiver, licenseId, amount, licenseData); } _mint(receiver, licenseId, amount, ""); return licenseId; @@ -331,7 +333,7 @@ contract LicenseRegistry is ERC1155 { } Licensing.Policy memory pol = policy(licenseData.policyId); - + Licensing.Parameter[] memory linkParams = _frameworks[pol.frameworkId].linkParentParams; bytes[] memory linkParamValues = pol.linkParentParamValues; for (uint256 i = 0; i < linkParams.length; i++) { @@ -357,7 +359,7 @@ contract LicenseRegistry is ERC1155 { revert Errors.LicenseRegistry__ParentIdEqualThanChild(); } _ipIdParents[childIpId].add(parent); - // TODO: emit + emit IpIdLinkedToParent(msg.sender, childIpId, parent); } // Burn license diff --git a/contracts/resolvers/IPMetadataResolver.sol b/contracts/resolvers/IPMetadataResolver.sol index 72122751..fb76323e 100644 --- a/contracts/resolvers/IPMetadataResolver.sol +++ b/contracts/resolvers/IPMetadataResolver.sol @@ -113,6 +113,7 @@ contract IPMetadataResolver is IIPMetadataResolver, ResolverBase { /// @param newMetadata The new metadata to set for the IP. function setMetadata(address ipId, IP.MetadataRecord calldata newMetadata) external onlyAuthorized(ipId) { _records[ipId] = newMetadata; + emit IPMetadataResolverSetRecord(ipId, newMetadata); } /// @notice Sets the name associated with an IP. @@ -120,6 +121,7 @@ contract IPMetadataResolver is IIPMetadataResolver, ResolverBase { /// @param newName The new string name to associate with the IP. function setName(address ipId, string calldata newName) external onlyAuthorized(ipId) { _records[ipId].name = newName; + emit IPMetadataResolverSetName(ipId, newName); } /// @notice Sets the description associated with an IP. @@ -127,6 +129,7 @@ contract IPMetadataResolver is IIPMetadataResolver, ResolverBase { /// @param newDescription The string description to associate with the IP. function setDescription(address ipId, string calldata newDescription) external onlyAuthorized(ipId) { _records[ipId].description = newDescription; + emit IPMetadataResolverSetDescription(ipId, newDescription); } /// @notice Sets the keccak-256 hash associated with an IP. @@ -134,6 +137,7 @@ contract IPMetadataResolver is IIPMetadataResolver, ResolverBase { /// @param newHash The keccak-256 hash to associate with the IP. function setHash(address ipId, bytes32 newHash) external onlyAuthorized(ipId) { _records[ipId].hash = newHash; + emit IPMetadataResolverSetHash(ipId, newHash); } /// @notice Sets an IP owner defined URI to associate with the IP. @@ -141,6 +145,7 @@ contract IPMetadataResolver is IIPMetadataResolver, ResolverBase { /// @param newURI The new token URI to set for the IP. function setURI(address ipId, string calldata newURI) external onlyAuthorized(ipId) { _records[ipId].uri = newURI; + emit IPMetadataResolverSetURI(ipId, newURI); } /// @notice Checks whether the resolver interface is supported.