GovernorCountingOverridable.sol 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {SignatureChecker} from "../../utils/cryptography/SignatureChecker.sol";
  4. import {SafeCast} from "../../utils/math/SafeCast.sol";
  5. import {VotesExtended} from "../utils/VotesExtended.sol";
  6. import {GovernorVotes} from "./GovernorVotes.sol";
  7. /**
  8. * @dev Extension of {Governor} which enables delegatees to override the vote of their delegates. This module requires a
  9. * token token that inherits `VotesExtended`.
  10. */
  11. abstract contract GovernorCountingOverridable is GovernorVotes {
  12. bytes32 public constant OVERRIDE_BALLOT_TYPEHASH =
  13. keccak256("OverrideBallot(uint256 proposalId,uint8 support,address voter,uint256 nonce,string reason)");
  14. /**
  15. * @dev Supported vote types. Matches Governor Bravo ordering.
  16. */
  17. enum VoteType {
  18. Against,
  19. For,
  20. Abstain
  21. }
  22. struct VoteReceipt {
  23. uint8 casted; // 0 if vote was not casted. Otherwise: support + 1
  24. bool hasOverriden;
  25. uint208 overridenWeight;
  26. }
  27. struct ProposalVote {
  28. uint256[3] votes;
  29. mapping(address voter => VoteReceipt) voteReceipt;
  30. }
  31. event VoteReduced(address indexed voter, uint256 proposalId, uint8 support, uint256 weight);
  32. event OverrideVoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
  33. error GovernorAlreadyOverridenVote(address account);
  34. mapping(uint256 proposalId => ProposalVote) private _proposalVotes;
  35. /**
  36. * @dev See {IGovernor-COUNTING_MODE}.
  37. */
  38. // solhint-disable-next-line func-name-mixedcase
  39. function COUNTING_MODE() public pure virtual override returns (string memory) {
  40. return "support=bravo,override&quorum=for,abstain&overridable=true";
  41. }
  42. /**
  43. * @dev See {IGovernor-hasVoted}.
  44. */
  45. function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
  46. return _proposalVotes[proposalId].voteReceipt[account].casted != 0;
  47. }
  48. /**
  49. * @dev Check if an `account` has overridden their delegate for a proposal.
  50. */
  51. function hasVotedOverride(uint256 proposalId, address account) public view virtual returns (bool) {
  52. return _proposalVotes[proposalId].voteReceipt[account].hasOverriden;
  53. }
  54. /**
  55. * @dev Accessor to the internal vote counts.
  56. */
  57. function proposalVotes(
  58. uint256 proposalId
  59. ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) {
  60. uint256[3] storage votes = _proposalVotes[proposalId].votes;
  61. return (votes[uint8(VoteType.Against)], votes[uint8(VoteType.For)], votes[uint8(VoteType.Abstain)]);
  62. }
  63. /**
  64. * @dev See {Governor-_quorumReached}.
  65. */
  66. function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
  67. uint256[3] storage votes = _proposalVotes[proposalId].votes;
  68. return quorum(proposalSnapshot(proposalId)) <= votes[uint8(VoteType.For)] + votes[uint8(VoteType.Abstain)];
  69. }
  70. /**
  71. * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes.
  72. */
  73. function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
  74. uint256[3] storage votes = _proposalVotes[proposalId].votes;
  75. return votes[uint8(VoteType.For)] > votes[uint8(VoteType.Against)];
  76. }
  77. /**
  78. * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo).
  79. *
  80. * NOTE: called by {Governor-_castVote} which emits the {IGovernor-VoteCast} (or {IGovernor-VoteCastWithParams})
  81. * event.
  82. */
  83. function _countVote(
  84. uint256 proposalId,
  85. address account,
  86. uint8 support,
  87. uint256 totalWeight,
  88. bytes memory /*params*/
  89. ) internal virtual override returns (uint256) {
  90. ProposalVote storage proposalVote = _proposalVotes[proposalId];
  91. if (support > uint8(VoteType.Abstain)) {
  92. revert GovernorInvalidVoteType();
  93. }
  94. if (proposalVote.voteReceipt[account].casted != 0) {
  95. revert GovernorAlreadyCastVote(account);
  96. }
  97. totalWeight -= proposalVote.voteReceipt[account].overridenWeight;
  98. proposalVote.votes[support] += totalWeight;
  99. proposalVote.voteReceipt[account].casted = support + 1;
  100. return totalWeight;
  101. }
  102. /// @dev Variant of {Governor-_countVote} that deals with vote overrides.
  103. function _countOverride(uint256 proposalId, address account, uint8 support) internal virtual returns (uint256) {
  104. ProposalVote storage proposalVote = _proposalVotes[proposalId];
  105. if (support > uint8(VoteType.Abstain)) {
  106. revert GovernorInvalidVoteType();
  107. }
  108. if (proposalVote.voteReceipt[account].hasOverriden) {
  109. revert GovernorAlreadyOverridenVote(account);
  110. }
  111. uint256 proposalSnapshot = proposalSnapshot(proposalId);
  112. uint256 overridenWeight = VotesExtended(address(token())).getPastBalanceOf(account, proposalSnapshot);
  113. address delegate = VotesExtended(address(token())).getPastDelegate(account, proposalSnapshot);
  114. uint8 delegateCasted = proposalVote.voteReceipt[delegate].casted;
  115. proposalVote.voteReceipt[account].hasOverriden = true;
  116. proposalVote.votes[support] += overridenWeight;
  117. if (delegateCasted == 0) {
  118. proposalVote.voteReceipt[delegate].overridenWeight += SafeCast.toUint208(overridenWeight);
  119. } else {
  120. uint8 delegateSupport = delegateCasted - 1;
  121. proposalVote.votes[delegateSupport] -= overridenWeight;
  122. emit VoteReduced(delegate, proposalId, delegateSupport, overridenWeight);
  123. }
  124. return overridenWeight;
  125. }
  126. /// @dev variant of {Governor-_castVote} that deals with vote overrides.
  127. function _castOverride(
  128. uint256 proposalId,
  129. address account,
  130. uint8 support,
  131. string calldata reason
  132. ) internal virtual returns (uint256) {
  133. _validateStateBitmap(proposalId, _encodeStateBitmap(ProposalState.Active));
  134. uint256 overridenWeight = _countOverride(proposalId, account, support);
  135. emit OverrideVoteCast(account, proposalId, support, overridenWeight, reason);
  136. _tallyUpdated(proposalId);
  137. return overridenWeight;
  138. }
  139. /// @dev Public function for casting an override vote
  140. function castOverrideVote(
  141. uint256 proposalId,
  142. uint8 support,
  143. string calldata reason
  144. ) public virtual returns (uint256) {
  145. address voter = _msgSender();
  146. return _castOverride(proposalId, voter, support, reason);
  147. }
  148. /// @dev Public function for casting an override vote using a voter's signature
  149. function castOverrideVoteBySig(
  150. uint256 proposalId,
  151. uint8 support,
  152. address voter,
  153. string calldata reason,
  154. bytes calldata signature
  155. ) public virtual returns (uint256) {
  156. bool valid = SignatureChecker.isValidSignatureNow(
  157. voter,
  158. _hashTypedDataV4(
  159. keccak256(
  160. abi.encode(
  161. OVERRIDE_BALLOT_TYPEHASH,
  162. proposalId,
  163. support,
  164. voter,
  165. _useNonce(voter),
  166. keccak256(bytes(reason))
  167. )
  168. )
  169. ),
  170. signature
  171. );
  172. if (!valid) {
  173. revert GovernorInvalidSignature(voter);
  174. }
  175. return _castOverride(proposalId, voter, support, reason);
  176. }
  177. }