Skip to content

Commit

Permalink
v0 working
Browse files Browse the repository at this point in the history
  • Loading branch information
uintgroup committed Aug 10, 2023
1 parent bc5ba89 commit 6409a81
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 24 deletions.
13 changes: 11 additions & 2 deletions src/Counter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ import {SelectivePausable} from "./SelectivePausable.sol";
contract Counter is SelectivePausable {
uint256 public number;

function setNumber(uint256 newNumber) public whenNotPaused(false) {
function setNumber(
uint256 newNumber,
bool pauseAfterCall
) public whenNotPausedSelective(pauseAfterCall) {
number = newNumber;
}

function increment() public whenNotPaused(true) {
function increment(
bool pauseAfterCall
) public whenNotPausedSelective(pauseAfterCall) {
number++;
}

function setIsPaused(bytes4 _functionSelector, bool _isPaused) external {
_setIsPaused(_functionSelector, _isPaused);
}
}
74 changes: 55 additions & 19 deletions src/SelectivePausable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,88 @@ pragma solidity ^0.8.21;
* @title SelectivePausable
* @author @uintgroup @curi0n-s
* @notice pauses contract functions using each function's selector
* @dev replaces OpenZeppelin's Pausable.sol (whenNotPaused would conflict)
* @dev can replace or work in tandem with OpenZeppelin's Pausable.sol
*/

abstract contract SelectivePausable {
// @notice revert with this custom error when array lengths do not match
/**
* @dev Revert with this custom error when array lengths do not match.
*/
error ArrayLengthMismatch();

// @notice revert with this custom error when a function is called while paused
error FunctionIsPaused();
/**
* @dev Revert with this custom error when a function is called while paused.
*/
error FunctionIsPaused(bytes4 functionSelector);

// @notice emitted when a functions pause state is changed
/**
* @dev Emitted when a functions pause state is changed
*/
event FunctionIsPausedUpdate(
address indexed sender,
bytes4 indexed functionSelector,
bool isPaused
bool indexed isPaused
);

/// @notice function is paused by admins: functionId => bool
/**
* @dev Indicates whether function is paused: functionId => bool.
*/
mapping(bytes4 => bool) public functionIsPaused;

/// @dev reverts if the function of the function selector in msg.sig is paused
modifier whenNotPaused(bool _pauseAfterCall) {
if (functionIsPaused[msg.sig]) {
revert FunctionIsPaused();
/**
* @dev Reverts if the function of the function selector in _msgSig() is paused.
*/
modifier whenNotPausedSelective(bool _pauseAfterCall) {
if (functionIsPaused[_msgSig()]) {
revert FunctionIsPaused(_msgSig());
}
_;
functionIsPaused[msg.sig] = _pauseAfterCall;
functionIsPaused[_msgSig()] = _pauseAfterCall;
}

/// @dev modifies pause state of single function
function setIsPaused(bytes4 _functionSelector, bool _isPaused) external {
/**
* @dev Modifies pause state of a single function.
*/
function _setIsPaused(
bytes4 _functionSelector,
bool _isPaused
) internal virtual {
functionIsPaused[_functionSelector] = _isPaused;
emit FunctionIsPausedUpdate(_functionSelector, _isPaused);
emit FunctionIsPausedUpdate(_msgSender(), _functionSelector, _isPaused);
}

/// @dev modifies pause state of multiple functions
function batchSetFunctionIsPaused(
/**
* @dev Modifies pause state of multiple functions.
*/
function _batchSetFunctionIsPaused(
bytes4[] calldata _selectors,
bool[] calldata _isPaused
) external {
) internal virtual {
if (_selectors.length != _isPaused.length) {
revert ArrayLengthMismatch();
}

for (uint256 i = 0; i < _selectors.length; i++) {
functionIsPaused[_selectors[i]] = _isPaused[i];
emit FunctionIsPausedUpdate(_selectors[i], _isPaused[i]);
emit FunctionIsPausedUpdate(
_msgSender(),
_selectors[i],
_isPaused[i]
);
}
}

/**
* @dev Replaces need to import OpenZeppelin's Context.sol. Returns the msg.sender.
*/
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}

/**
* @dev Returns the msg.sig (function selector).
*/
function _msgSig() internal view virtual returns (bytes4) {
return msg.sig;
}
}
29 changes: 26 additions & 3 deletions test/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,39 @@ contract CounterTest is Test {

function setUp() public {
counter = new Counter();
counter.setNumber(0);
counter.setNumber(0, false);
}

function testIncrement() public {
counter.increment();
counter.increment(true);
assertEq(counter.number(), 1);
}

function testSetNumber(uint256 x) public {
counter.setNumber(x);
counter.setNumber(x, true);
assertEq(counter.number(), x);
}

/**
* @dev Tests that counter.increment(bool) can't be called when paused.
*/

function testPauseIncrementAndTryToCall() public {
counter.setIsPaused(counter.increment.selector, true);
uint256 numBefore = counter.number();
vm.expectRevert();
counter.increment(true);
assertEq(counter.number(), numBefore);
}

/**
* @dev Tests that counter.increment(true) will pause the function after the call.
*/
function testCallAfterPausingAfterCall() public {
counter.increment(true);
uint256 numBefore = counter.number();
vm.expectRevert();
counter.increment(false);
assertEq(counter.number(), numBefore);
}
}

0 comments on commit 6409a81

Please sign in to comment.