@@ -5,57 +5,22 @@ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
55import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol " ;
66import {IFractionalGovernor} from "src/interfaces/IFractionalGovernor.sol " ;
77import {IVotingToken} from "src/interfaces/IVotingToken.sol " ;
8+ import {FlexVotingBase} from "src/FlexVotingBase.sol " ;
89
910/// @notice This is an abstract contract designed to make it easy to build clients
1011/// for governance systems that inherit from GovernorCountingFractional, a.k.a.
1112/// Flexible Voting governors.
1213///
13- /// A "client" in this sense is a contract that:
14-
15- /// - (a) receives deposits of governance tokens from its users,
16- /// - (b) gives said depositors the ability to express their voting preferences
17- /// on governance proposals, and
18- /// - (c) casts votes on said proposals to flexible voting governors according
19- /// to the expressed preferences of its depositors.
20- ///
21- /// This contract assumes that a child contract will implement a mechanism for
22- /// receiving and storing deposit balances, part (a). With that in place, this
23- /// contract supplies features (b) and (c).
24- ///
25- /// A key concept here is that of a user's "raw balance". The raw balance is the
26- /// system's internal representation of a user's claim on the governance tokens
27- /// that it custodies. Since different systems might represent such claims in
28- /// different ways, this contract leaves the implementation of the `_rawBalance`
29- /// function to the child contract.
30- ///
31- /// The simplest such representation would be to directly store the cumulative
32- /// balance of the governance token that the user has deposited. In such a
33- /// system, the amount that the user deposits is the amount that the user has
34- /// claim to. If the user has claim to 1e18 governance tokens, the internal
35- /// representation is just 1e18.
36- ///
37- /// In many systems, however, the raw balance will not be equivalent to the
38- /// amount of governance tokens the user has claim to. In Aave, for example,
39- /// deposit amounts are scaled down by an ever-increasing index that represents
40- /// the cumulative amount of interest earned over the lifetime of deposits. The
41- /// "raw balance" of a user in Aave's case is this scaled down amount, since it
42- /// is the value that represents the user's claim on deposits. Thus for Aave, a
43- /// users's raw balance will always be less than the actual amount they have
44- /// claim to.
45- ///
46- /// If the raw balance can be identified and defined for a system, and
47- /// `_rawBalance` can be implemented for it, then this contract will take care
48- /// of the rest.
49- abstract contract FlexVotingClient {
50- using SafeCast for uint256 ;
51-
52- // @dev Trace208 is used instead of Trace224 because the former allocates 48
53- // bits to its _key. We need at least 48 bits because the _key is going to be
54- // a timepoint. Timepoints in the context of ERC20Votes and ERC721Votes
55- // conform to the EIP-6372 standard, which specifies they be uint48s.
14+ /// This contract extends FlexVotingBase, adding two features:
15+ /// (a) the ability for depositors to express voting preferences on
16+ /// {Governor}'s proprosals, and
17+ /// (b) the ability to cast fractional, rolled up votes on behalf of depositors.
18+ abstract contract FlexVotingClient is FlexVotingBase {
5619 using Checkpoints for Checkpoints.Trace208;
20+ using SafeCast for uint256 ;
5721
58- /// @notice The voting options corresponding to those used in the Governor.
22+ /// @notice The voting options. The order of options should match that of the
23+ /// voting options in the corresponding {Governor} contract.
5924 enum VoteType {
6025 Against,
6126 For,
@@ -70,54 +35,26 @@ abstract contract FlexVotingClient {
7035 }
7136
7237 /// @dev Map proposalId to an address to whether they have voted on this proposal.
73- mapping (uint256 => mapping (address => bool )) private proposalVotersHasVoted ;
38+ mapping (uint256 => mapping (address => bool )) private proposalVoterHasVoted ;
7439
7540 /// @notice Map proposalId to vote totals expressed on this proposal.
7641 mapping (uint256 => ProposalVote) public proposalVotes;
7742
78- /// @notice The governor contract associated with this governance token. It
79- /// must be one that supports fractional voting, e.g. GovernorCountingFractional.
80- IFractionalGovernor public immutable GOVERNOR;
81-
82- /// @dev Mapping from address to the checkpoint history of internal voting
83- /// weight for that address, i.e. how much weight they can call `expressVote`
84- /// with at a given time.
85- mapping (address => Checkpoints.Trace208) internal voteWeightCheckpoints;
86-
87- /// @dev History of the sum total of voting weight in the system. May or may
88- /// not be equivalent to this contract's balance of `GOVERNOR`s token at a
89- /// given time.
90- Checkpoints.Trace208 internal totalVoteWeightCheckpoints;
91-
92- // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7b74442c5e87ea51dde41c7f18a209fa5154f1a4/contracts/governance/extensions/GovernorCountingFractional.sol#L37
43+ /// Constant used by OZ's implementation of {GovernorCountingFractional} to
44+ /// signal fractional voting.
45+ /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7b74442c5e87ea51dde41c7f18a209fa5154f1a4/contracts/governance/extensions/GovernorCountingFractional.sol#L37
9346 uint8 internal constant VOTE_TYPE_FRACTIONAL = 255 ;
9447
9548 error FlexVotingClient__NoVotingWeight ();
9649 error FlexVotingClient__AlreadyVoted ();
9750 error FlexVotingClient__InvalidSupportValue ();
9851 error FlexVotingClient__NoVotesExpressed ();
9952
100- /// @param _governor The address of the flex-voting-compatible governance contract.
101- constructor (address _governor ) {
102- GOVERNOR = IFractionalGovernor (_governor);
103- }
104-
105- /// @dev Returns a representation of the current amount of `GOVERNOR`s
106- /// token that `_user` has claim to in this system. It may or may not be
107- /// equivalent to the withdrawable balance of `GOVERNOR`s token for `user`,
108- /// e.g. if the internal representation of balance has been scaled down.
109- function _rawBalanceOf (address _user ) internal view virtual returns (uint208 );
110-
11153 /// @dev Used as the `reason` param when submitting a vote to `GOVERNOR`.
11254 function _castVoteReasonString () internal virtual returns (string memory ) {
11355 return "rolled-up vote from governance token holders " ;
11456 }
11557
116- /// @dev Delegates the present contract's voting rights with `GOVERNOR` to itself.
117- function _selfDelegate () internal {
118- IVotingToken (GOVERNOR.token ()).delegate (address (this ));
119- }
120-
12158 /// @notice Allow the caller to express their voting preference for a given
12259 /// proposal. Their preference is recorded internally but not moved to the
12360 /// Governor until `castVote` is called.
@@ -128,8 +65,8 @@ abstract contract FlexVotingClient {
12865 uint256 weight = getPastVoteWeight (voter, GOVERNOR.proposalSnapshot (proposalId));
12966 if (weight == 0 ) revert FlexVotingClient__NoVotingWeight ();
13067
131- if (proposalVotersHasVoted [proposalId][voter]) revert FlexVotingClient__AlreadyVoted ();
132- proposalVotersHasVoted [proposalId][voter] = true ;
68+ if (proposalVoterHasVoted [proposalId][voter]) revert FlexVotingClient__AlreadyVoted ();
69+ proposalVoterHasVoted [proposalId][voter] = true ;
13370
13471 if (support == uint8 (VoteType.Against)) {
13572 proposalVotes[proposalId].againstVotes += SafeCast.toUint128 (weight);
@@ -202,44 +139,6 @@ abstract contract FlexVotingClient {
202139 );
203140 }
204141
205- function _applyDeltaToCheckpoint (Checkpoints.Trace208 storage _checkpoint , int256 _delta )
206- internal
207- returns (uint208 _prevTotal , uint208 _newTotal )
208- {
209- // The casting in this function is safe since:
210- // - if oldTotal + delta > int256.max it will panic and revert.
211- // - if |delta| <= oldTotal there is no risk of wrapping
212- // - if |delta| > oldTotal
213- // * uint256(oldTotal + delta) will wrap but the wrapped value will
214- // necessarily be greater than uint208.max, so SafeCast will revert.
215- // * the lowest that oldTotal + delta can be is int256.min (when
216- // oldTotal is 0 and delta is int256.min). The wrapped value of a
217- // negative signed integer is:
218- // wrapped(integer) = uint256.max + integer
219- // Substituting:
220- // wrapped(int256.min) = uint256.max + int256.min
221- // But:
222- // uint256.max + int256.min > uint208.max
223- // Substituting again:
224- // wrapped(int256.min) > uint208.max, which will revert when safecast.
225- _prevTotal = _checkpoint.latest ();
226- int256 _castTotal = int256 (uint256 (_prevTotal));
227- _newTotal = SafeCast.toUint208 (uint256 (_castTotal + _delta));
228-
229- uint48 _timepoint = IVotingToken (GOVERNOR.token ()).clock ();
230- _checkpoint.push (_timepoint, _newTotal);
231- }
232-
233- /// @dev Checkpoints internal voting weight of `user` after applying `_delta`.
234- function _checkpointVoteWeightOf (address _user , int256 _delta ) internal virtual {
235- _applyDeltaToCheckpoint (voteWeightCheckpoints[_user], _delta);
236- }
237-
238- /// @dev Checkpoints the total vote weight after applying `_delta`.
239- function _checkpointTotalVoteWeight (int256 _delta ) internal virtual {
240- _applyDeltaToCheckpoint (totalVoteWeightCheckpoints, _delta);
241- }
242-
243142 /// @notice Returns the `_user`'s internal voting weight at `_timepoint`.
244143 /// @param _user The account that's historical vote weight will be looked up.
245144 /// @param _timepoint The timepoint at which to lookup the _user's internal
0 commit comments