2
2
pragma solidity ^ 0.8.30 ;
3
3
4
4
import {Test} from "forge-std/Test.sol " ;
5
- import { ERC1967Proxy } from " @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol " ;
6
- import {XanV1} from "src/XanV1.sol " ;
5
+
6
+ import {XanV1} from "../../ src/XanV1.sol " ;
7
7
8
8
contract XanV1Handler is Test {
9
9
XanV1 public token;
10
10
address public initialHolder;
11
11
12
12
// ============ GHOST VARIABLES (for invariant tracking) ============
13
- address [] public actors ;
14
- mapping (address => bool ) internal isActor ;
13
+ address [] internal _actors ;
14
+ mapping (address actor = > bool isActor ) internal _isActor ;
15
15
16
16
// Valid implementation addresses for testing
17
- address [5 ] public validImpls = [address (0x1111 ), address (0x2222 ), address (0x3333 ), address (0x4444 ), address (0x5555 )];
17
+ address [5 ] internal _validImpls =
18
+ [address (0x1111 ), address (0x2222 ), address (0x3333 ), address (0x4444 ), address (0x5555 )];
18
19
19
- mapping (address => mapping (address => uint256 )) internal previousVotes; // voter => impl => votes
20
- mapping (address => uint256 ) internal previousLockedBalances ;
21
- uint256 internal previousLockedSupply ;
20
+ mapping (address voter = > mapping (address impl = > uint256 previousVote )) internal _previousVotes;
21
+ mapping (address voter = > uint256 previousLockedBalance ) internal _previousLockedBalances ;
22
+ uint256 internal _previousLockedSupply ;
22
23
23
24
constructor (XanV1 _token , address _initialHolder ) {
24
25
token = _token;
25
26
initialHolder = _initialHolder;
26
27
_addActor (_initialHolder);
27
28
}
28
29
29
- // ============ HELPER FUNCTIONS ============
30
-
31
- function _addActor (address a ) internal {
32
- if (! isActor[a]) {
33
- isActor[a] = true ;
34
- actors.push (a);
35
- }
36
- }
37
-
38
- function getActors () external view returns (address [] memory ) {
39
- return actors;
40
- }
41
-
42
- function getValidImpls () external view returns (address [5 ] memory ) {
43
- return validImpls;
44
- }
45
-
46
- function getPreviousVotes (address voter , address impl ) external view returns (uint256 ) {
47
- return previousVotes[voter][impl];
48
- }
49
-
50
- function getPreviousLockedBalance (address account ) external view returns (uint256 ) {
51
- return previousLockedBalances[account];
52
- }
53
-
54
- function getPreviousLockedSupply () external view returns (uint256 ) {
55
- return previousLockedSupply;
56
- }
57
-
58
- function updateStateTracking () public {
59
- // Update previous locked supply
60
- previousLockedSupply = token.lockedSupply ();
61
-
62
- // Update previous locked balances and votes for all actors
63
- for (uint256 i = 0 ; i < actors.length ; i++ ) {
64
- address actor = actors[i];
65
- previousLockedBalances[actor] = token.lockedBalanceOf (actor);
66
- // Capture votes across all implementations
67
- for (uint256 j = 0 ; j < validImpls.length ; j++ ) {
68
- previousVotes[actor][validImpls[j]] = token.getVotes (actor, validImpls[j]);
69
- }
70
- }
71
- }
72
-
73
- function updateStateTrackingFor (address account ) public {
74
- // Update previous locked supply
75
- previousLockedSupply = token.lockedSupply ();
76
-
77
- // Update previous locked balance and votes for a specific account
78
- previousLockedBalances[account] = token.lockedBalanceOf (account);
79
- for (uint256 j = 0 ; j < validImpls.length ; j++ ) {
80
- previousVotes[account][validImpls[j]] = token.getVotes (account, validImpls[j]);
81
- }
82
- }
83
-
84
- function updateStateTrackingFor (address account1 , address account2 ) public {
85
- // Update previous locked supply
86
- previousLockedSupply = token.lockedSupply ();
87
-
88
- // Update state for account1
89
- previousLockedBalances[account1] = token.lockedBalanceOf (account1);
90
- for (uint256 j = 0 ; j < validImpls.length ; j++ ) {
91
- previousVotes[account1][validImpls[j]] = token.getVotes (account1, validImpls[j]);
92
- }
93
-
94
- // Update state for account2
95
- previousLockedBalances[account2] = token.lockedBalanceOf (account2);
96
- for (uint256 j = 0 ; j < validImpls.length ; j++ ) {
97
- previousVotes[account2][validImpls[j]] = token.getVotes (account2, validImpls[j]);
98
- }
99
- }
100
-
101
30
// ============ FUZZED BUSINESS LOGIC FUNCTIONS ============
102
31
103
32
function airdrop (address to , uint256 amount ) external {
104
33
// bounding to non-zero addresses, to avoid unnecessary reverts
105
34
to = address (uint160 (bound (uint160 (to), 1 , type (uint160 ).max)));
35
+
106
36
_addActor (to);
37
+
107
38
uint256 unlocked = token.unlockedBalanceOf (initialHolder);
108
39
amount = bound (amount, 0 , unlocked);
40
+
109
41
updateStateTrackingFor (initialHolder, to);
42
+
110
43
vm.prank (initialHolder);
111
44
token.transfer (to, amount);
112
45
}
113
46
114
47
function transfer (address from , address to , uint256 amount ) external {
115
48
from = address (uint160 (bound (uint160 (from), 1 , type (uint160 ).max)));
116
49
to = address (uint160 (bound (uint160 (to), 1 , type (uint160 ).max)));
50
+
117
51
_addActor (from);
118
52
_addActor (to);
53
+
119
54
uint256 unlocked = token.unlockedBalanceOf (from);
120
55
amount = bound (amount, 0 , unlocked);
56
+
121
57
updateStateTrackingFor (from, to);
58
+
122
59
vm.prank (from);
123
60
token.transfer (to, amount);
124
61
}
@@ -137,8 +74,10 @@ contract XanV1Handler is Test {
137
74
function transferAndLock (address from , address to , uint256 amount ) external {
138
75
from = address (uint160 (bound (uint160 (from), 1 , type (uint160 ).max)));
139
76
to = address (uint160 (bound (uint160 (to), 1 , type (uint160 ).max)));
77
+
140
78
_addActor (from);
141
79
_addActor (to);
80
+
142
81
uint256 unlocked = token.unlockedBalanceOf (from);
143
82
amount = bound (amount, 0 , unlocked);
144
83
@@ -154,8 +93,8 @@ contract XanV1Handler is Test {
154
93
_addActor (who);
155
94
156
95
// Restrict to only valid implementation addresses
157
- implIndex = bound (implIndex, 0 , validImpls .length - 1 );
158
- address impl = validImpls [implIndex];
96
+ implIndex = bound (implIndex, 0 , _validImpls .length - 1 );
97
+ address impl = _validImpls [implIndex];
159
98
160
99
updateStateTrackingFor (who);
161
100
@@ -198,8 +137,8 @@ contract XanV1Handler is Test {
198
137
// Council-only; prank as council.
199
138
address council = token.governanceCouncil ();
200
139
201
- implIndex = bound (implIndex, 0 , validImpls .length - 1 );
202
- address impl = validImpls [implIndex];
140
+ implIndex = bound (implIndex, 0 , _validImpls .length - 1 );
141
+ address impl = _validImpls [implIndex];
203
142
204
143
updateStateTracking ();
205
144
@@ -223,4 +162,78 @@ contract XanV1Handler is Test {
223
162
// Anyone can attempt; requires quorum/min-locked reached or will revert.
224
163
token.vetoCouncilUpgrade ();
225
164
}
165
+
166
+ // ============ GETTER FUNCTIONS ============
167
+
168
+ function getActors () external view returns (address [] memory arr ) {
169
+ arr = _actors;
170
+ }
171
+
172
+ function getValidImpls () external view returns (address [5 ] memory arr ) {
173
+ arr = _validImpls;
174
+ }
175
+
176
+ function getPreviousVotes (address voter , address impl ) external view returns (uint256 previousVotes ) {
177
+ previousVotes = _previousVotes[voter][impl];
178
+ }
179
+
180
+ function getPreviousLockedBalance (address account ) external view returns (uint256 previousLockedBalance ) {
181
+ previousLockedBalance = _previousLockedBalances[account];
182
+ }
183
+
184
+ function getPreviousLockedSupply () external view returns (uint256 previousLockedSupply ) {
185
+ previousLockedSupply = _previousLockedSupply;
186
+ }
187
+
188
+ // ============ HELPER FUNCTIONS ============
189
+
190
+ function updateStateTracking () public {
191
+ // Update previous locked supply
192
+ _previousLockedSupply = token.lockedSupply ();
193
+
194
+ // Update previous locked balances and votes for all actors
195
+ for (uint256 i = 0 ; i < _actors.length ; ++ i) {
196
+ address actor = _actors[i];
197
+ _previousLockedBalances[actor] = token.lockedBalanceOf (actor);
198
+ // Capture votes across all implementations
199
+ for (uint256 j = 0 ; j < _validImpls.length ; ++ j) {
200
+ _previousVotes[actor][_validImpls[j]] = token.getVotes (actor, _validImpls[j]);
201
+ }
202
+ }
203
+ }
204
+
205
+ function updateStateTrackingFor (address account ) public {
206
+ // Update previous locked supply
207
+ _previousLockedSupply = token.lockedSupply ();
208
+
209
+ // Update previous locked balance and votes for a specific account
210
+ _previousLockedBalances[account] = token.lockedBalanceOf (account);
211
+ for (uint256 j = 0 ; j < _validImpls.length ; ++ j) {
212
+ _previousVotes[account][_validImpls[j]] = token.getVotes (account, _validImpls[j]);
213
+ }
214
+ }
215
+
216
+ function updateStateTrackingFor (address account1 , address account2 ) public {
217
+ // Update previous locked supply
218
+ _previousLockedSupply = token.lockedSupply ();
219
+
220
+ // Update state for account1
221
+ _previousLockedBalances[account1] = token.lockedBalanceOf (account1);
222
+ for (uint256 j = 0 ; j < _validImpls.length ; ++ j) {
223
+ _previousVotes[account1][_validImpls[j]] = token.getVotes (account1, _validImpls[j]);
224
+ }
225
+
226
+ // Update state for account2
227
+ _previousLockedBalances[account2] = token.lockedBalanceOf (account2);
228
+ for (uint256 j = 0 ; j < _validImpls.length ; ++ j) {
229
+ _previousVotes[account2][_validImpls[j]] = token.getVotes (account2, _validImpls[j]);
230
+ }
231
+ }
232
+
233
+ function _addActor (address a ) internal {
234
+ if (! _isActor[a]) {
235
+ _isActor[a] = true ;
236
+ _actors.push (a);
237
+ }
238
+ }
226
239
}
0 commit comments