Skip to content

Commit

Permalink
Override propose and cancel. Add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
garyghayrat committed Jun 24, 2024
1 parent cbd085a commit 06eaa4f
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/L2ArbitrumGovernorV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ contract L2ArbitrumGovernorV2 is
GovernorPreventLateQuorumUpgradeable,
OwnableUpgradeable
{
error NotProposer(address proposer);
/// @notice address for which votes will not be counted toward quorum
/// @dev A portion of the Arbitrum tokens will be held by entities (eg the treasury) that
/// are not eligible to vote. However, even if their voting/delegation is restricted their
Expand All @@ -38,7 +39,9 @@ contract L2ArbitrumGovernorV2 is
/// Example address that should be excluded: DAO treasury, foundation, unclaimed tokens,
/// burned tokens and swept (see TokenDistributor) tokens.
/// Note that Excluded Address is a readable name with no code of PK associated with it, and thus can't vote.

address public constant EXCLUDE_ADDRESS = address(0xA4b86);
mapping(uint256 proposalId => address) public proposers;

constructor() {
_disableInitializers();
Expand Down Expand Up @@ -126,6 +129,28 @@ contract L2ArbitrumGovernorV2 is
return (getPastCirculatingSupply(timepoint) * quorumNumerator(timepoint)) / quorumDenominator();
}

function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public override returns (uint256 _proposalId) {
_proposalId = GovernorUpgradeable.propose(targets, values, calldatas, description);
proposers[_proposalId] = msg.sender;
}

function cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
public
override
returns (uint256)
{
address _proposer = proposers[GovernorUpgradeable.hashProposal(targets, values, calldatas, descriptionHash)];
if (msg.sender != _proposer) {
revert NotProposer(_proposer);
}
return GovernorUpgradeable.cancel(targets, values, calldatas, descriptionHash);
}

function _castVote(uint256 _proposalId, address _account, uint8 _support, string memory _reason, bytes memory _params)
internal
virtual
Expand Down
88 changes: 88 additions & 0 deletions test/L2ArbitrumGovernorV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {IVotes} from "openzeppelin/governance/utils/IVotes.sol";
import {TransparentUpgradeableProxy} from "openzeppelin/proxy/transparent/TransparentUpgradeableProxy.sol";
import {ERC20Mock} from "openzeppelin-contracts/contracts/mocks/token/ERC20Mock.sol";
import {ERC20VotesUpgradeable} from "openzeppelin-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import {IGovernor} from "openzeppelin/governance/IGovernor.sol";

// ----------------------------------------------------------------------------------------------------------------- //
// Test Suite Base - Shared values, setup, helpers, and virtual methods needed by concrete test contracts
Expand Down Expand Up @@ -47,6 +48,15 @@ abstract contract L2ArbitrumGovernorV2Test is Test, SharedGovernorConstants {
proxyDeployer.setUp();
governor = proxyDeployer.run(_implementation);
}

function _getMajorDelegate(uint256 _actorSeed) public pure returns (address) {
address[] memory _majorDelegates = new address[](4);
_majorDelegates[0] = 0x1B686eE8E31c5959D9F5BBd8122a58682788eeaD; // L2BEAT
_majorDelegates[1] = 0xF4B0556B9B6F53E00A1FDD2b0478Ce841991D8fA; // olimpio
_majorDelegates[2] = 0x11cd09a0c5B1dc674615783b0772a9bFD53e3A8F; // Gauntlet
_majorDelegates[3] = 0xB933AEe47C438f22DE0747D57fc239FE37878Dd1; // Wintermute
return _majorDelegates[_actorSeed % _majorDelegates.length];
}
}

// ----------------------------------------------------------------------------------------------------------------- //
Expand Down Expand Up @@ -196,6 +206,60 @@ abstract contract Quorum is L2ArbitrumGovernorV2Test {
}
}

abstract contract Propose is L2ArbitrumGovernorV2Test {
event ProposalCreated(
uint256 proposalId,
address proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 voteStart,
uint256 voteEnd,
string description
);

function testFuzz_Propose(uint256 _actorSeed) public {
// Proposal parameters
address[] memory targets = new address[](1);
uint256[] memory values = new uint256[](1);
bytes[] memory calldatas = new bytes[](1);
string[] memory signatures = new string[](1);
uint256 voteStart = vm.getBlockNumber() + governor.votingDelay();
uint256 voteEnd = voteStart + governor.votingPeriod();
string memory description = "Test";

uint256 proposalId = governor.hashProposal(targets, values, calldatas, keccak256(bytes(description)));
address _actor = _getMajorDelegate(_actorSeed);
vm.prank(_actor);
vm.expectEmit();
emit ProposalCreated(proposalId, _actor, targets, values, signatures, calldatas, voteStart, voteEnd, description);
governor.propose(targets, values, calldatas, description);

assertEq(uint256(governor.state(proposalId)), uint256(IGovernor.ProposalState.Pending));
}
}

abstract contract Cancel is L2ArbitrumGovernorV2Test {
event ProposalCanceled(uint256 proposalId);

function testFuzz_CancelProposalAfterSucceedingButBeforeQueuing(uint256 _actorSeed) public virtual {
address[] memory targets = new address[](1);
uint256[] memory values = new uint256[](1);
bytes[] memory calldatas = new bytes[](1);
string memory description = "Test";

address _actor = _getMajorDelegate(_actorSeed);
vm.prank(_actor);
uint256 proposalId = governor.propose(targets, values, calldatas, description);

assertEq(uint256(governor.state(proposalId)), uint256(IGovernor.ProposalState.Pending));
vm.prank(address(_actor));
governor.cancel(targets, values, calldatas, keccak256(bytes(description)));
assertEq(uint256(governor.state(proposalId)), uint256(IGovernor.ProposalState.Canceled));
}
}

// ----------------------------------------------------------------------------------------------------------------- //
// Concrete Test Contracts - Inherit from each abstract test and implement concrete methods for Core & Treasury case
// ----------------------------------------------------------------------------------------------------------------- //
Expand All @@ -218,6 +282,18 @@ contract CoreGovernorQuorum is Quorum {
}
}

contract CoreGovernorPropose is Propose {
function _createGovernorDeployer() internal override returns (BaseGovernorDeployer) {
return new DeployCoreGovernor();
}
}

contract CoreGovernorCancel is Cancel {
function _createGovernorDeployer() internal override returns (BaseGovernorDeployer) {
return new DeployCoreGovernor();
}
}

contract TreasuryGovernorInitialize is Initialize {
function _createGovernorDeployer() internal override returns (BaseGovernorDeployer) {
return new DeployTreasuryGovernor();
Expand All @@ -235,3 +311,15 @@ contract TreasuryGovernorQuorum is Quorum {
return new DeployTreasuryGovernor();
}
}

contract TreasuryGovernorPropose is Propose {
function _createGovernorDeployer() internal override returns (BaseGovernorDeployer) {
return new DeployTreasuryGovernor();
}
}

contract TreasuryGovernorCancel is Cancel {
function _createGovernorDeployer() internal override returns (BaseGovernorDeployer) {
return new DeployTreasuryGovernor();
}
}

0 comments on commit 06eaa4f

Please sign in to comment.