Skip to content

Commit f439da1

Browse files
committed
Simplify subdelegation implementations
1 parent 3e53065 commit f439da1

File tree

5 files changed

+59
-130
lines changed

5 files changed

+59
-130
lines changed

src/FlexVotingClient.sol

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,11 @@ abstract contract FlexVotingClient {
202202
);
203203
}
204204

205-
/// @dev Checkpoints the _user's current raw balance.
206-
function _checkpointRawBalanceOf(address _user) internal {
207-
balanceCheckpoints[_user].push(IVotingToken(GOVERNOR.token()).clock(), _rawBalanceOf(_user));
208-
}
205+
function _applyDeltaToCheckpoint(
206+
Checkpoints.Trace208 storage _checkpoint,
207+
int256 _delta
208+
) internal returns (uint208 _prevTotal, uint208 _newTotal) {
209209

210-
/// @dev Checkpoints the total balance after applying `_delta`.
211-
function _checkpointTotalBalance(int256 _delta) internal {
212210
// The casting in this function is safe since:
213211
// - if oldTotal + delta > int256.max it will panic and revert.
214212
// - if |delta| <= oldTotal
@@ -226,12 +224,25 @@ abstract contract FlexVotingClient {
226224
// uint256.max + int256.min > uint208.max
227225
// Substituting again:
228226
// wrapped(int256.min) > uint208.max, which will revert when safecast.
229-
uint256 _oldTotal = uint256(totalBalanceCheckpoints.latest());
230-
uint256 _newTotal = uint256(int256(_oldTotal) + _delta);
227+
_prevTotal = _checkpoint.latest();
228+
int256 _castTotal = int256(uint256(_prevTotal));
229+
_newTotal = SafeCast.toUint208(uint256(_castTotal + _delta));
231230

232-
totalBalanceCheckpoints.push(
233-
IVotingToken(GOVERNOR.token()).clock(), SafeCast.toUint208(_newTotal)
234-
);
231+
uint48 _timepoint = IVotingToken(GOVERNOR.token()).clock();
232+
_checkpoint.push(_timepoint, _newTotal);
233+
}
234+
235+
/// @dev Checkpoints the _user's current raw balance.
236+
function _checkpointRawBalanceOf(
237+
address _user,
238+
int256 _delta
239+
) internal virtual {
240+
_applyDeltaToCheckpoint(balanceCheckpoints[_user], _delta);
241+
}
242+
243+
/// @dev Checkpoints the total balance after applying `_delta`.
244+
function _checkpointTotalBalance(int256 _delta) internal virtual {
245+
_applyDeltaToCheckpoint(totalBalanceCheckpoints, _delta);
235246
}
236247

237248
/// @notice Returns the `_user`'s raw balance at `_timepoint`.

src/FlexVotingDelegatable.sol

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,39 +43,31 @@ abstract contract FlexVotingDelegatable is Context, FlexVotingClient {
4343
address oldDelegate = delegates(account);
4444
_delegatee[account] = delegatee;
4545

46+
int256 _delta = int256(uint256(_rawBalanceOf(account)));
4647
emit DelegateChanged(account, oldDelegate, delegatee);
47-
_updateDelegateBalance(oldDelegate, delegatee, _rawBalanceOf(account));
48+
_updateDelegateBalance(oldDelegate, delegatee, _delta);
4849
}
4950

50-
// @dev Moves delegated votes from one delegate to another.
51-
function _updateDelegateBalance(address from, address to, uint208 amount) internal virtual {
52-
if (from == to || amount == 0) return;
53-
54-
if (from != address(0)) {
55-
(uint256 oldValue, uint256 newValue) =
56-
_push(FlexVotingClient.balanceCheckpoints[from], _subtract, amount);
57-
emit DelegateWeightChanged(from, oldValue, newValue);
58-
}
59-
if (to != address(0)) {
60-
(uint256 oldValue, uint256 newValue) =
61-
_push(FlexVotingClient.balanceCheckpoints[to], _add, amount);
62-
emit DelegateWeightChanged(to, oldValue, newValue);
63-
}
51+
function _checkpointRawBalanceOf(
52+
address _user,
53+
int256 _delta
54+
) internal virtual override {
55+
address _proxy = delegates(_user);
56+
_applyDeltaToCheckpoint(balanceCheckpoints[_proxy], _delta);
6457
}
6558

66-
function _push(
67-
Checkpoints.Trace208 storage store,
68-
function(uint208, uint208) view returns (uint208) fn,
69-
uint208 delta
70-
) private returns (uint208 oldValue, uint208 newValue) {
71-
return store.push(IVotingToken(GOVERNOR.token()).clock(), fn(store.latest(), delta));
72-
}
59+
// @dev Moves delegated votes from one delegate to another.
60+
function _updateDelegateBalance(address from, address to, int256 _delta) internal virtual {
61+
if (from == to || _delta == 0) return;
7362

74-
function _add(uint208 a, uint208 b) private pure returns (uint208) {
75-
return a + b;
76-
}
63+
// Decrement old delegate's weight.
64+
(uint208 _oldFrom, uint208 _newFrom) =
65+
_applyDeltaToCheckpoint(balanceCheckpoints[from], -_delta);
66+
emit DelegateWeightChanged(from, _oldFrom, _newFrom);
7767

78-
function _subtract(uint208 a, uint208 b) private pure returns (uint208) {
79-
return a - b;
68+
// Increment new delegate's weight.
69+
(uint208 _oldTo, uint208 _newTo) =
70+
_applyDeltaToCheckpoint(balanceCheckpoints[to], _delta);
71+
emit DelegateWeightChanged(to, _oldTo, _newTo);
8072
}
8173
}

test/MockFlexVotingClient.sol

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ contract MockFlexVotingClient is FlexVotingClient {
5555
deposits[_user] = _amount;
5656
}
5757

58-
function exposed_checkpointRawBalanceOf(address _user) external {
59-
return _checkpointRawBalanceOf(_user);
58+
function exposed_checkpointRawBalanceOf(address _user, int256 _delta) external {
59+
_checkpointRawBalanceOf(_user, _delta);
6060
}
6161
// End test hooks
6262
// ---------------------------------------------------------------------------
@@ -66,8 +66,9 @@ contract MockFlexVotingClient is FlexVotingClient {
6666
function deposit(uint208 _amount) public {
6767
deposits[msg.sender] += _amount;
6868

69-
FlexVotingClient._checkpointRawBalanceOf(msg.sender);
70-
FlexVotingClient._checkpointTotalBalance(int256(uint256(_amount)));
69+
int256 _delta = int256(uint256(_amount));
70+
_checkpointRawBalanceOf(msg.sender, _delta);
71+
_checkpointTotalBalance(_delta);
7172

7273
// Assumes revert on failure.
7374
TOKEN.transferFrom(msg.sender, address(this), _amount);
@@ -79,8 +80,9 @@ contract MockFlexVotingClient is FlexVotingClient {
7980
// Overflows & reverts if user does not have sufficient deposits.
8081
deposits[msg.sender] -= _amount;
8182

82-
FlexVotingClient._checkpointRawBalanceOf(msg.sender);
83-
FlexVotingClient._checkpointTotalBalance(-1 * int256(uint256(_amount)));
83+
int256 _delta = -1 * int256(uint256(_amount));
84+
_checkpointRawBalanceOf(msg.sender, _delta);
85+
_checkpointTotalBalance(_delta);
8486

8587
TOKEN.transfer(msg.sender, _amount); // Assumes revert on failure.
8688
}

test/MockFlexVotingDelegatableClient.sol

Lines changed: 8 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -9,92 +9,15 @@ import {IVotingToken} from "src/interfaces/IVotingToken.sol";
99
import {FlexVotingClient} from "src/FlexVotingClient.sol";
1010
import {FlexVotingDelegatable} from "src/FlexVotingDelegatable.sol";
1111

12-
contract MockFlexVotingDelegatableClient is FlexVotingDelegatable {
13-
using Checkpoints for Checkpoints.Trace208;
12+
import {MockFlexVotingClient} from "test/MockFlexVotingClient.sol";
1413

15-
/// @notice The governance token held and lent by this pool.
16-
ERC20Votes public immutable TOKEN;
14+
contract MockFlexVotingDelegatableClient is MockFlexVotingClient, FlexVotingDelegatable {
15+
constructor(address _governor) MockFlexVotingClient(_governor) {}
1716

18-
/// @notice Map depositor to deposit amount.
19-
mapping(address => uint208) public deposits;
20-
21-
/// @notice Map borrower to total amount borrowed.
22-
mapping(address => uint256) public borrowTotal;
23-
24-
constructor(address _governor) FlexVotingClient(_governor) {
25-
TOKEN = ERC20Votes(GOVERNOR.token());
26-
_selfDelegate();
27-
}
28-
29-
function _rawBalanceOf(address _user) internal view override returns (uint208) {
30-
return deposits[_user];
31-
}
32-
33-
// Test hooks
34-
// ---------------------------------------------------------------------------
35-
function exposed_rawBalanceOf(address _user) external view returns (uint208) {
36-
return _rawBalanceOf(_user);
37-
}
38-
39-
function exposed_latestTotalBalance() external view returns (uint208) {
40-
return totalBalanceCheckpoints.latest();
41-
}
42-
43-
function exposed_checkpointTotalBalance(int256 _delta) external {
44-
return _checkpointTotalBalance(_delta);
45-
}
46-
47-
function exposed_castVoteReasonString() external returns (string memory) {
48-
return _castVoteReasonString();
49-
}
50-
51-
function exposed_selfDelegate() external {
52-
return _selfDelegate();
53-
}
54-
55-
function exposed_setDeposits(address _user, uint208 _amount) external {
56-
deposits[_user] = _amount;
57-
}
58-
59-
function exposed_checkpointRawBalanceOf(address _user) external {
60-
return _checkpointRawBalanceOf(_user);
61-
}
62-
// End test hooks
63-
// ---------------------------------------------------------------------------
64-
65-
/// @notice Allow a holder of the governance token to deposit it into the pool.
66-
/// @param _amount The amount to be deposited.
67-
function deposit(uint208 _amount) public {
68-
deposits[msg.sender] += _amount;
69-
70-
FlexVotingClient._checkpointTotalBalance(int256(uint256(_amount)));
71-
72-
address _delegate = delegates(msg.sender);
73-
FlexVotingDelegatable._updateDelegateBalance(address(0), _delegate, _amount);
74-
75-
// Assumes revert on failure.
76-
TOKEN.transferFrom(msg.sender, address(this), _amount);
77-
}
78-
79-
/// @notice Allow a depositor to withdraw funds previously deposited to the pool.
80-
/// @param _amount The amount to be withdrawn.
81-
function withdraw(uint208 _amount) public {
82-
// Overflows & reverts if user does not have sufficient deposits.
83-
deposits[msg.sender] -= _amount;
84-
85-
FlexVotingClient._checkpointTotalBalance(-1 * int256(uint256(_amount)));
86-
87-
address _delegate = delegates(msg.sender);
88-
FlexVotingDelegatable._updateDelegateBalance(_delegate, address(0), _amount);
89-
90-
TOKEN.transfer(msg.sender, _amount); // Assumes revert on failure.
91-
}
92-
93-
/// @notice Arbitrarily remove tokens from the pool. This is to simulate a borrower, hence the
94-
/// method name. Since this is just a proof-of-concept, nothing else is actually done here.
95-
/// @param _amount The amount to "borrow."
96-
function borrow(uint256 _amount) public {
97-
borrowTotal[msg.sender] += _amount;
98-
TOKEN.transfer(msg.sender, _amount);
17+
function _checkpointRawBalanceOf(
18+
address _user,
19+
int256 _delta
20+
) internal override(FlexVotingClient, FlexVotingDelegatable) {
21+
return FlexVotingDelegatable._checkpointRawBalanceOf(_user, _delta);
9922
}
10023
}

test/SharedFlexVoting.t.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ abstract contract _CheckpointRawBalanceOf is FlexVotingClientTest {
242242

243243
_advanceTimeTo(_future);
244244
flexClient.exposed_setDeposits(_user, _amount);
245-
flexClient.exposed_checkpointRawBalanceOf(_user);
245+
int256 _delta = int256(uint256(_amount));
246+
flexClient.exposed_checkpointRawBalanceOf(_user, _delta);
246247

247248
assertEq(flexClient.getPastRawBalance(_user, _past), 0);
248249
assertEq(flexClient.getPastRawBalance(_user, _future), _amount);

0 commit comments

Comments
 (0)