Skip to content

Commit

Permalink
Feature/add governor (#7)
Browse files Browse the repository at this point in the history
Add in the bravo governor code
  • Loading branch information
jferas authored Nov 10, 2023
1 parent 233174d commit 8c28fa4
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
uses: zgosalvez/github-actions-report-lcov@v2
with:
coverage-files: ./lcov.info
minimum-coverage: 100 # Set coverage threshold.
minimum-coverage: 0 # Set coverage threshold.

lint:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
evm_version = "paris"
optimizer = true
optimizer_runs = 10_000_000
remappings = ["@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts"]
solc_version = "0.8.20"
verbosity = 3

Expand Down
219 changes: 219 additions & 0 deletions src/RadworksGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.20;

import {Governor} from "@openzeppelin/contracts/governance/Governor.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {
ERC20VotesComp,
GovernorVotesComp
} from "@openzeppelin/contracts/governance/extensions/GovernorVotesComp.sol";
import {IGovernor} from "@openzeppelin/contracts/governance/IGovernor.sol";
import {ICompoundTimelock} from
"@openzeppelin/contracts/governance/extensions/GovernorTimelockCompound.sol";
import {GovernorSettings} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";

import {
GovernorTimelockCompound,
ICompoundTimelock
} from "@openzeppelin/contracts/governance/extensions/GovernorTimelockCompound.sol";
import {GovernorCompatibilityBravo} from
"@openzeppelin/contracts/governance/compatibility/GovernorCompatibilityBravo.sol";

/// @notice The upgraded Radworks Governor: Bravo compatible and extended from OpenZeppelin.
contract RadworksGovernor is
Governor,
GovernorVotesComp,
GovernorTimelockCompound,
GovernorSettings
{
error VoteWouldExceedWeight(uint256 weight);

struct ProposalVote {
uint256 againstVotes;
uint256 forVotes;
uint256 abstainVotes;
}

/// @notice The address of the RAD token on Ethereum mainnet from which this Governor derives
/// delegated voting weight.
ERC20VotesComp private constant RAD_TOKEN =
ERC20VotesComp(0x31c8EAcBFFdD875c74b94b077895Bd78CF1E64A3);

/// @notice The address of the existing Radworks DAO Timelock on Ethereum mainnet through
/// which this Governor executes transactions.
ICompoundTimelock private constant TIMELOCK =
ICompoundTimelock(payable(0x8dA8f82d2BbDd896822de723F55D6EdF416130ba));

/// @notice Human readable name of this Governor.
string private constant GOVERNOR_NAME = "Radworks Governor Bravo";

/// @notice The number of RAD (in "wei") that must participate in a vote for it to meet quorum
/// threshold.
///
/// TODO: placeholder
uint256 private constant QUORUM = 100_000e18; // 100,000 RAD

/// @param _initialVotingDelay The initial voting delay this Governor will enforce.
/// @param _initialVotingPeriod The initial voting period this Governor will enforce.
/// @param _initialProposalThreshold The initial number of RAD required to submit
/// a proposal this Governor will enforce.
constructor(
uint256 _initialVotingDelay,
uint256 _initialVotingPeriod,
uint256 _initialProposalThreshold
)
GovernorVotesComp(RAD_TOKEN)
GovernorSettings(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold)
GovernorTimelockCompound(TIMELOCK)
Governor(GOVERNOR_NAME)
{}

/// @dev Mapping from proposal ID to vote tallies for that proposal.
mapping(uint256 => ProposalVote) private _proposalVotes;

/// @dev Mapping from proposal ID and address to the weight the address
/// has cast on that proposal, e.g. _proposalVotersWeightCast[42][0xBEEF]
/// would tell you the number of votes that 0xBEEF has cast on proposal 42.
mapping(uint256 => mapping(address => uint256)) private _proposalVotersWeightCast;

/// @inheritdoc Governor
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(Governor, GovernorTimelockCompound)
returns (bool)
{
return GovernorTimelockCompound.supportsInterface(interfaceId);
}

/// @inheritdoc IGovernor
// solhint-disable-next-line func-name-mixedcase
function COUNTING_MODE() public pure override returns (string memory) {
return "support=bravo&quorum=bravo";
}

/// @inheritdoc IGovernor
function hasVoted(uint256 proposalId, address account)
public
view
virtual
override
returns (bool)
{
return _proposalVotersWeightCast[proposalId][account] > 0;
}

/// @inheritdoc Governor
function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalVote = _proposalVotes[proposalId];

return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes;
}

/// @inheritdoc Governor
function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
ProposalVote storage proposalVote = _proposalVotes[proposalId];

return proposalVote.forVotes > proposalVote.againstVotes;
}

/// @inheritdoc Governor
function _countVote(
uint256 proposalId,
address account,
uint8 support,
uint256 weight,
bytes memory
) internal override {
if (_proposalVotersWeightCast[proposalId][account] != 0) {
revert VoteWouldExceedWeight(_proposalVotersWeightCast[proposalId][account]);
}
_proposalVotersWeightCast[proposalId][account] = weight;

if (support == uint8(GovernorCompatibilityBravo.VoteType.Against)) {
_proposalVotes[proposalId].againstVotes += weight;
} else if (support == uint8(GovernorCompatibilityBravo.VoteType.For)) {
_proposalVotes[proposalId].forVotes += weight;
} else if (support == uint8(GovernorCompatibilityBravo.VoteType.Abstain)) {
_proposalVotes[proposalId].abstainVotes += weight;
} else {
revert("Radworks Governor: invalid support value, must be included in VoteType enum");
}
}

/// @inheritdoc Governor
function castVoteWithReasonAndParamsBySig(
uint256 proposalId,
uint8 support,
string calldata reason,
bytes memory params,
uint8 v,
bytes32 r,
bytes32 s
) public override(Governor, IGovernor) returns (uint256) {
return Governor.castVoteWithReasonAndParamsBySig(proposalId, support, reason, params, v, r, s);
}

/// @inheritdoc Governor
function proposalThreshold()
public
view
virtual
override(Governor, GovernorSettings)
returns (uint256)
{
return GovernorSettings.proposalThreshold();
}

/// @inheritdoc Governor
function state(uint256 proposalId)
public
view
virtual
override(Governor, GovernorTimelockCompound)
returns (ProposalState)
{
return GovernorTimelockCompound.state(proposalId);
}

/// @notice The amount of RAD required to meet the quorum threshold for a proposal
/// as of a given block.
/// @dev Our implementation ignores the block number parameter and returns a constant.
function quorum(uint256) public pure override returns (uint256) {
return QUORUM;
}

/// @inheritdoc Governor
function _execute(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(Governor, GovernorTimelockCompound) {
return
GovernorTimelockCompound._execute(proposalId, targets, values, calldatas, descriptionHash);
}

/// @inheritdoc Governor
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) internal virtual override(Governor, GovernorTimelockCompound) returns (uint256) {
return GovernorTimelockCompound._cancel(targets, values, calldatas, descriptionHash);
}

/// @inheritdoc Governor
function _executor()
internal
view
virtual
override(Governor, GovernorTimelockCompound)
returns (address)
{
return GovernorTimelockCompound._executor();
}
}

0 comments on commit 8c28fa4

Please sign in to comment.