Skip to content

Commit

Permalink
Pauser and renouncAdmin Drips tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jferas committed Nov 28, 2023
1 parent 672ce0f commit 77ddd23
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 17 deletions.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[profile.default]
evm_version = "paris"
evm_version = "shanghai"
fuzz = { seed = "1" }
optimizer = true
optimizer_runs = 10_000_000
Expand Down
16 changes: 16 additions & 0 deletions src/interfaces/IDrips.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

interface IDrips {
function admin() external view returns (address);
function proposeNewAdmin(address newAdmin) external;
function acceptAdmin() external;
function renounceAdmin() external;
function grantPauser(address pauser) external;
function revokePauser(address pauser) external;
function isPauser(address pauser) external view returns (bool);
function allPausers() external view returns (address[] memory);
function isPaused() external view returns (bool);
function pause() external;
function unpause() external;
}
1 change: 1 addition & 0 deletions test/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ contract Constants {
address constant GOVERNOR_ALPHA = 0x690e775361AD66D1c4A25d89da9fCd639F5198eD;
address payable constant RAD_TOKEN = payable(0x31c8EAcBFFdD875c74b94b077895Bd78CF1E64A3);
address constant TIMELOCK = 0x8dA8f82d2BbDd896822de723F55D6EdF416130ba;
address constant DRIPS = 0xd0Dd053392db676D57317CD4fe96Fc2cCf42D0b4;

// TODO: resolve the list of large delegates with tallyaddress
address constant PROPOSER = 0x464D78a5C97A2E2E9839C353ee9B6d4204c90B0b; // cloudhead.eth
Expand Down
130 changes: 130 additions & 0 deletions test/RadworksDripsGovernance.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {ERC20VotesComp} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20VotesComp.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {IGovernorAlpha} from "src/interfaces/IGovernorAlpha.sol";
import {RadworksGovernorTest} from "test/helpers/RadworksGovernorTest.sol";
import {ProposalTest} from "test/helpers/ProposalTest.sol";

abstract contract RadworksDripsGovernance is ProposalTest {
function setUp() public virtual override(ProposalTest) {
ProposalTest.setUp();
_upgradeToBravoGovernor();
}

function _proposePassAndExecuteDripsProposal(string memory _description, bytes memory _callData)
internal
{
address[] memory _targets = new address[](1);
uint256[] memory _values = new uint256[](1);
bytes[] memory _calldatas = new bytes[](1);

_targets[0] = DRIPS;
_calldatas[0] = _callData;

// Submit the new proposal
vm.prank(PROPOSER);
uint256 _newProposalId = governorBravo.propose(_targets, _values, _calldatas, _description);

// Ensure proposal is in the expected state
IGovernor.ProposalState _state = governorBravo.state(_newProposalId);
assertEq(_state, IGovernor.ProposalState.Pending);

_jumpToActiveProposal(_newProposalId);

_delegatesCastVoteOnBravoGovernor(_newProposalId, FOR);
_jumpToVotingComplete(_newProposalId);

// Ensure the proposal has succeeded
_state = governorBravo.state(_newProposalId);
assertEq(_state, IGovernor.ProposalState.Succeeded);

// Queue the proposal
governorBravo.queue(_targets, _values, _calldatas, keccak256(bytes(_description)));

// Ensure the proposal is queued
_state = governorBravo.state(_newProposalId);
assertEq(_state, IGovernor.ProposalState.Queued);

_jumpPastProposalEta(_newProposalId);

// Execute the proposal
governorBravo.execute(_targets, _values, _calldatas, keccak256(bytes(_description)));

// Ensure the proposal is executed
_state = governorBravo.state(_newProposalId);
assertEq(_state, IGovernor.ProposalState.Executed);
}

function _grantNewPauserViaGovernance(address _newPauser) internal {
_proposePassAndExecuteDripsProposal(
"Grant Pauser role to an address",
_buildProposalData("grantPauser(address)", abi.encode(_newPauser))
);
}

function _revokePauserViaGovernance(address _newPauser) internal {
_proposePassAndExecuteDripsProposal(
"Revoke Pauser role from an address",
_buildProposalData("revokePauser(address)", abi.encode(_newPauser))
);
}

function testFuzz_grantPauserOnDrips(address _newPauser) public {
assummeNotTimelock(_newPauser);
address[] memory _originalPausers = drips.allPausers();

_grantNewPauserViaGovernance(_newPauser);

// Ensure the new pauser has been granted pauser role
assertEq(drips.isPauser(_newPauser), true);

// Ensure the the list of pausers got longer by 1
assertEq(_originalPausers.length + 1, drips.allPausers().length);

// Ensure the new pauser can pause the DRIPS protocol
vm.prank(_newPauser);
drips.pause();
assertTrue(drips.isPaused());

// Ensure the new pauser can un-pause the DRIPS protocol
vm.prank(_newPauser);
drips.unpause();
assertFalse(drips.isPaused());
}

function testFuzz_revokePauserOnDrips(address _newPauser) public {
assummeNotTimelock(_newPauser);
_grantNewPauserViaGovernance(_newPauser);

// Ensure the new pauser has been granted pauser role
assertEq(drips.isPauser(_newPauser), true);

_revokePauserViaGovernance(_newPauser);

// Ensure the new pauser has subsequently had pauser role revoked
assertEq(drips.isPauser(_newPauser), false);

// Ensure the newly-revoked pauser cannot pause the DRIPS protocol
vm.prank(_newPauser);
vm.expectRevert("Caller not the admin or a pauser");
drips.pause();
}

function test_renounceAdminViaGovernance() public {
_proposePassAndExecuteDripsProposal(
"Renounce Admin role", _buildProposalData("renounceAdmin()", abi.encode())
);

// Ensure the admin role has been renounced
assertEq(drips.admin(), address(0));
}
}

contract _ExecuteTestWithDeployScriptGovernor is RadworksDripsGovernance {
function _useDeployedGovernorBravo() internal pure override returns (bool) {
return false;
}
}
9 changes: 0 additions & 9 deletions test/RadworksGovernor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -490,9 +490,6 @@ abstract contract Propose is ProposalTest {
}
}

// TODO: future PR
abstract contract Execute is ProposalTest {}

// Run the tests using the deployed Governor Bravo (future PR)

// Run the tests using a version of the Governor deployed by the Deploy script
Expand All @@ -516,9 +513,3 @@ contract ProposeTestWithDeployScriptGovernor is Propose {
// return false;
// }
// }

// contract _ExecuteTestWithDeployScriptGovernor is _Execute {
// function _useDeployedGovernorBravo() internal pure override returns (bool) {
// return false;
// }
// }
20 changes: 13 additions & 7 deletions test/helpers/ProposalTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";

import {TestableProposeScript} from "test/helpers/TestableProposeScript.sol";
import {IGovernorAlpha} from "src/interfaces/IGovernorAlpha.sol";
import {IDrips} from "src/interfaces/IDrips.sol";
import {RadworksGovernorTest} from "test/helpers/RadworksGovernorTest.sol";

abstract contract ProposalTest is RadworksGovernorTest {
//----------------- State and Setup ----------- //

IDrips drips = IDrips(DRIPS);
IGovernorAlpha governorAlpha = IGovernorAlpha(GOVERNOR_ALPHA);
ICompoundTimelock timelock = ICompoundTimelock(payable(TIMELOCK));
uint256 initialProposalCount;
Expand Down Expand Up @@ -57,25 +59,29 @@ abstract contract ProposalTest is RadworksGovernorTest {
// a cheat for fuzzing addresses that are payable only
// Why is this no longer in standard cheats? JJF
// see https://github.com/foundry-rs/foundry/issues/3631
function assumePayable(address addr) internal virtual {
(bool success,) = payable(addr).call{value: 0}("");
function assumePayable(address _addr) internal virtual {
(bool success,) = payable(_addr).call{value: 0}("");
vm.assume(success);
}

function _assumeReceiver(address _receiver) internal {
assumePayable(_receiver);
function assummeNotTimelock(address _addr) internal virtual {
vm.assume(
// We don't want the receiver to be the Timelock, as that would make our
// assertions less meaningful -- most of our tests want to confirm that
// proposals can cause tokens to be sent *from* the timelock to somewhere
// else.
_receiver != TIMELOCK
_addr != TIMELOCK
// We also can't have the receiver be the zero address because RAD
// blocks transfers to the zero address -- see line 396:
// https://etherscan.io/address/0x31c8EAcBFFdD875c74b94b077895Bd78CF1E64A3#code
&& _receiver > address(0)
&& _addr > address(0)
);
assumeNoPrecompiles(_receiver);
assumeNoPrecompiles(_addr);
}

function _assumeReceiver(address _receiver) internal {
assumePayable(_receiver);
assummeNotTimelock(_receiver);
}

function _randomERC20Token(uint256 _seed) internal pure returns (IERC20 _token) {
Expand Down

0 comments on commit 77ddd23

Please sign in to comment.