Contract Source Code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {IPausable} from "src/interfaces/IPausable.sol";
contract Pauser is Auth {
// ========================================= STATE =========================================
/**
* @notice List of contracts that can be paused and unpaused using:
* - `pauseAll`
* - `unpauseAll`
*/
IPausable[] internal pausables;
/**
* @notice Used to pause calls to `deposit` and `depositWithPermit`.
*/
bool public isPaused;
/**
* @notice Maps a sender to a pausable contract.
* @dev Used to pause and unpause using `senderPause` and `senderUnpause`.
*/
mapping(address => IPausable) public senderToPausable;
//============================== ERRORS ===============================
error Pauser__IndexOutOfBounds();
//============================== EVENTS ===============================
event PausablePaused(address indexed pausable);
event PausableUnpaused(address indexed pausable);
event PausableAdded(address indexed pausable);
event PausableRemoved(address indexed pausable);
event SenderToPausableUpdated(address indexed sender, address indexed pausable);
//============================== IMMUTABLES ===============================
constructor(address _owner, Authority _authority, IPausable[] memory _pausables) Auth(_owner, _authority) {
for (uint256 i = 0; i < _pausables.length; ++i) {
pausables.push(_pausables[i]);
}
}
// ========================================= ADMIN FUNCTIONS =========================================
/**
* @notice Adds a contract to the list of pausables.
* @dev Callable by PAUSER_ADMIN_ROLE.
*/
function addPausable(IPausable _pausable) external requiresAuth {
pausables.push(_pausable);
emit PausableAdded(address(_pausable));
}
/**
* @notice Removes a contract from the list of pausables.
* @dev Callable by PAUSER_ADMIN_ROLE.
*/
function removePausable(uint256 index) external requiresAuth {
uint256 pausablesLength = pausables.length;
if (index >= pausablesLength) {
revert Pauser__IndexOutOfBounds();
}
address removed = address(pausables[index]);
pausables[index] = pausables[pausablesLength - 1];
pausables.pop();
emit PausableRemoved(removed);
}
/**
* @notice Updates the index of the pausable contract that the sender can pause and unpause.
* @dev Callable by PAUSER_ADMIN_ROLE.
*/
function updateSenderToPausable(address sender, IPausable pausable) external requiresAuth {
senderToPausable[sender] = pausable;
emit SenderToPausableUpdated(sender, address(pausable));
}
// ========================================= GENERIC PAUSER FUNCTIONS =========================================
/**
* @notice Pauses a single pausable contract.
* @dev Callable by GENERIC_PAUSER_ROLE.
*/
function pauseSingle(IPausable pausable) external requiresAuth {
pausable.pause();
emit PausablePaused(address(pausable));
}
/**
* @notice Unpauses a single pausable contract.
* @dev Callable by GENERIC_UNPAUSER_ROLE.
*/
function unpauseSingle(IPausable pausable) external requiresAuth {
pausable.unpause();
emit PausableUnpaused(address(pausable));
}
/**
* @notice Pauses multiple pausable contracts.
* @dev Callable by GENERIC_PAUSER_ROLE.
*/
function pauseMultiple(IPausable[] calldata _pausables) external requiresAuth {
for (uint256 i = 0; i < _pausables.length; ++i) {
_pausables[i].pause();
emit PausablePaused(address(_pausables[i]));
}
}
/**
* @notice Unpauses multiple pausable contracts.
* @dev Callable by GENERIC_UNPAUSER_ROLE.
*/
function unpauseMultiple(IPausable[] calldata _pausables) external requiresAuth {
for (uint256 i = 0; i < _pausables.length; ++i) {
_pausables[i].unpause();
emit PausableUnpaused(address(_pausables[i]));
}
}
// ========================================= PAUSABLES ALL FUNCTIONS =========================================
/**
* @notice Pauses all pausable contracts.
* @dev Callable by PAUSE_ALL_ROLE.
*/
function pauseAll() external requiresAuth {
for (uint256 i = 0; i < pausables.length; ++i) {
pausables[i].pause();
emit PausablePaused(address(pausables[i]));
}
}
/**
* @notice Unpauses all pausable contracts.
* @dev Callable by UNPAUSE_ALL_ROLE.
*/
function unpauseAll() external requiresAuth {
for (uint256 i = 0; i < pausables.length; ++i) {
pausables[i].unpause();
emit PausableUnpaused(address(pausables[i]));
}
}
// ========================================= SENDER FUNCTIONS =========================================
/**
* @notice The below functions can be marked as publically callable, as the `senderToPausable` mapping
* must be updated by an admin in order for the call to succeed. The main advantage of this
* is needing less overhead to explicilty grant a role to pausing bots.
* However if security is of upmost importance, then seperate roles can be created for each function.
*/
/**
* @notice Pauses senders pausable contract.
* @dev Callable by PUBLIC or SENDER_PAUSER_ROLE.
*/
function senderPause() external requiresAuth {
IPausable pausable = senderToPausable[msg.sender];
pausable.pause();
emit PausablePaused(address(pausable));
}
/**
* @notice Unpauses senders pausable contract.
* @dev Callable by PUBLIC or SENDER_UNPAUSER_ROLE.
*/
function senderUnpause() external requiresAuth {
IPausable pausable = senderToPausable[msg.sender];
pausable.unpause();
emit PausableUnpaused(address(pausable));
}
// ========================================= VIEW FUNCTIONS =========================================
/**
* @notice Returns the list of pausable contracts.
*/
function getPausables() external view returns (IPausable[] memory) {
return pausables;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
interface IPausable {
function pause() external;
function unpause() external;
}