@@ -6,21 +6,15 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
6
6
import "@openzeppelin/contracts/utils/math/Math.sol " ;
7
7
import "@openzeppelin/contracts/utils/math/SafeCast.sol " ;
8
8
9
- /**
10
- * @dev Abstract contract to support checkpoints for Compound-like voting and delegation.
11
- * This implementation supports token supply up to 2^96^ - 1.
12
- *
13
- * This contract keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
14
- * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
15
- * power can be queried through the public accessors {getVotes} and {getPastVotes}.
16
- *
17
- * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
18
- * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
19
- * Enabling self-delegation can easily be done by overriding the {delegates} function. Keep in mind however that this
20
- * will significantly increase the base gas cost of transfers.
21
- *
22
- * NOTE: Extracted from OpenZeppelin ERCVotes.sol. Maybe worth trying to push it upstream there.
23
- */
9
+ /// @title Checkpoints
10
+ /// @dev Abstract contract to support checkpoints for Compound-like voting and
11
+ /// delegation. This implementation supports token supply up to 2^96 - 1.
12
+ /// This contract keeps a history (checkpoints) of each account's vote
13
+ /// power. Vote power can be delegated either by calling the {delegate}
14
+ /// function directly, or by providing a signature to be used with
15
+ /// {delegateBySig}. Voting power can be publicly queried through
16
+ /// {getVotes} and {getPastVotes}.
17
+ /// NOTE: Extracted from OpenZeppelin ERCVotes.sol.
24
18
abstract contract Checkpoints {
25
19
struct Checkpoint {
26
20
uint32 fromBlock;
@@ -31,18 +25,15 @@ abstract contract Checkpoints {
31
25
mapping (address => uint128 []) internal _checkpoints;
32
26
uint128 [] internal _totalSupplyCheckpoints;
33
27
34
- /**
35
- * @dev Emitted when an account changes their delegate.
36
- */
28
+ /// @notice Emitted when an account changes their delegate.
37
29
event DelegateChanged (
38
30
address indexed delegator ,
39
31
address indexed fromDelegate ,
40
32
address indexed toDelegate
41
33
);
42
34
43
- /**
44
- * @dev Emitted when a token transfer or delegate change results in changes to an account's voting power.
45
- */
35
+ /// @notice Emitted when a balance or delegate change results in changes
36
+ /// to an account's voting power.
46
37
event DelegateVotesChanged (
47
38
address indexed delegate ,
48
39
uint256 previousBalance ,
@@ -51,9 +42,6 @@ abstract contract Checkpoints {
51
42
52
43
function delegate (address delegator , address delegatee ) internal virtual ;
53
44
54
- /**
55
- * @dev Get the `pos`-th checkpoint for `account`.
56
- */
57
45
function checkpoints (address account , uint32 pos )
58
46
public
59
47
view
@@ -66,9 +54,7 @@ abstract contract Checkpoints {
66
54
checkpoint = Checkpoint (fromBlock, votes);
67
55
}
68
56
69
- /**
70
- * @dev Get number of checkpoints for `account`.
71
- */
57
+ /// @notice Get number of checkpoints for `account`.
72
58
function numCheckpoints (address account )
73
59
public
74
60
view
@@ -78,9 +64,7 @@ abstract contract Checkpoints {
78
64
return SafeCast.toUint32 (_checkpoints[account].length );
79
65
}
80
66
81
- /**
82
- * @dev Get the address `account` is currently delegating to.
83
- */
67
+ /// @notice Get the address `account` is currently delegating to.
84
68
function delegates (address account ) public view virtual returns (address ) {
85
69
return _delegates[account];
86
70
}
@@ -105,25 +89,26 @@ abstract contract Checkpoints {
105
89
view
106
90
returns (uint96 )
107
91
{
108
- return _checkpointsLookup (_checkpoints[account], blockNumber);
92
+ return lookupCheckpoint (_checkpoints[account], blockNumber);
109
93
}
110
94
111
- /**
112
- * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
113
- * It is but NOT the sum of all the delegated votes!
114
- *
115
- * Requirements:
116
- *
117
- * - `blockNumber` must have been already mined
118
- */
95
+ /// @notice Retrieve the `totalSupply` at the end of `blockNumber`.
96
+ /// Note, this value is the sum of all balances, but it is NOT the
97
+ /// sum of all the delegated votes!
98
+ /// @param blockNumber The block number to get the total supply at
99
+ /// @dev `blockNumber` must have been already mined
119
100
function getPastTotalSupply (uint256 blockNumber )
120
101
public
121
102
view
122
103
returns (uint96 )
123
104
{
124
- return _checkpointsLookup (_totalSupplyCheckpoints, blockNumber);
105
+ return lookupCheckpoint (_totalSupplyCheckpoints, blockNumber);
125
106
}
126
107
108
+ /// @notice Encodes a `blockNumber` and `value` into a single `uint128`
109
+ /// checkpoint.
110
+ /// @dev `blockNumber` is stored in the first 32 bits, while `value` in the
111
+ /// remaining 96 bits.
127
112
function encodeCheckpoint (uint32 blockNumber , uint96 value )
128
113
internal
129
114
pure
@@ -132,46 +117,55 @@ abstract contract Checkpoints {
132
117
return (uint128 (blockNumber) << 96 ) | uint128 (value);
133
118
}
134
119
135
- function decodeBlockNumber (uint128 _checkpoint )
120
+ /// @notice Decodes a block number from a `uint128` `checkpoint`.
121
+ function decodeBlockNumber (uint128 checkpoint )
136
122
internal
137
123
pure
138
124
returns (uint32 )
139
125
{
140
- return uint32 (bytes4 (bytes16 (_checkpoint )));
126
+ return uint32 (bytes4 (bytes16 (checkpoint )));
141
127
}
142
128
143
- function decodeValue (uint128 _checkpoint ) internal pure returns (uint96 ) {
144
- return uint96 (_checkpoint);
129
+ /// @notice Decodes a voting value from a `uint128` `checkpoint`.
130
+ function decodeValue (uint128 checkpoint ) internal pure returns (uint96 ) {
131
+ return uint96 (checkpoint);
145
132
}
146
133
147
- function decodeCheckpoint (uint128 _checkpoint )
134
+ /// @notice Decodes a block number and voting value from a `uint128`
135
+ /// `checkpoint`.
136
+ function decodeCheckpoint (uint128 checkpoint )
148
137
internal
149
138
pure
150
139
returns (uint32 blockNumber , uint96 value )
151
140
{
152
- blockNumber = decodeBlockNumber (_checkpoint );
153
- value = decodeValue (_checkpoint );
141
+ blockNumber = decodeBlockNumber (checkpoint );
142
+ value = decodeValue (checkpoint );
154
143
}
155
144
156
- /**
157
- * @dev Lookup a value in a list of (sorted) checkpoints.
158
- */
159
- function _checkpointsLookup (uint128 [] storage ckpts , uint256 blockNumber )
145
+ /// @notice Lookup a value in a list of (sorted) checkpoints.
146
+ /// @param ckpts The checkpoints array to use
147
+ /// @param blockNumber Block number when we want to get the checkpoint at
148
+ function lookupCheckpoint (uint128 [] storage ckpts , uint256 blockNumber )
160
149
internal
161
150
view
162
151
returns (uint96 )
163
152
{
164
- // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
165
- //
166
- // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
167
- // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
168
- // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
169
- // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
170
- // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
171
- // out of bounds (in which case we're looking too far in the past and the result is 0).
172
- // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
173
- // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
174
- // the same.
153
+ // We run a binary search to look for the earliest checkpoint taken
154
+ // after `blockNumber`. During the loop, the index of the wanted
155
+ // checkpoint remains in the range [low-1, high). With each iteration,
156
+ // either `low` or `high` is moved towards the middle of the range to
157
+ // maintain the invariant.
158
+ // - If the middle checkpoint is after `blockNumber`,
159
+ // we look in [low, mid)
160
+ // - If the middle checkpoint is before or equal to `blockNumber`,
161
+ // we look in [mid+1, high)
162
+ // Once we reach a single value (when low == high), we've found the
163
+ // right checkpoint at the index high-1, if not out of bounds (in that
164
+ // case we're looking too far in the past and the result is 0).
165
+ // Note that if the latest checkpoint available is exactly for
166
+ // `blockNumber`, we end up with an index that is past the end of the
167
+ // array, so we technically don't find a checkpoint after
168
+ // `blockNumber`, but it works out the same.
175
169
require (blockNumber < block .number , "Block not yet determined " );
176
170
177
171
uint256 high = ckpts.length ;
@@ -189,40 +183,50 @@ abstract contract Checkpoints {
189
183
return high == 0 ? 0 : decodeValue (ckpts[high - 1 ]);
190
184
}
191
185
192
- /**
193
- * @dev Maximum token supply. Defaults to `type(uint96).max` (2^96^ - 1).
194
- */
195
- function _maxSupply () internal view virtual returns (uint96 ) {
186
+ /// @notice Maximum token supply. Defaults to `type(uint96).max` (2^96 - 1)
187
+ function maxSupply () internal view virtual returns (uint96 ) {
196
188
return type (uint96 ).max;
197
189
}
198
190
199
- function _moveVotingPower (
191
+ /// @notice Moves voting power from one delegate to another
192
+ /// @param src Address of old delegate
193
+ /// @param dst Address of new delegate
194
+ /// @param amount Voting power amount to transfer between delegates
195
+ function moveVotingPower (
200
196
address src ,
201
197
address dst ,
202
198
uint256 amount
203
199
) internal {
204
200
if (src != dst && amount > 0 ) {
205
201
if (src != address (0 )) {
206
- (uint256 oldWeight , uint256 newWeight ) = _writeCheckpoint (
202
+ (uint256 oldWeight , uint256 newWeight ) = writeCheckpoint (
207
203
_checkpoints[src],
208
- _subtract ,
204
+ subtract ,
209
205
amount
210
206
);
211
207
emit DelegateVotesChanged (src, oldWeight, newWeight);
212
208
}
213
209
214
210
if (dst != address (0 )) {
215
- (uint256 oldWeight , uint256 newWeight ) = _writeCheckpoint (
211
+ (uint256 oldWeight , uint256 newWeight ) = writeCheckpoint (
216
212
_checkpoints[dst],
217
- _add ,
213
+ add ,
218
214
amount
219
215
);
220
216
emit DelegateVotesChanged (dst, oldWeight, newWeight);
221
217
}
222
218
}
223
219
}
224
220
225
- function _writeCheckpoint (
221
+ /// @notice Writes a new checkpoint based on operating last stored value
222
+ /// with a `delta`. Usually, said operation is the `add` or
223
+ /// `subtract` functions from this contract, but more complex
224
+ /// functions can be passed as parameters.
225
+ /// @param ckpts The checkpoints array to use
226
+ /// @param op The function to apply over the last value and the `delta`
227
+ /// @param delta Variation with respect to last stored value to be used
228
+ /// for new checkpoint
229
+ function writeCheckpoint (
226
230
uint128 [] storage ckpts ,
227
231
function (uint256 , uint256 ) view returns (uint256 ) op,
228
232
uint256 delta
@@ -251,12 +255,12 @@ abstract contract Checkpoints {
251
255
}
252
256
253
257
// slither-disable-next-line dead-code
254
- function _add (uint256 a , uint256 b ) internal pure returns (uint256 ) {
258
+ function add (uint256 a , uint256 b ) internal pure returns (uint256 ) {
255
259
return a + b;
256
260
}
257
261
258
262
// slither-disable-next-line dead-code
259
- function _subtract (uint256 a , uint256 b ) internal pure returns (uint256 ) {
263
+ function subtract (uint256 a , uint256 b ) internal pure returns (uint256 ) {
260
264
return a - b;
261
265
}
262
266
}
0 commit comments