Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* protocol and individual contract pausing
  • Loading branch information
Ramarti authored Apr 14, 2024
1 parent 99a23ba commit cf2f07f
Show file tree
Hide file tree
Showing 22 changed files with 591 additions and 45 deletions.
18 changes: 12 additions & 6 deletions contracts/access/AccessController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ pragma solidity 0.8.23;

import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import { IAccessController } from "../interfaces/access/IAccessController.sol";
import { IModuleRegistry } from "../interfaces/registries/IModuleRegistry.sol";
import { IIPAccountRegistry } from "../interfaces/registries/IIPAccountRegistry.sol";
import { IModuleRegistry } from "../interfaces/registries/IModuleRegistry.sol";
import { IPAccountChecker } from "../lib/registries/IPAccountChecker.sol";
import { IIPAccount } from "../interfaces/IIPAccount.sol";
import { ProtocolPausableUpgradeable } from "../pause/ProtocolPausableUpgradeable.sol";
import { AccessPermission } from "../lib/AccessPermission.sol";
import { IPAccountChecker } from "../lib/registries/IPAccountChecker.sol";
import { Errors } from "../lib/Errors.sol";

/// @title AccessController
Expand All @@ -29,7 +29,7 @@ import { Errors } from "../lib/Errors.sol";
/// - setPermission: Sets the permission for a specific function call.
/// - getPermission: Returns the permission level for a specific function call.
/// - checkPermission: Checks if a specific function call is allowed.
contract AccessController is IAccessController, AccessManagedUpgradeable, UUPSUpgradeable {
contract AccessController is IAccessController, ProtocolPausableUpgradeable, UUPSUpgradeable {
using IPAccountChecker for IIPAccountRegistry;

/// @dev The storage struct of AccessController.
Expand Down Expand Up @@ -60,7 +60,8 @@ contract AccessController is IAccessController, AccessManagedUpgradeable, UUPSUp
if (accessManager == address(0)) {
revert Errors.AccessController__ZeroAccessManager();
}
__AccessManaged_init(accessManager);
__ProtocolPausable_init(accessManager);
__UUPSUpgradeable_init();
}

/// @notice Sets the addresses of the IP account registry and the module registry
Expand Down Expand Up @@ -106,8 +107,13 @@ contract AccessController is IAccessController, AccessManagedUpgradeable, UUPSUp
/// @param to The address that can be called by the `signer` (currently only modules can be `to`)
/// @param func The function selector of `to` that can be called by the `signer` on behalf of the `ipAccount`
/// @param permission The new permission level
function setPermission(address ipAccount, address signer, address to, bytes4 func, uint8 permission) public {
// TODO: Reintroduce pause
function setPermission(
address ipAccount,
address signer,
address to,
bytes4 func,
uint8 permission
) public whenNotPaused {
// IPAccount and signer does not support wildcard permission
if (ipAccount == address(0)) {
revert Errors.AccessController__IPAccountIsZeroAddress();
Expand Down
40 changes: 40 additions & 0 deletions contracts/interfaces/pause/IProtocolPauseAdmin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.23;

/// @title ProtocolPauseAdmin
/// @notice Contract that allows the pausing and unpausing of the protocol. It allows adding and removing
/// pausable contracts, which are contracts that implement the `IPausable` interface.
/// @dev The contract is restricted to be used only the admin role defined in the `AccessManaged` contract.
/// NOTE: If a contract is upgraded to remove the `IPausable` interface, it should be removed from the list of pausables
/// before the upgrade, otherwise pause() and unpause() will revert.
interface IProtocolPauseAdmin {
/// @notice Emitted when a pausable contract is added.
event PausableAdded(address indexed pausable);
/// @notice Emitted when a pausable contract is removed.
event PausableRemoved(address indexed pausable);
/// @notice Emitted when the protocol is paused.
event ProtocolPaused();
/// @notice Emitted when the protocol is unpaused.
event ProtocolUnpaused();

/// @notice Adds a pausable contract to the list of pausables.
function addPausable(address pausable) external;

/// @notice Removes a pausable contract from the list of pausables.
function removePausable(address pausable) external;

/// @notice Pauses the protocol by calling the pause() function on all pausable contracts.
function pause() external;

/// @notice Unpauses the protocol by calling the unpause() function on all pausable contracts.
function unpause() external;

/// @notice Checks if a pausable contract is registered.
function isPausableRegistered(address pausable) external view returns (bool);

/// @notice Returns true if all the pausable contracts are paused.
function isAllProtocolPaused() external view returns (bool);

/// @notice Returns the list of pausable contracts.
function pausables() external view returns (address[] memory);
}
9 changes: 9 additions & 0 deletions contracts/lib/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ library Errors {
error IpRoyaltyVault__ClaimerNotAnAncestor();
error IpRoyaltyVault__IpTagged();
error IpRoyaltyVault__ZeroDisputeModule();
error IpRoyaltyVault__EnforcedPause();

////////////////////////////////////////////////////////////////////////////
// ModuleRegistry //
Expand Down Expand Up @@ -348,4 +349,12 @@ library Errors {
// CoreMetadataModule //
////////////////////////////////////////////////////////////////////////////
error CoreMetadataModule__MetadataAlreadyFrozen();

////////////////////////////////////////////////////////////////////////////
// ProtocolPauseAdmin //
////////////////////////////////////////////////////////////////////////////
error ProtocolPauseAdmin__ZeroAddress();
error ProtocolPauseAdmin__AddingPausedContract();
error ProtocolPauseAdmin__PausableAlreadyAdded();
error ProtocolPauseAdmin__PausableNotFound();
}
5 changes: 4 additions & 1 deletion contracts/lib/ProtocolAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ library ProtocolAdmin {
string public constant PROTOCOL_ADMIN_ROLE_LABEL = "PROTOCOL_ADMIN_ROLE";
/// @notice Public role, as it is used in AccessManager
uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1
/// @notice Upgrader role, as it is used in AccessManager
/// @notice Upgrader role. Grants ability to upgrade UUPS contracts.
uint64 public constant UPGRADER_ROLE = 1;
string public constant UPGRADER_ROLE_LABEL = "UPGRADER_ROLE";
/// @notice Pause admin role. Grants ability to pause and unpause contracts.
uint64 public constant PAUSE_ADMIN_ROLE = 2;
string public constant PAUSE_ADMIN_ROLE_LABEL = "PAUSE_ADMIN_ROLE";
}
17 changes: 10 additions & 7 deletions contracts/modules/dispute/DisputeModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ pragma solidity 0.8.23;
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";

import { DISPUTE_MODULE_KEY } from "../../lib/modules/Module.sol";
Expand All @@ -17,14 +15,15 @@ import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.
import { IArbitrationPolicy } from "../../interfaces/modules/dispute/policies/IArbitrationPolicy.sol";
import { Errors } from "../../lib/Errors.sol";
import { ShortStringOps } from "../../utils/ShortStringOps.sol";
import { ProtocolPausableUpgradeable } from "../../pause/ProtocolPausableUpgradeable.sol";

/// @title Dispute Module
/// @notice The dispute module acts as an enforcement layer for IP assets that allows raising and resolving disputes
/// through arbitration by judges.
contract DisputeModule is
IDisputeModule,
BaseModule,
AccessManagedUpgradeable,
ProtocolPausableUpgradeable,
ReentrancyGuardUpgradeable,
AccessControlled,
UUPSUpgradeable,
Expand Down Expand Up @@ -96,7 +95,7 @@ contract DisputeModule is
if (accessManager == address(0)) {
revert Errors.DisputeModule__ZeroAccessManager();
}
__AccessManaged_init(accessManager);
__ProtocolPausable_init(accessManager);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
__Multicall_init();
Expand Down Expand Up @@ -180,7 +179,7 @@ contract DisputeModule is
string memory linkToDisputeEvidence,
bytes32 targetTag,
bytes calldata data
) external nonReentrant returns (uint256) {
) external nonReentrant whenNotPaused returns (uint256) {
if (!IP_ASSET_REGISTRY.isRegistered(targetIpId)) revert Errors.DisputeModule__NotRegisteredIpId();
DisputeModuleStorage storage $ = _getDisputeModuleStorage();
if (!$.isWhitelistedDisputeTag[targetTag]) revert Errors.DisputeModule__NotWhitelistedDisputeTag();
Expand Down Expand Up @@ -222,7 +221,11 @@ contract DisputeModule is
/// @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 nonReentrant {
function setDisputeJudgement(
uint256 disputeId,
bool decision,
bytes calldata data
) external nonReentrant whenNotPaused {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();

Dispute memory dispute = $.disputes[disputeId];
Expand Down Expand Up @@ -269,7 +272,7 @@ contract DisputeModule is
address parentIpId,
address derivativeIpId,
uint256 parentDisputeId
) external {
) external whenNotPaused {
DisputeModuleStorage storage $ = _getDisputeModuleStorage();

Dispute memory parentDispute = $.disputes[parentDisputeId];
Expand Down
13 changes: 6 additions & 7 deletions contracts/modules/licensing/LicensingModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import { IIPAccount } from "../../interfaces/IIPAccount.sol";
import { IModule } from "../../interfaces/modules/base/IModule.sol";
Expand All @@ -28,6 +26,7 @@ import { IMintingFeeModule } from "contracts/interfaces/modules/licensing/IMinti
import { IPAccountStorageOps } from "../../lib/IPAccountStorageOps.sol";
import { IHookModule } from "../../interfaces/modules/base/IHookModule.sol";
import { ILicenseToken } from "../../interfaces/ILicenseToken.sol";
import { ProtocolPausableUpgradeable } from "../../pause/ProtocolPausableUpgradeable.sol";

/// @title Licensing Module
/// @notice Licensing module is the main entry point for the licensing system. It is responsible for:
Expand All @@ -39,7 +38,7 @@ contract LicensingModule is
ILicensingModule,
BaseModule,
ReentrancyGuardUpgradeable,
AccessManagedUpgradeable,
ProtocolPausableUpgradeable,
UUPSUpgradeable
{
using ERC165Checker for address;
Expand Down Expand Up @@ -102,7 +101,7 @@ contract LicensingModule is
}
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
__AccessManaged_init(accessManager);
__ProtocolPausable_init(accessManager);
}

/// @notice Attaches license terms to an IP.
Expand Down Expand Up @@ -146,7 +145,7 @@ contract LicensingModule is
uint256 amount,
address receiver,
bytes calldata royaltyContext
) external returns (uint256 startLicenseTokenId) {
) external whenNotPaused returns (uint256 startLicenseTokenId) {
if (amount == 0) {
revert Errors.LicensingModule__MintAmountZero();
}
Expand Down Expand Up @@ -211,7 +210,7 @@ contract LicensingModule is
uint256[] calldata licenseTermsIds,
address licenseTemplate,
bytes calldata royaltyContext
) external nonReentrant verifyPermission(childIpId) {
) external whenNotPaused nonReentrant verifyPermission(childIpId) {
if (parentIpIds.length != licenseTermsIds.length) {
revert Errors.LicensingModule__LicenseTermsLengthMismatch(parentIpIds.length, licenseTermsIds.length);
}
Expand Down Expand Up @@ -275,7 +274,7 @@ contract LicensingModule is
address childIpId,
uint256[] calldata licenseTokenIds,
bytes calldata royaltyContext
) external nonReentrant verifyPermission(childIpId) {
) external nonReentrant whenNotPaused verifyPermission(childIpId) {
if (licenseTokenIds.length == 0) {
revert Errors.LicensingModule__NoLicenseToken();
}
Expand Down
9 changes: 4 additions & 5 deletions contracts/modules/royalty/RoyaltyModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
// solhint-disable-next-line max-line-length
import { AccessManagedUpgradeable } from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import { BaseModule } from "../BaseModule.sol";
import { IRoyaltyModule } from "../../interfaces/modules/royalty/IRoyaltyModule.sol";
Expand All @@ -15,13 +13,14 @@ import { IDisputeModule } from "../../interfaces/modules/dispute/IDisputeModule.
import { Errors } from "../../lib/Errors.sol";
import { ROYALTY_MODULE_KEY } from "../../lib/modules/Module.sol";
import { BaseModule } from "../BaseModule.sol";
import { ProtocolPausableUpgradeable } from "../../pause/ProtocolPausableUpgradeable.sol";

/// @title Story Protocol Royalty Module
/// @notice The Story Protocol royalty module allows to set royalty policies an IP asset and pay royalties as a
/// derivative IP.
contract RoyaltyModule is
IRoyaltyModule,
AccessManagedUpgradeable,
ProtocolPausableUpgradeable,
ReentrancyGuardUpgradeable,
BaseModule,
UUPSUpgradeable
Expand Down Expand Up @@ -61,7 +60,7 @@ contract RoyaltyModule is
if (accessManager == address(0)) {
revert Errors.RoyaltyModule__ZeroAccessManager();
}
__AccessManaged_init(accessManager);
__ProtocolPausable_init(accessManager);
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
}
Expand Down Expand Up @@ -186,7 +185,7 @@ contract RoyaltyModule is
address payerIpId,
address token,
uint256 amount
) external nonReentrant {
) external nonReentrant whenNotPaused {
RoyaltyModuleStorage storage $ = _getRoyaltyModuleStorage();
if (!$.isWhitelistedRoyaltyToken[token]) revert Errors.RoyaltyModule__NotWhitelistedRoyaltyToken();

Expand Down
24 changes: 19 additions & 5 deletions contracts/modules/royalty/policies/IpRoyaltyVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.23;

import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
// solhint-disable-next-line max-line-length
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/security/ReentrancyGuardUpgradeable.sol";
// solhint-disable-next-line max-line-length
Expand Down Expand Up @@ -59,6 +60,12 @@ contract IpRoyaltyVault is IIpRoyaltyVault, ERC20SnapshotUpgradeable, Reentrancy
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IDisputeModule public immutable DISPUTE_MODULE;

modifier whenNotPaused() {
// DEV NOTE: If we upgrade RoyaltyPolicyLAP to not pausable, we need to remove this.
if (PausableUpgradeable(address(ROYALTY_POLICY_LAP)).paused()) revert Errors.IpRoyaltyVault__EnforcedPause();
_;
}

/// @notice Constructor
/// @param royaltyPolicyLAP The address of the royalty policy LAP
/// @param disputeModule The address of the dispute module
Expand Down Expand Up @@ -116,7 +123,7 @@ contract IpRoyaltyVault is IIpRoyaltyVault, ERC20SnapshotUpgradeable, Reentrancy

/// @notice Snapshots the claimable revenue and royalty token amounts
/// @return snapshotId The snapshot id
function snapshot() external returns (uint256) {
function snapshot() external whenNotPaused returns (uint256) {
IpRoyaltyVaultStorage storage $ = _getIpRoyaltyVaultStorage();

if (block.timestamp - $.lastSnapshotTimestamp < ROYALTY_POLICY_LAP.getSnapshotInterval())
Expand Down Expand Up @@ -158,14 +165,21 @@ contract IpRoyaltyVault is IIpRoyaltyVault, ERC20SnapshotUpgradeable, Reentrancy
/// @param snapshotId The snapshot id
/// @param token The revenue token to claim
/// @return The amount of revenue token claimable
function claimableRevenue(address account, uint256 snapshotId, address token) external view returns (uint256) {
function claimableRevenue(
address account,
uint256 snapshotId,
address token
) external view whenNotPaused returns (uint256) {
return _claimableRevenue(account, snapshotId, token);
}

/// @notice Allows token holders to claim revenue token based on the token balance at certain snapshot
/// @param snapshotId The snapshot id
/// @param tokenList The list of revenue tokens to claim
function claimRevenueByTokenBatch(uint256 snapshotId, address[] calldata tokenList) external nonReentrant {
function claimRevenueByTokenBatch(
uint256 snapshotId,
address[] calldata tokenList
) external nonReentrant whenNotPaused {
IpRoyaltyVaultStorage storage $ = _getIpRoyaltyVaultStorage();

for (uint256 i = 0; i < tokenList.length; i++) {
Expand All @@ -183,7 +197,7 @@ contract IpRoyaltyVault is IIpRoyaltyVault, ERC20SnapshotUpgradeable, Reentrancy
/// @notice Allows token holders to claim by a list of snapshot ids based on the token balance at certain snapshot
/// @param snapshotIds The list of snapshot ids
/// @param token The revenue token to claim
function claimRevenueBySnapshotBatch(uint256[] memory snapshotIds, address token) external {
function claimRevenueBySnapshotBatch(uint256[] memory snapshotIds, address token) external whenNotPaused {
IpRoyaltyVaultStorage storage $ = _getIpRoyaltyVaultStorage();

uint256 claimableToken;
Expand All @@ -200,7 +214,7 @@ contract IpRoyaltyVault is IIpRoyaltyVault, ERC20SnapshotUpgradeable, Reentrancy

/// @notice Allows ancestors to claim the royalty tokens and any accrued revenue tokens
/// @param ancestorIpId The ip id of the ancestor to whom the royalty tokens belong to
function collectRoyaltyTokens(address ancestorIpId) external nonReentrant {
function collectRoyaltyTokens(address ancestorIpId) external nonReentrant whenNotPaused {
IpRoyaltyVaultStorage storage $ = _getIpRoyaltyVaultStorage();

(, , , address[] memory ancestors, uint32[] memory ancestorsRoyalties) = ROYALTY_POLICY_LAP.getRoyaltyData(
Expand Down
Loading

0 comments on commit cf2f07f

Please sign in to comment.