123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.0;
- import "../utils/cryptography/ECDSA.sol";
- import "../utils/cryptography/draft-EIP712.sol";
- import "../utils/introspection/ERC165.sol";
- import "../utils/math/SafeCast.sol";
- import "../utils/Address.sol";
- import "../utils/Context.sol";
- import "../utils/Timers.sol";
- import "./IGovernor.sol";
- /**
- * @dev Core of the governance system, designed to be extended though various modules.
- *
- * This contract is abstract and requires several function to be implemented in various modules:
- *
- * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
- * - A voting module must implement {getVotes}
- * - Additionanly, the {votingPeriod} must also be implemented
- *
- * _Available since v4.3._
- */
- abstract contract Governor is Context, ERC165, EIP712, IGovernor {
- using SafeCast for uint256;
- using Timers for Timers.BlockNumber;
- bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
- struct ProposalCore {
- Timers.BlockNumber voteStart;
- Timers.BlockNumber voteEnd;
- bool executed;
- bool canceled;
- }
- string private _name;
- mapping(uint256 => ProposalCore) private _proposals;
- /**
- * @dev Restrict access to governor executing address. Some module might override the _executor function to make
- * sure this modifier is consistant with the execution model.
- */
- modifier onlyGovernance() {
- require(_msgSender() == _executor(), "Governor: onlyGovernance");
- _;
- }
- /**
- * @dev Sets the value for {name} and {version}
- */
- constructor(string memory name_) EIP712(name_, version()) {
- _name = name_;
- }
- /**
- * @dev See {IERC165-supportsInterface}.
- */
- function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
- return interfaceId == type(IGovernor).interfaceId || super.supportsInterface(interfaceId);
- }
- /**
- * @dev See {IGovernor-name}.
- */
- function name() public view virtual override returns (string memory) {
- return _name;
- }
- /**
- * @dev See {IGovernor-version}.
- */
- function version() public view virtual override returns (string memory) {
- return "1";
- }
- /**
- * @dev See {IGovernor-hashProposal}.
- *
- * The proposal id is produced by hashing the RLC encoded `targets` array, the `values` array, the `calldatas` array
- * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id
- * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in
- * advance, before the proposal is submitted.
- *
- * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the
- * same proposal (with same operation and same description) will have the same id if submitted on multiple governors
- * accross multiple networks. This also means that in order to execute the same operation twice (on the same
- * governor) the proposer will have to change the description in order to avoid proposal id conflicts.
- */
- function hashProposal(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 descriptionHash
- ) public pure virtual override returns (uint256) {
- return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
- }
- /**
- * @dev See {IGovernor-state}.
- */
- function state(uint256 proposalId) public view virtual override returns (ProposalState) {
- ProposalCore memory proposal = _proposals[proposalId];
- if (proposal.executed) {
- return ProposalState.Executed;
- } else if (proposal.canceled) {
- return ProposalState.Canceled;
- } else if (proposal.voteStart.isPending()) {
- return ProposalState.Pending;
- } else if (proposal.voteEnd.isPending()) {
- return ProposalState.Active;
- } else if (proposal.voteEnd.isExpired()) {
- return
- _quorumReached(proposalId) && _voteSucceeded(proposalId)
- ? ProposalState.Succeeded
- : ProposalState.Defeated;
- } else {
- revert("Governor: unknown proposal id");
- }
- }
- /**
- * @dev See {IGovernor-proposalSnapshot}.
- */
- function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
- return _proposals[proposalId].voteStart.getDeadline();
- }
- /**
- * @dev See {IGovernor-proposalDeadline}.
- */
- function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) {
- return _proposals[proposalId].voteEnd.getDeadline();
- }
- /**
- * @dev Amount of votes already cast passes the threshold limit.
- */
- function _quorumReached(uint256 proposalId) internal view virtual returns (bool);
- /**
- * @dev Is the proposal successful or not.
- */
- function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool);
- /**
- * @dev Register a vote with a given support and voting weight.
- *
- * Note: Support is generic and can represent various things depending on the voting system used.
- */
- function _countVote(
- uint256 proposalId,
- address account,
- uint8 support,
- uint256 weight
- ) internal virtual;
- /**
- * @dev See {IGovernor-propose}.
- */
- function propose(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- string memory description
- ) public virtual override returns (uint256) {
- uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
- require(targets.length == values.length, "Governor: invalid proposal length");
- require(targets.length == calldatas.length, "Governor: invalid proposal length");
- require(targets.length > 0, "Governor: empty proposal");
- ProposalCore storage proposal = _proposals[proposalId];
- require(proposal.voteStart.isUnset(), "Governor: proposal already exists");
- uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
- uint64 deadline = snapshot + votingPeriod().toUint64();
- proposal.voteStart.setDeadline(snapshot);
- proposal.voteEnd.setDeadline(deadline);
- emit ProposalCreated(
- proposalId,
- _msgSender(),
- targets,
- values,
- new string[](targets.length),
- calldatas,
- snapshot,
- deadline,
- description
- );
- return proposalId;
- }
- /**
- * @dev See {IGovernor-execute}.
- */
- function execute(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 descriptionHash
- ) public payable virtual override returns (uint256) {
- uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
- ProposalState status = state(proposalId);
- require(
- status == ProposalState.Succeeded || status == ProposalState.Queued,
- "Governor: proposal not successful"
- );
- _proposals[proposalId].executed = true;
- emit ProposalExecuted(proposalId);
- _execute(proposalId, targets, values, calldatas, descriptionHash);
- return proposalId;
- }
- /**
- * @dev Internal execution mechanism. Can be overriden to implement different execution mechanism
- */
- function _execute(
- uint256, /* proposalId */
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 /*descriptionHash*/
- ) internal virtual {
- string memory errorMessage = "Governor: call reverted without message";
- for (uint256 i = 0; i < targets.length; ++i) {
- (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
- Address.verifyCallResult(success, returndata, errorMessage);
- }
- }
- /**
- * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as
- * canceled to allow distinguishing it from executed proposals.
- *
- * Emits a {IGovernor-ProposalCanceled} event.
- */
- function _cancel(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 descriptionHash
- ) internal virtual returns (uint256) {
- uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
- ProposalState status = state(proposalId);
- require(
- status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed,
- "Governor: proposal not active"
- );
- _proposals[proposalId].canceled = true;
- emit ProposalCanceled(proposalId);
- return proposalId;
- }
- /**
- * @dev See {IGovernor-castVote}.
- */
- function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) {
- address voter = _msgSender();
- return _castVote(proposalId, voter, support, "");
- }
- /**
- * @dev See {IGovernor-castVoteWithReason}.
- */
- function castVoteWithReason(
- uint256 proposalId,
- uint8 support,
- string calldata reason
- ) public virtual override returns (uint256) {
- address voter = _msgSender();
- return _castVote(proposalId, voter, support, reason);
- }
- /**
- * @dev See {IGovernor-castVoteBySig}.
- */
- function castVoteBySig(
- uint256 proposalId,
- uint8 support,
- uint8 v,
- bytes32 r,
- bytes32 s
- ) public virtual override returns (uint256) {
- address voter = ECDSA.recover(
- _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))),
- v,
- r,
- s
- );
- return _castVote(proposalId, voter, support, "");
- }
- /**
- * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve
- * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function.
- *
- * Emits a {IGovernor-VoteCast} event.
- */
- function _castVote(
- uint256 proposalId,
- address account,
- uint8 support,
- string memory reason
- ) internal virtual returns (uint256) {
- ProposalCore storage proposal = _proposals[proposalId];
- require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
- uint256 weight = getVotes(account, proposal.voteStart.getDeadline());
- _countVote(proposalId, account, support, weight);
- emit VoteCast(account, proposalId, support, weight, reason);
- return weight;
- }
- /**
- * @dev Address through which the governor executes action. Will be overloaded by module that execute actions
- * through another contract such as a timelock.
- */
- function _executor() internal view virtual returns (address) {
- return address(this);
- }
- }
|