Skip to content

Commit

Permalink
Pauser and renounceAdmin Drips tests (#18)
Browse files Browse the repository at this point in the history
This PR contains test for testing manipulation (via governance) of the "pauser" and "renounceAdmin" related functions of the Drips protocol.
Additional PR(s) will test the ability (via governance) to change the "admin" of the Drips protocol.
  • Loading branch information
jferas authored Dec 28, 2023
1 parent 7335349 commit 1240370
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ out/
# Dotenv file
.env

# MacOS file cruff
.DS_Store

# Coverage
lcov.info

Expand Down
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
190 changes: 190 additions & 0 deletions src/interfaces/IDrips.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.4;

/// @dev This interface file for the DRIPS protocol was created via the command:
/// `cast interface 0xb0C9B6D67608bE300398d0e4FB0cCa3891E1B33F`
interface IDrips {
type StreamConfig is uint256;

struct AccountMetadata {
bytes32 key;
bytes value;
}

struct SplitsReceiver {
uint256 accountId;
uint32 weight;
}

struct StreamReceiver {
uint256 accountId;
StreamConfig config;
}

struct StreamsHistory {
bytes32 streamsHash;
StreamReceiver[] receivers;
uint32 updateTime;
uint32 maxEnd;
}

event AccountMetadataEmitted(uint256 indexed accountId, bytes32 indexed key, bytes value);
event AdminChanged(address previousAdmin, address newAdmin);
event BeaconUpgraded(address indexed beacon);
event Collectable(uint256 indexed accountId, address indexed erc20, uint128 amt);
event Collected(uint256 indexed accountId, address indexed erc20, uint128 collected);
event DriverAddressUpdated(
uint32 indexed driverId, address indexed oldDriverAddr, address indexed newDriverAddr
);
event DriverRegistered(uint32 indexed driverId, address indexed driverAddr);
event Given(
uint256 indexed accountId, uint256 indexed receiver, address indexed erc20, uint128 amt
);
event NewAdminProposed(address indexed currentAdmin, address indexed newAdmin);
event Paused(address indexed pauser);
event PauserGranted(address indexed pauser, address indexed admin);
event PauserRevoked(address indexed pauser, address indexed admin);
event ReceivedStreams(
uint256 indexed accountId, address indexed erc20, uint128 amt, uint32 receivableCycles
);
event Split(
uint256 indexed accountId, uint256 indexed receiver, address indexed erc20, uint128 amt
);
event SplitsReceiverSeen(bytes32 indexed receiversHash, uint256 indexed accountId, uint32 weight);
event SplitsSet(uint256 indexed accountId, bytes32 indexed receiversHash);
event SqueezedStreams(
uint256 indexed accountId,
address indexed erc20,
uint256 indexed senderId,
uint128 amt,
bytes32[] streamsHistoryHashes
);
event StreamReceiverSeen(
bytes32 indexed receiversHash, uint256 indexed accountId, StreamConfig config
);
event StreamsSet(
uint256 indexed accountId,
address indexed erc20,
bytes32 indexed receiversHash,
bytes32 streamsHistoryHash,
uint128 balance,
uint32 maxEnd
);
event Unpaused(address indexed pauser);
event Upgraded(address indexed implementation);
event Withdrawn(address indexed erc20, address indexed receiver, uint256 amt);

function AMT_PER_SEC_EXTRA_DECIMALS() external view returns (uint8);
function AMT_PER_SEC_MULTIPLIER() external view returns (uint160);
function DRIVER_ID_OFFSET() external view returns (uint8);
function MAX_SPLITS_RECEIVERS() external view returns (uint256);
function MAX_STREAMS_RECEIVERS() external view returns (uint256);
function MAX_TOTAL_BALANCE() external view returns (uint128);
function TOTAL_SPLITS_WEIGHT() external view returns (uint32);
function acceptAdmin() external;
function admin() external view returns (address);
function allPausers() external view returns (address[] memory pausersList);
function balanceAt(
uint256 accountId,
address erc20,
StreamReceiver[] memory currReceivers,
uint32 timestamp
) external view returns (uint128 balance);
function balances(address erc20)
external
view
returns (uint128 streamsBalance, uint128 splitsBalance);
function collect(uint256 accountId, address erc20) external returns (uint128 amt);
function collectable(uint256 accountId, address erc20) external view returns (uint128 amt);
function cycleSecs() external view returns (uint32);
function driverAddress(uint32 driverId) external view returns (address driverAddr);
function emitAccountMetadata(uint256 accountId, AccountMetadata[] memory accountMetadata)
external;
function give(uint256 accountId, uint256 receiver, address erc20, uint128 amt) external;
function grantPauser(address pauser) external;
function hashSplits(SplitsReceiver[] memory receivers)
external
pure
returns (bytes32 receiversHash);
function hashStreams(StreamReceiver[] memory receivers)
external
pure
returns (bytes32 streamsHash);
function hashStreamsHistory(
bytes32 oldStreamsHistoryHash,
bytes32 streamsHash,
uint32 updateTime,
uint32 maxEnd
) external pure returns (bytes32 streamsHistoryHash);
function implementation() external view returns (address);
function isPaused() external view returns (bool);
function isPauser(address pauser) external view returns (bool isAddrPauser);
function minAmtPerSec() external view returns (uint160);
function nextDriverId() external view returns (uint32 driverId);
function pause() external;
function proposeNewAdmin(address newAdmin) external;
function proposedAdmin() external view returns (address);
function proxiableUUID() external view returns (bytes32);
function receivableStreamsCycles(uint256 accountId, address erc20)
external
view
returns (uint32 cycles);
function receiveStreams(uint256 accountId, address erc20, uint32 maxCycles)
external
returns (uint128 receivedAmt);
function receiveStreamsResult(uint256 accountId, address erc20, uint32 maxCycles)
external
view
returns (uint128 receivableAmt);
function registerDriver(address driverAddr) external returns (uint32 driverId);
function renounceAdmin() external;
function revokePauser(address pauser) external;
function setSplits(uint256 accountId, SplitsReceiver[] memory receivers) external;
function setStreams(
uint256 accountId,
address erc20,
StreamReceiver[] memory currReceivers,
int128 balanceDelta,
StreamReceiver[] memory newReceivers,
uint32 FirstMaxEndHint,
uint32 SecondMaxEndHint
) external returns (int128 realBalanceDelta);
function split(uint256 accountId, address erc20, SplitsReceiver[] memory currReceivers)
external
returns (uint128 collectableAmt, uint128 splitAmt);
function splitResult(uint256 accountId, SplitsReceiver[] memory currReceivers, uint128 amount)
external
view
returns (uint128 collectableAmt, uint128 splitAmt);
function splitsHash(uint256 accountId) external view returns (bytes32 currSplitsHash);
function splittable(uint256 accountId, address erc20) external view returns (uint128 amt);
function squeezeStreams(
uint256 accountId,
address erc20,
uint256 senderId,
bytes32 historyHash,
StreamsHistory[] memory streamsHistory
) external returns (uint128 amt);
function squeezeStreamsResult(
uint256 accountId,
address erc20,
uint256 senderId,
bytes32 historyHash,
StreamsHistory[] memory streamsHistory
) external view returns (uint128 amt);
function streamsState(uint256 accountId, address erc20)
external
view
returns (
bytes32 streamsHash,
bytes32 streamsHistoryHash,
uint32 updateTime,
uint128 balance,
uint32 maxEnd
);
function unpause() external;
function updateDriverAddress(uint32 driverId, address newDriverAddr) external;
function upgradeTo(address newImplementation) external;
function upgradeToAndCall(address newImplementation, bytes memory data) external payable;
function withdraw(address erc20, address receiver, uint256 amt) 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
128 changes: 128 additions & 0 deletions test/RadworksDripsGovernance.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.20;

import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {ProposalTest} from "test/helpers/ProposalTest.sol";

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

function _grantNewPauserViaGovernance(address _newPauser) internal {
(
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
string memory _description
) = _buildDripsGovernanceProposal(
"Grant Pauser role to an address",
_buildProposalData("grantPauser(address)", abi.encode(_newPauser))
);
_queueAndVoteAndExecuteProposalWithBravoGovernor(
_targets, _values, _calldatas, _description, FOR
);
}

function _revokePauserViaGovernance(address _newPauser) internal {
(
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
string memory _description
) = _buildDripsGovernanceProposal(
"Revoke Pauser role from an address",
_buildProposalData("revokePauser(address)", abi.encode(_newPauser))
);
_queueAndVoteAndExecuteProposalWithBravoGovernor(
_targets, _values, _calldatas, _description, FOR
);
}

function testFuzz_grantPauserOnDrips(address _newPauser) public {
_assumeNotTimelock(_newPauser);
vm.assume(!drips.isPauser(_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);
}

function testFuzz_grantedPauserCanPauseAndUnPause(address _newPauser) public {
_assumeNotTimelock(_newPauser);

_grantNewPauserViaGovernance(_newPauser);

// 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 {
_assumeNotTimelock(_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);
}

function testFuzz_revertWhenRevokedPauserAttemptsPause(address _newPauser) public {
_assumeNotTimelock(_newPauser);
_grantNewPauserViaGovernance(_newPauser);

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

_revokePauserViaGovernance(_newPauser);

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

// Ensure that the Timelock contract is can still pause the DRIPS protocol
vm.prank(TIMELOCK);
drips.pause();
assertEq(drips.isPauser(_newPauser), false);
}

function test_renounceAdminViaGovernance() public {
(
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
string memory _description
) = _buildDripsGovernanceProposal(
"Renounce Admin role", _buildProposalData("renounceAdmin()", abi.encode())
);
_queueAndVoteAndExecuteProposalWithBravoGovernor(
_targets, _values, _calldatas, _description, FOR
);

// 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 @@ -654,9 +654,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 @@ -680,9 +677,3 @@ contract ProposeTestWithDeployScriptGovernor is Propose {
// return false;
// }
// }

// contract _ExecuteTestWithDeployScriptGovernor is _Execute {
// function _useDeployedGovernorBravo() internal pure override returns (bool) {
// return false;
// }
// }
Loading

0 comments on commit 1240370

Please sign in to comment.