From ccee29c923497b9bd55c421cbb3393389a8ae797 Mon Sep 17 00:00:00 2001 From: Ben DiFrancesco Date: Tue, 7 Nov 2023 17:24:21 -0500 Subject: [PATCH] Assemble the v1 Governor w/ constructor test --- foundry.toml | 2 +- src/GuineaPigGovernor.sol | 119 ++++++++++++++++++++++++++++++++++- src/GuineaPigToken.sol | 6 +- test/GuineaPigGovernor.t.sol | 25 +++++++- 4 files changed, 143 insertions(+), 9 deletions(-) diff --git a/foundry.toml b/foundry.toml index eab6fa4..26ba983 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ optimizer = true optimizer_runs = 10_000_000 remappings = [ - "@openzeppelin/contracts/=lib/flexible-voting/lib/openzeppelin-contracts/contracts", + "@openzeppelin/=lib/flexible-voting/lib/openzeppelin-contracts/contracts", "flexible-voting/=lib/flexible-voting/src", ] solc_version = "0.8.22" diff --git a/src/GuineaPigGovernor.sol b/src/GuineaPigGovernor.sol index 3ff6849..f0e3e74 100644 --- a/src/GuineaPigGovernor.sol +++ b/src/GuineaPigGovernor.sol @@ -1,4 +1,121 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.22; -contract GuineaPigGovernor {} +import { + Governor, GovernorCountingFractional +} from "flexible-voting/GovernorCountingFractional.sol"; +import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol"; +import {GovernorVotes} from "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import {GovernorTimelockControl} from + "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; +import {GovernorSettings} from "@openzeppelin/governance/extensions/GovernorSettings.sol"; +import {IVotes} from "@openzeppelin/governance/utils/IVotes.sol"; +// TODO: convert to interface? +import {TimelockController} from "@openzeppelin/governance/TimelockController.sol"; + +contract GuineaPigGovernor is + GovernorCountingFractional, + GovernorVotes, + GovernorTimelockControl, + GovernorSettings +{ + /// @notice Human readable name of this Governor. + string private constant GOVERNOR_NAME = "Guinea Pig DAO Governor v1"; + + constructor( + IVotes _token, + uint256 _initialVotingDelay, + uint256 _initialVotingPeriod, + uint256 _initialProposalThreshold, + TimelockController _timelock + ) + GovernorVotes(_token) + GovernorSettings(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold) + GovernorTimelockControl(_timelock) + Governor(GOVERNOR_NAME) + {} + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (bool) + { + return GovernorTimelockControl.supportsInterface(interfaceId); + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + uint8 v, + bytes32 r, + bytes32 s + ) public override(Governor, GovernorCountingFractional, IGovernor) returns (uint256) { + return GovernorCountingFractional.castVoteWithReasonAndParamsBySig( + proposalId, support, reason, params, v, r, s + ); + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function proposalThreshold() + public + view + virtual + override(Governor, GovernorSettings) + returns (uint256) + { + return GovernorSettings.proposalThreshold(); + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function state(uint256 proposalId) + public + view + virtual + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return GovernorTimelockControl.state(proposalId); + } + + function quorum(uint256) public pure override returns (uint256) { + return 1; // TODO: determine quorum rules + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) { + return GovernorTimelockControl._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function _executor() + internal + view + virtual + override(Governor, GovernorTimelockControl) + returns (address) + { + return GovernorTimelockControl._executor(); + } + + /// @dev We override this function to resolve ambiguity between inherited contracts. + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override(Governor, GovernorTimelockControl) returns (uint256) { + return GovernorTimelockControl._cancel(targets, values, calldatas, descriptionHash); + } +} diff --git a/src/GuineaPigToken.sol b/src/GuineaPigToken.sol index b2dc1c3..5a0f533 100644 --- a/src/GuineaPigToken.sol +++ b/src/GuineaPigToken.sol @@ -1,11 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.22; -import { - ERC20Votes, - ERC20Permit, - ERC20 -} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import {ERC20Votes, ERC20Permit, ERC20} from "@openzeppelin/token/ERC20/extensions/ERC20Votes.sol"; import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; diff --git a/test/GuineaPigGovernor.t.sol b/test/GuineaPigGovernor.t.sol index f01c07d..80b567b 100644 --- a/test/GuineaPigGovernor.t.sol +++ b/test/GuineaPigGovernor.t.sol @@ -2,9 +2,30 @@ pragma solidity 0.8.22; import {Test, console2} from "forge-std/Test.sol"; +import {GuineaPigGovernor, IVotes, TimelockController} from "src/GuineaPigGovernor.sol"; contract GuineaPigGovernorTest is Test { - function setUp() public {} + GuineaPigGovernor governor; + + IVotes token = IVotes(address(0x01b)); + TimelockController timelock = TimelockController(payable(address(0x01c))); + + uint256 INITIAL_VOTING_DELAY = 50; + uint256 INITIAL_VOTING_PERIOD = 7200; + uint256 INITIAL_PROPOSAL_THRESHOLD = 100_000e18; + + function setUp() public { + governor = + new GuineaPigGovernor(token, INITIAL_VOTING_DELAY, INITIAL_VOTING_PERIOD, INITIAL_PROPOSAL_THRESHOLD, timelock); + } } -contract Deployment is GuineaPigGovernorTest {} +contract Constructor is GuineaPigGovernorTest { + function test_ConstructorArgumentsSetCorrectly() public { + assertEq(governor.votingDelay(), INITIAL_VOTING_DELAY); + assertEq(governor.votingPeriod(), INITIAL_VOTING_PERIOD); + assertEq(governor.proposalThreshold(), INITIAL_PROPOSAL_THRESHOLD); + assertEq(governor.timelock(), address(timelock)); + assertEq(address(governor.token()), address(token)); + } +}