From 917826ecbb425b00ea1aadc5847ebefed7bf7d58 Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 13 Dec 2023 11:33:21 -0500 Subject: [PATCH] Moved test functions from RadworksGovernorAlpha.t.sol to RadworksGovernor.t.sol for scopelint compatibility --- test/RadworksGovernor.t.sol | 285 +++++++++++++++++++++++++++ test/RadworksGovernorAlpha.t.sol | 328 ------------------------------- 2 files changed, 285 insertions(+), 328 deletions(-) delete mode 100644 test/RadworksGovernorAlpha.t.sol diff --git a/test/RadworksGovernor.t.sol b/test/RadworksGovernor.t.sol index a18225a..8c970e5 100644 --- a/test/RadworksGovernor.t.sol +++ b/test/RadworksGovernor.t.sol @@ -666,6 +666,291 @@ abstract contract Propose is ProposalTest { vm.expectRevert("Timelock::queueTransaction: Call must come from admin."); governorAlpha.queue(_vars.alphaProposalId); } + + function test_GovernorUpgradeProposalIsSubmittedCorrectly() public { + // Proposal has been recorded + assertEq(governorAlpha.proposalCount(), initialProposalCount + 1); + + // Proposal is in the expected state + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, PENDING); + + // Proposal actions correspond to Governor upgrade + ( + address[] memory _targets, + uint256[] memory _values, + string[] memory _signatures, + bytes[] memory _calldatas + ) = governorAlpha.getActions(upgradeProposalId); + 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(_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 { + _jumpToActiveUpgradeProposal(); + + // Ensure proposal has become active the block after the voting delay + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, ACTIVE); + } + + function testFuzz_UpgradeProposerCanCastVote(bool _willSupport) public { + _jumpToActiveUpgradeProposal(); + uint256 _proposerVotes = + ERC20VotesComp(RAD_TOKEN).getPriorVotes(PROPOSER, _upgradeProposalStartBlock()); + + vm.prank(PROPOSER); + governorAlpha.castVote(upgradeProposalId, _willSupport); + + IGovernorAlpha.Receipt memory _receipt = governorAlpha.getReceipt(upgradeProposalId, PROPOSER); + assertEq(_receipt.hasVoted, true); + assertEq(_receipt.support, _willSupport); + assertEq(_receipt.votes, _proposerVotes); + } + + function test_UpgradeProposalSucceedsWhenAllDelegatesVoteFor() public { + _passUpgradeProposal(); + + // Ensure proposal state is now succeeded + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, SUCCEEDED); + } + + function test_UpgradeProposalDefeatedWhenAllDelegatesVoteAgainst() public { + _defeatUpgradeProposal(); + + // Ensure proposal state is now defeated + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, DEFEATED); + } + + function test_UpgradeProposalCanBeQueuedAfterSucceeding() public { + _passUpgradeProposal(); + governorAlpha.queue(upgradeProposalId); + + // Ensure proposal can be queued after success + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, QUEUED); + + ( + address[] memory _targets, + uint256[] memory _values, + string[] memory _signatures, + bytes[] memory _calldatas + ) = governorAlpha.getActions(upgradeProposalId); + + uint256 _eta = block.timestamp + timelock.delay(); + + for (uint256 _index = 0; _index < _targets.length; _index++) { + // Calculate hash of transaction in Timelock + bytes32 _txHash = keccak256( + abi.encode(_targets[_index], _values[_index], _signatures[_index], _calldatas[_index], _eta) + ); + + // Ensure transaction is queued in Timelock + bool _isQueued = timelock.queuedTransactions(_txHash); + assertEq(_isQueued, true); + } + } + + function test_UpgradeProposalCanBeExecutedAfterDelay() public { + _passAndQueueUpgradeProposal(); + _jumpPastProposalEta(); + + // Execute the proposal + governorAlpha.execute(upgradeProposalId); + + // Ensure the proposal is now executed + uint8 _state = governorAlpha.state(upgradeProposalId); + assertEq(_state, EXECUTED); + + // Ensure the governorBravo is now the admin of the timelock + assertEq(timelock.admin(), address(governorBravo)); + } + + // + // Post proposal tests + // + function testFuzz_OldGovernorSendsETHAfterProposalIsDefeated(uint128 _amount, address _receiver) + public + { + _assumeReceiver(_receiver); + + // Counter-intuitively, the Governor (not the Timelock) must hold the ETH. + // See the deployed GovernorAlpha, line 281: + // https://etherscan.io/address/0x690e775361AD66D1c4A25d89da9fCd639F5198eD#code + // The governor transfers ETH to the Timelock in the process of executing + // the proposal. The Timelock then just passes that ETH along. + vm.deal(address(governorAlpha), _amount); + + uint256 _receiverETHBalance = _receiver.balance; + uint256 _governorETHBalance = address(governorAlpha).balance; + + // Defeat the proposal to upgrade the Governor + _defeatUpgradeProposal(); + + // Create a new proposal to send the ETH. + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + _targets[0] = _receiver; + _values[0] = _amount; + + _queueAndVoteAndExecuteProposalWithAlphaGovernor( + _targets, + _values, + new string[](1), // No signature needed for an ETH send. + new bytes[](1), // No calldata needed for an ETH send. + true // GovernorAlpha is still the Timelock admin. + ); + + // Ensure the ETH has been transferred to the receiver + assertEq( + address(governorAlpha).balance, + _governorETHBalance - _amount, + "Governor alpha ETH balance is incorrect" + ); + assertEq(_receiver.balance, _receiverETHBalance + _amount, "Receiver ETH balance is incorrect"); + } + + function testFuzz_OldGovernorCannotSendETHAfterProposalSucceeds( + uint256 _amount, + address _receiver + ) public { + _assumeReceiver(_receiver); + + // Counter-intuitively, the Governor must hold the ETH, not the Timelock. + // See the deployed GovernorAlpha, line 204: + // https://etherscan.io/address/0xB3a87172F555ae2a2AB79Be60B336D2F7D0187f0#code + // The governor transfers ETH to the Timelock in the process of executing + // the proposal. The Timelock then just passes that ETH along. + vm.deal(address(governorAlpha), _amount); + + uint256 _receiverETHBalance = _receiver.balance; + uint256 _governorETHBalance = address(governorAlpha).balance; + + // Pass and execute the proposal to upgrade the Governor + _upgradeToBravoGovernor(); + + // Create a new proposal to send the ETH. + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + _targets[0] = _receiver; + _values[0] = _amount; + + _queueAndVoteAndExecuteProposalWithAlphaGovernor( + _targets, + _values, + new string[](1), // No signature needed for an ETH send. + new bytes[](1), // No calldata needed for an ETH send. + false // GovernorAlpha is not the Timelock admin. + ); + + // Ensure no ETH has been transferred to the receiver + assertEq(address(governorAlpha).balance, _governorETHBalance); + assertEq(_receiver.balance, _receiverETHBalance); + } + + function testFuzz_OldGovernorSendsTokenAfterProposalIsDefeated( + uint256 _amount, + address _receiver, + uint256 _seed + ) public { + _assumeReceiver(_receiver); + IERC20 _token = _randomERC20Token(_seed); + + 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); + + // Defeat the proposal to upgrade the Governor + _defeatUpgradeProposal(); + + // Craft a new proposal to send the token. + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + + _targets[0] = address(_token); + _values[0] = 0; + _signatures[0] = "transfer(address,uint256)"; + _calldatas[0] = abi.encode(_receiver, _amount); + + _queueAndVoteAndExecuteProposalWithAlphaGovernor( + _targets, + _values, + _signatures, + _calldatas, + true // GovernorAlpha is still the Timelock admin. + ); + + // Ensure the tokens have been transferred from the timelock to the receiver. + assertEq( + _token.balanceOf(TIMELOCK), + _timelockTokenBalance - _amount, + "Timelock token balance is incorrect" + ); + assertEq( + _token.balanceOf(_receiver), + _receiverTokenBalance + _amount, + "Receiver token balance is incorrect" + ); + } + + function testFuzz_OldGovernorCanNotSendTokensAfterUpgradeCompletes( + uint256 _amount, + address _receiver, + uint256 _seed + ) public { + _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); + + // Craft a new proposal to send the token. + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + + _targets[0] = address(_token); + _values[0] = 0; + _signatures[0] = "transfer(address,uint256)"; + _calldatas[0] = abi.encode(_receiver, _amount); + + _queueAndVoteAndExecuteProposalWithAlphaGovernor( + _targets, + _values, + _signatures, + _calldatas, + false // GovernorAlpha is not the Timelock admin anymore. + ); + + // Ensure no tokens have been transferred from the timelock to the receiver. + assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance, "Timelock balance is incorrect"); + assertEq(_token.balanceOf(_receiver), _receiverTokenBalance, "Receiver balance is incorrect"); + } } abstract contract _Execute is ProposalTest { diff --git a/test/RadworksGovernorAlpha.t.sol b/test/RadworksGovernorAlpha.t.sol deleted file mode 100644 index 0764286..0000000 --- a/test/RadworksGovernorAlpha.t.sol +++ /dev/null @@ -1,328 +0,0 @@ -// 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 Propose is ProposalTest { - function test_GovernorUpgradeProposalIsSubmittedCorrectly() public { - // Proposal has been recorded - assertEq(governorAlpha.proposalCount(), initialProposalCount + 1); - - // Proposal is in the expected state - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, PENDING); - - // Proposal actions correspond to Governor upgrade - ( - address[] memory _targets, - uint256[] memory _values, - string[] memory _signatures, - bytes[] memory _calldatas - ) = governorAlpha.getActions(upgradeProposalId); - 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(_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 { - _jumpToActiveUpgradeProposal(); - - // Ensure proposal has become active the block after the voting delay - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, ACTIVE); - } - - function testFuzz_UpgradeProposerCanCastVote(bool _willSupport) public { - _jumpToActiveUpgradeProposal(); - uint256 _proposerVotes = - ERC20VotesComp(RAD_TOKEN).getPriorVotes(PROPOSER, _upgradeProposalStartBlock()); - - vm.prank(PROPOSER); - governorAlpha.castVote(upgradeProposalId, _willSupport); - - IGovernorAlpha.Receipt memory _receipt = governorAlpha.getReceipt(upgradeProposalId, PROPOSER); - assertEq(_receipt.hasVoted, true); - assertEq(_receipt.support, _willSupport); - assertEq(_receipt.votes, _proposerVotes); - } - - function test_UpgradeProposalSucceedsWhenAllDelegatesVoteFor() public { - _passUpgradeProposal(); - - // Ensure proposal state is now succeeded - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, SUCCEEDED); - } - - function test_UpgradeProposalDefeatedWhenAllDelegatesVoteAgainst() public { - _defeatUpgradeProposal(); - - // Ensure proposal state is now defeated - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, DEFEATED); - } - - function test_UpgradeProposalCanBeQueuedAfterSucceeding() public { - _passUpgradeProposal(); - governorAlpha.queue(upgradeProposalId); - - // Ensure proposal can be queued after success - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, QUEUED); - - ( - address[] memory _targets, - uint256[] memory _values, - string[] memory _signatures, - bytes[] memory _calldatas - ) = governorAlpha.getActions(upgradeProposalId); - - uint256 _eta = block.timestamp + timelock.delay(); - - for (uint256 _index = 0; _index < _targets.length; _index++) { - // Calculate hash of transaction in Timelock - bytes32 _txHash = keccak256( - abi.encode(_targets[_index], _values[_index], _signatures[_index], _calldatas[_index], _eta) - ); - - // Ensure transaction is queued in Timelock - bool _isQueued = timelock.queuedTransactions(_txHash); - assertEq(_isQueued, true); - } - } - - 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(); - - // Execute the proposal - governorAlpha.execute(upgradeProposalId); - - // Ensure the proposal is now executed - uint8 _state = governorAlpha.state(upgradeProposalId); - assertEq(_state, EXECUTED); - - // 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" - ); - } - - // - // Post proposal tests - // - function testFuzz_OldGovernorSendsETHAfterProposalIsDefeated(uint128 _amount, address _receiver) - public - { - _assumeReceiver(_receiver); - - // Counter-intuitively, the Governor (not the Timelock) must hold the ETH. - // See the deployed GovernorAlpha, line 281: - // https://etherscan.io/address/0x690e775361AD66D1c4A25d89da9fCd639F5198eD#code - // The governor transfers ETH to the Timelock in the process of executing - // the proposal. The Timelock then just passes that ETH along. - vm.deal(address(governorAlpha), _amount); - - uint256 _receiverETHBalance = _receiver.balance; - uint256 _governorETHBalance = address(governorAlpha).balance; - - // Defeat the proposal to upgrade the Governor - _defeatUpgradeProposal(); - - // Create a new proposal to send the ETH. - address[] memory _targets = new address[](1); - uint256[] memory _values = new uint256[](1); - _targets[0] = _receiver; - _values[0] = _amount; - - _queueAndVoteAndExecuteProposalWithAlphaGovernor( - _targets, - _values, - new string[](1), // No signature needed for an ETH send. - new bytes[](1), // No calldata needed for an ETH send. - true // GovernorAlpha is still the Timelock admin. - ); - - // Ensure the ETH has been transferred to the receiver - assertEq( - address(governorAlpha).balance, - _governorETHBalance - _amount, - "Governor alpha ETH balance is incorrect" - ); - assertEq(_receiver.balance, _receiverETHBalance + _amount, "Receiver ETH balance is incorrect"); - } - - function testFuzz_OldGovernorCannotSendETHAfterProposalSucceeds( - uint256 _amount, - address _receiver - ) public { - _assumeReceiver(_receiver); - - // Counter-intuitively, the Governor must hold the ETH, not the Timelock. - // See the deployed GovernorAlpha, line 204: - // https://etherscan.io/address/0xB3a87172F555ae2a2AB79Be60B336D2F7D0187f0#code - // The governor transfers ETH to the Timelock in the process of executing - // the proposal. The Timelock then just passes that ETH along. - vm.deal(address(governorAlpha), _amount); - - uint256 _receiverETHBalance = _receiver.balance; - uint256 _governorETHBalance = address(governorAlpha).balance; - - // Pass and execute the proposal to upgrade the Governor - _upgradeToBravoGovernor(); - - // Create a new proposal to send the ETH. - address[] memory _targets = new address[](1); - uint256[] memory _values = new uint256[](1); - _targets[0] = _receiver; - _values[0] = _amount; - - _queueAndVoteAndExecuteProposalWithAlphaGovernor( - _targets, - _values, - new string[](1), // No signature needed for an ETH send. - new bytes[](1), // No calldata needed for an ETH send. - false // GovernorAlpha is not the Timelock admin. - ); - - // Ensure no ETH has been transferred to the receiver - assertEq(address(governorAlpha).balance, _governorETHBalance); - assertEq(_receiver.balance, _receiverETHBalance); - } - - function testFuzz_OldGovernorSendsTokenAfterProposalIsDefeated( - uint256 _amount, - address _receiver, - uint256 _seed - ) public { - _assumeReceiver(_receiver); - IERC20 _token = _randomERC20Token(_seed); - - 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); - - // Defeat the proposal to upgrade the Governor - _defeatUpgradeProposal(); - - // Craft a new proposal to send the token. - address[] memory _targets = new address[](1); - uint256[] memory _values = new uint256[](1); - string[] memory _signatures = new string[](1); - bytes[] memory _calldatas = new bytes[](1); - - _targets[0] = address(_token); - _values[0] = 0; - _signatures[0] = "transfer(address,uint256)"; - _calldatas[0] = abi.encode(_receiver, _amount); - - _queueAndVoteAndExecuteProposalWithAlphaGovernor( - _targets, - _values, - _signatures, - _calldatas, - true // GovernorAlpha is still the Timelock admin. - ); - - // Ensure the tokens have been transferred from the timelock to the receiver. - assertEq( - _token.balanceOf(TIMELOCK), - _timelockTokenBalance - _amount, - "Timelock token balance is incorrect" - ); - assertEq( - _token.balanceOf(_receiver), - _receiverTokenBalance + _amount, - "Receiver token balance is incorrect" - ); - } - - function testFuzz_OldGovernorCanNotSendTokensAfterUpgradeCompletes( - uint256 _amount, - address _receiver, - uint256 _seed - ) public { - _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); - - // Craft a new proposal to send the token. - address[] memory _targets = new address[](1); - uint256[] memory _values = new uint256[](1); - string[] memory _signatures = new string[](1); - bytes[] memory _calldatas = new bytes[](1); - - _targets[0] = address(_token); - _values[0] = 0; - _signatures[0] = "transfer(address,uint256)"; - _calldatas[0] = abi.encode(_receiver, _amount); - - _queueAndVoteAndExecuteProposalWithAlphaGovernor( - _targets, - _values, - _signatures, - _calldatas, - false // GovernorAlpha is not the Timelock admin anymore. - ); - - // Ensure no tokens have been transferred from the timelock to the receiver. - assertEq(_token.balanceOf(TIMELOCK), _timelockTokenBalance, "Timelock balance is incorrect"); - assertEq(_token.balanceOf(_receiver), _receiverTokenBalance, "Receiver balance is incorrect"); - } -} - -// Run the tests using a version of the Governor deployed by the Deploy script - -contract AlphaTestWithDeployScriptGovernor is Propose { - function _useDeployedGovernorBravo() internal pure override returns (bool) { - return false; - } -} - -// Run the tests using the deployed Governor Bravo - -contract AlphaTestWithOnChainGovernor is Propose { - function _useDeployedGovernorBravo() internal pure override returns (bool) { - return DEPLOYED_BRAVO_GOVERNOR != address(0); - } -}