Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added transfer of RAD tokens to ScopeLift upon successful upgrade to governor Bravo #19

Merged
merged 4 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions script/DeployInput.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ contract DeployInput {
uint256 constant INITIAL_VOTING_DELAY = 7200; // 24 hours
uint256 constant INITIAL_VOTING_PERIOD = 17_280; // matches existing config
uint256 constant INITIAL_PROPOSAL_THRESHOLD = 1_000_000e18; // matches existing config

// ScopeLift address for receiving the RAD tokens upon upgrade execution
address constant SCOPELIFT_ADDRESS = 0x5C04E7808455ee0e22c2773328C151d0DD79dC62;

// Number of RAD tokens to transfer to ScopeLift upon upgrade execution
uint256 constant SCOPELIFT_PAYMENT = 5000e18;
}
28 changes: 17 additions & 11 deletions script/Propose.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,33 @@ import {ICompoundTimelock} from
import {RadworksGovernor} from "src/RadworksGovernor.sol";
import {IGovernorAlpha} from "src/interfaces/IGovernorAlpha.sol";
import {Constants} from "test/Constants.sol";
import {DeployInput} from "script/DeployInput.sol";

/// @notice Script to submit the proposal to upgrade Radworks governor.
contract Propose is Script, Constants {
contract Propose is Script, Constants, DeployInput {
IGovernorAlpha constant RADWORK_GOVERNOR_ALPHA = IGovernorAlpha(GOVERNOR_ALPHA);
address PROPOSER_ADDRESS = 0x464D78a5C97A2E2E9839C353ee9B6d4204c90B0b; // cloudhead.eth

function propose(RadworksGovernor _newGovernor) internal returns (uint256 _proposalId) {
address[] memory _targets = new address[](2);
uint256[] memory _values = new uint256[](2);
string[] memory _signatures = new string[](2);
bytes[] memory _calldatas = new bytes[](2);
address[] memory _targets = new address[](3);
uint256[] memory _values = new uint256[](3);
string[] memory _signatures = new string[](3);
bytes[] memory _calldatas = new bytes[](3);

_targets[0] = RADWORK_GOVERNOR_ALPHA.timelock();
_targets[0] = RAD_TOKEN;
_values[0] = 0;
_signatures[0] = "setPendingAdmin(address)";
_calldatas[0] = abi.encode(address(_newGovernor));
_signatures[0] = "transfer(address,uint256)";
_calldatas[0] = abi.encode(SCOPELIFT_ADDRESS, SCOPELIFT_PAYMENT);

_targets[1] = address(_newGovernor);
_targets[1] = RADWORK_GOVERNOR_ALPHA.timelock();
_values[1] = 0;
_signatures[1] = "__acceptAdmin()";
_calldatas[1] = "";
_signatures[1] = "setPendingAdmin(address)";
_calldatas[1] = abi.encode(address(_newGovernor));

_targets[2] = address(_newGovernor);
_values[2] = 0;
_signatures[2] = "__acceptAdmin()";
_calldatas[2] = "";

return RADWORK_GOVERNOR_ALPHA.propose(
_targets, _values, _signatures, _calldatas, "Upgrade to Governor Bravo"
Expand Down
168 changes: 164 additions & 4 deletions test/RadworksGovernor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ abstract contract Propose is ProposalTest {
uint256 _seed
) public {
IERC20 _token = _randomERC20Token(_seed);
// RAD_TOKEN handled in seperate test because bravo upgrade changes RAD token balances
vm.assume(_token != IERC20(RAD_TOKEN));
_assumeReceiver(_receiver);
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);

Expand All @@ -96,6 +98,37 @@ abstract contract Propose is ProposalTest {
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - _amount);
}

function testFuzz_NewGovernorCanPassProposalToSendRadTokens(uint256 _amount, address _receiver)
public
{
// RAD_TOKEN handled specially as bravo upgrade changes RAD token balances
IERC20 _token = IERC20(RAD_TOKEN);
_assumeReceiver(_receiver);
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);

// bound by the number of tokens the timelock currently controls
// (less the amount to be sent to ScopeLift on the Bravo upgrade)
_amount = bound(_amount, 0, _timelockTokenBalance - SCOPELIFT_PAYMENT);
uint256 _initialTokenBalance = _token.balanceOf(_receiver);

_upgradeToBravoGovernor();

(
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
string memory _description
) = _buildTokenSendProposal(address(_token), _amount, _receiver);

_queueAndVoteAndExecuteProposalWithBravoGovernor(
_targets, _values, _calldatas, _description, FOR
);

// Ensure the tokens have been transferred
assertEq(_token.balanceOf(_receiver), _initialTokenBalance + _amount);
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - (_amount + SCOPELIFT_PAYMENT));
}

function testFuzz_NewGovernorCanPassProposalToSendETH(uint256 _amount, address _receiver) public {
_assumeReceiver(_receiver);
vm.deal(TIMELOCK, _amount);
Expand All @@ -115,7 +148,7 @@ abstract contract Propose is ProposalTest {
_values,
new bytes[](1), // There is no calldata for a plain ETH call.
"Transfer some ETH via the new Governor",
FOR // Vote/suppport type.
FOR // Vote/support type.
);

// Ensure the ETH was transferred.
Expand All @@ -130,6 +163,8 @@ abstract contract Propose is ProposalTest {
uint256 _seed
) public {
IERC20 _token = _randomERC20Token(_seed);
// RAD_TOKEN handled in separate test because bravo upgrade changes RAD token balances
vm.assume(_token != IERC20(RAD_TOKEN));
_assumeReceiver(_receiver);

vm.deal(TIMELOCK, _amountETH);
Expand All @@ -141,13 +176,64 @@ abstract contract Propose is ProposalTest {
uint256 _receiverTokenBalance = _token.balanceOf(_receiver);
_amountToken = bound(_amountToken, 0, _timelockTokenBalance);

// Craft a new proposal to send ETH and tokens.
address[] memory _targets = new address[](2);
uint256[] memory _values = new uint256[](2);
bytes[] memory _calldatas = new bytes[](2);

_upgradeToBravoGovernor();

// First call transfers tokens.
_targets[0] = address(_token);
_calldatas[0] =
_buildProposalData("transfer(address,uint256)", abi.encode(_receiver, _amountToken));

// Second call sends ETH.
_targets[1] = _receiver;
_values[1] = _amountETH;

_queueAndVoteAndExecuteProposalWithBravoGovernor(
_targets,
_values,
_calldatas,
"Transfer tokens and ETH via the new Governor",
FOR // Vote/support type.
);

// Ensure the ETH was transferred.
assertEq(_receiver.balance, _receiverETHBalance + _amountETH);
assertEq(TIMELOCK.balance, _timelockETHBalance - _amountETH);

// Ensure the tokens were transferred.
assertEq(_token.balanceOf(_receiver), _receiverTokenBalance + _amountToken);
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - _amountToken);
}

function testFuzz_NewGovernorCanPassProposalToSendETHAndRadTokens(
uint256 _amountETH,
uint256 _amountToken,
address _receiver
) public {
// @dev: RAD_TOKEN handled specially as bravo upgrade changes RAD token balances
IERC20 _token = IERC20(RAD_TOKEN);
_assumeReceiver(_receiver);

vm.deal(TIMELOCK, _amountETH);
uint256 _timelockETHBalance = TIMELOCK.balance;
uint256 _receiverETHBalance = _receiver.balance;

// Bound _amountToken by the number of tokens the timelock currently controls.
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);
uint256 _receiverTokenBalance = _token.balanceOf(_receiver);
_amountToken = bound(_amountToken, 0, _timelockTokenBalance - SCOPELIFT_PAYMENT);

// Craft a new proposal to send ETH and tokens.
address[] memory _targets = new address[](2);
uint256[] memory _values = new uint256[](2);
bytes[] memory _calldatas = new bytes[](2);

_upgradeToBravoGovernor();

// First call transfers tokens.
_targets[0] = address(_token);
_calldatas[0] =
Expand All @@ -162,7 +248,7 @@ abstract contract Propose is ProposalTest {
_values,
_calldatas,
"Transfer tokens and ETH via the new Governor",
FOR // Vote/suppport type.
FOR // Vote/support type.
);

// Ensure the ETH was transferred.
Expand All @@ -171,7 +257,7 @@ abstract contract Propose is ProposalTest {

// Ensure the tokens were transferred.
assertEq(_token.balanceOf(_receiver), _receiverTokenBalance + _amountToken);
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - _amountToken);
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - (_amountToken + SCOPELIFT_PAYMENT));
}

function testFuzz_NewGovernorFailedProposalsCantSendETH(uint256 _amount, address _receiver)
Expand Down Expand Up @@ -273,12 +359,14 @@ abstract contract Propose is ProposalTest {
assertEq(governorBravo.proposalThreshold(), _newProposalThreshold);
}

function testFuzz_NewGovernorCanPassMixedProposal(
function testFuzz_NewGovernorCanPassMixedProposalOfTokens(
uint256 _amount,
address _receiver,
uint256 _seed
) public {
IERC20 _token = _randomERC20Token(_seed);
// @dev: RAD_TOKEN handled in seperate test because bravo upgrade changes RAD token balances
vm.assume(_token != IERC20(RAD_TOKEN));
_assumeReceiver(_receiver);
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);

Expand All @@ -287,6 +375,7 @@ abstract contract Propose is ProposalTest {
uint256 _initialTokenBalance = _token.balanceOf(_receiver);

_upgradeToBravoGovernor();

(
uint256 _newProposalId,
address[] memory _targets,
Expand Down Expand Up @@ -344,6 +433,77 @@ abstract contract Propose is ProposalTest {
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - _amount);
}

function testFuzz_NewGovernorCanPassMixedProposalOfRadTokens(uint256 _amount, address _receiver)
public
{
// @dev: RAD_TOKEN handled specially as bravo upgrade changes RAD token balances
IERC20 _token = IERC20(RAD_TOKEN);
_assumeReceiver(_receiver);
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);

// bound by the number of tokens the timelock currently controls
_amount = bound(_amount, 0, _timelockTokenBalance - SCOPELIFT_PAYMENT);
uint256 _initialTokenBalance = _token.balanceOf(_receiver);

_upgradeToBravoGovernor();

(
uint256 _newProposalId,
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
string memory _description
) = _submitTokenSendProposalToGovernorBravo(address(_token), _amount, _receiver);

_jumpToActiveProposal(_newProposalId);

// Delegates vote with a mix of For/Against/Abstain with For winning.
// TODO: Need more delegates for this.. pool together had more!
wildmolasses marked this conversation as resolved.
Show resolved Hide resolved
vm.prank(delegates[0].addr);
governorBravo.castVote(_newProposalId, FOR);
vm.prank(delegates[1].addr);
governorBravo.castVote(_newProposalId, FOR);
vm.prank(delegates[2].addr);
governorBravo.castVote(_newProposalId, FOR);
vm.prank(delegates[3].addr);
governorBravo.castVote(_newProposalId, AGAINST);
vm.prank(delegates[4].addr);
governorBravo.castVote(_newProposalId, ABSTAIN);
vm.prank(delegates[5].addr);
governorBravo.castVote(_newProposalId, AGAINST);

// The vote should pass. We are asserting against the raw delegate voting
// weight as a sanity check. In the event that the fork block is changed and
// voting weights are materially different than they were when the test was
// written, we want this assertion to fail.
assertGt(
delegates[0].votes + delegates[1].votes + delegates[2].votes, // FOR votes.
delegates[3].votes + delegates[5].votes // AGAINST votes.
);

_jumpToVotingComplete(_newProposalId);

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

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

_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);

// Ensure the tokens have been transferred
assertEq(_token.balanceOf(_receiver), _initialTokenBalance + _amount);
assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance - (_amount + SCOPELIFT_PAYMENT));
}

function testFuzz_NewGovernorCanDefeatMixedProposal(
uint256 _amount,
address _receiver,
Expand Down
46 changes: 33 additions & 13 deletions test/RadworksGovernorAlpha.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,22 @@ abstract contract RadworksGovernorAlphaTest is ProposalTest {
string[] memory _signatures,
bytes[] memory _calldatas
) = governorAlpha.getActions(upgradeProposalId);
assertEq(_targets.length, 2);
assertEq(_targets[0], TIMELOCK);
assertEq(_targets[1], address(governorBravo));
assertEq(_values.length, 2);
assertEq(_targets.length, 3);
assertEq(_targets[0], RAD_TOKEN);
assertEq(_targets[1], TIMELOCK);
assertEq(_targets[2], address(governorBravo));
assertEq(_values.length, 3);
assertEq(_values[0], 0);
assertEq(_values[1], 0);
assertEq(_signatures.length, 2);
assertEq(_signatures[0], "setPendingAdmin(address)");
assertEq(_signatures[1], "__acceptAdmin()");
assertEq(_calldatas.length, 2);
assertEq(_calldatas[0], abi.encode(address(governorBravo)));
assertEq(_calldatas[1], "");
assertEq(_values[2], 0);
assertEq(_signatures.length, 3);
assertEq(_signatures[0], "transfer(address,uint256)");
assertEq(_signatures[1], "setPendingAdmin(address)");
assertEq(_signatures[2], "__acceptAdmin()");
assertEq(_calldatas.length, 3);
assertEq(_calldatas[0], abi.encode(SCOPELIFT_ADDRESS, SCOPELIFT_PAYMENT));
assertEq(_calldatas[1], abi.encode(address(governorBravo)));
assertEq(_calldatas[2], "");
}

function test_UpgradeProposalActiveAfterDelay() public {
Expand Down Expand Up @@ -106,6 +110,10 @@ abstract contract RadworksGovernorAlphaTest is ProposalTest {
}

function test_UpgradeProposalCanBeExecutedAfterDelay() public {
// get the starting Timelock and ScopeLift RAD balances
uint256 _timelockRADBalance = ERC20VotesComp(RAD_TOKEN).balanceOf(TIMELOCK);
uint256 _scopeLiftRADBalance = ERC20VotesComp(RAD_TOKEN).balanceOf(SCOPELIFT_ADDRESS);

_passAndQueueUpgradeProposal();
_jumpPastProposalEta();

Expand All @@ -118,6 +126,18 @@ abstract contract RadworksGovernorAlphaTest is ProposalTest {

// Ensure the governorBravo is now the admin of the timelock
assertEq(timelock.admin(), address(governorBravo));

// Ensure the Timelock has transferred the RAD tokens to the ScopeLift address
assertEq(
ERC20VotesComp(RAD_TOKEN).balanceOf(TIMELOCK),
_timelockRADBalance - SCOPELIFT_PAYMENT,
"Timelock RAD balance is incorrect"
);
assertEq(
ERC20VotesComp(RAD_TOKEN).balanceOf(SCOPELIFT_ADDRESS),
_scopeLiftRADBalance + SCOPELIFT_PAYMENT,
"ScopeLift RAD balance is incorrect"
);
}

//
Expand Down Expand Up @@ -172,14 +192,14 @@ abstract contract RadworksGovernorAlphaTest is ProposalTest {
_assumeReceiver(_receiver);
IERC20 _token = _randomERC20Token(_seed);

// Pass and execute the proposal to upgrade the Governor
_upgradeToBravoGovernor();

uint256 _receiverTokenBalance = _token.balanceOf(_receiver);
uint256 _timelockTokenBalance = _token.balanceOf(TIMELOCK);
// bound by the number of tokens the timelock currently controls
_amount = bound(_amount, 0, _timelockTokenBalance);

// Pass and execute the proposal to upgrade the Governor
_upgradeToBravoGovernor();

// Craft a new proposal to send the token.
address[] memory _targets = new address[](1);
uint256[] memory _values = new uint256[](1);
Expand Down
Loading