-
Notifications
You must be signed in to change notification settings - Fork 18
/
Accounts.sol
433 lines (357 loc) · 18.2 KB
/
Accounts.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.13 <0.9.0;
import {stdStorage, StdStorage} from "forge-std/StdStorage.sol";
import {strings} from "./Strings.sol";
import "./Vulcan.sol";
import {formatError} from "../_utils/formatError.sol";
library accountsSafe {
/// @dev Reads the storage at the specified `slot` for the given `who` address and returns the content.
/// @param who The address whose storage will be read.
/// @param slot The position of the storage slot to read.
/// @return The contents of the specified storage slot as a bytes32 value.
function readStorage(address who, bytes32 slot) internal view returns (bytes32) {
return vulcan.hevm.load(who, slot);
}
/// @dev Signs the specified `digest` using the provided `privKey` and returns the signature in the form of `(v, r, s)`.
/// @param privKey The private key to use for signing the digest.
/// @param digest The message digest to sign.
/// @return A tuple containing the signature parameters `(v, r, s)` as a `uint8`, `bytes32`, and `bytes32`, respectively.
function sign(uint256 privKey, bytes32 digest) internal pure returns (uint8, bytes32, bytes32) {
return vulcan.hevm.sign(privKey, digest);
}
/// @dev Derives the Ethereum address corresponding to the provided `privKey`.
/// @param privKey The private key to use for deriving the Ethereum address.
/// @return The Ethereum address derived from the provided private key.
function derive(uint256 privKey) internal pure returns (address) {
return vulcan.hevm.addr(privKey);
}
/// @dev Derives the private key corresponding to the specified `mnemonicOrPath` and `index`.
/// @param mnemonicOrPath The mnemonic or derivation path to use for deriving the private key.
/// @param index The index of the derived private key to retrieve.
/// @return The private key derived from the specified mnemonic and index as a `uint256` value.
function deriveKey(string memory mnemonicOrPath, uint32 index) internal pure returns (uint256) {
return vulcan.hevm.deriveKey(mnemonicOrPath, index);
}
/// @dev Derives the private key corresponding to the specified `mnemonicOrPath`, `derivationPath`, and `index`.
/// @param mnemonicOrPath The mnemonic or derivation path to use for deriving the master key.
/// @param derivationPath The specific derivation path to use for deriving the private key (optional).
/// @param index The index of the derived private key to retrieve.
/// @return The private key derived from the specified mnemonic, derivation path, and index as a `uint256` value.
function deriveKey(string memory mnemonicOrPath, string memory derivationPath, uint32 index)
internal
pure
returns (uint256)
{
return vulcan.hevm.deriveKey(mnemonicOrPath, derivationPath, index);
}
/// @dev Adds the specified `privKey` to the local forge wallet.
/// @param privKey The private key to add to the local forge wallet.
/// @return The Ethereum address corresponding to the added private key.
function rememberKey(uint256 privKey) internal returns (address) {
return vulcan.hevm.rememberKey(privKey);
}
/// @dev Returns the current `nonce` of the specified `who` address.
/// @param who The address for which to obtain the current `nonce`.
/// @return The current `nonce` of the specified address as a `uint64` value.
function getNonce(address who) internal view returns (uint64) {
return vulcan.hevm.getNonce(who);
}
/// @dev Starts recording all storage reads and writes for later analysis.
function recordStorage() internal {
vulcan.hevm.record();
}
/// @dev Obtains an array of slots that have been read and written for the specified address `who`.
/// @param who The address for which to obtain the storage accesses.
/// @return reads An array of storage slots that have been read.
/// @return writes An array of storage slots that have been written.
function getStorageAccesses(address who) internal returns (bytes32[] memory reads, bytes32[] memory writes) {
return vulcan.hevm.accesses(who);
}
/// @dev Adds a label to the specified address `who` for identification purposes in debug traces.
/// @param who The address to label.
/// @param lbl The label to apply to the address.
/// @return The same address that was passed as input.
function label(address who, string memory lbl) internal returns (address) {
vulcan.hevm.label(who, lbl);
return who;
}
/// @dev Creates an address without label.
function create() internal returns (address) {
uint256 id = _incrementId();
return derive(uint256(keccak256(abi.encode(id))));
}
/// @dev Creates an address using the hash of the specified `name` as the private key and adds a label to the address.
/// @param name The name to use as the basis for the address.
/// @return The newly created address.
function create(string memory name) internal returns (address) {
return create(name, name);
}
/// @dev Creates an address using the hash of the specified `name` as the private key and adds a label to the address.
/// @param name The name to use as the basis for the address.
/// @param lbl The label to apply to the address.
/// @return The newly created address.
function create(string memory name, string memory lbl) internal returns (address) {
address addr = derive(uint256(keccak256(abi.encodePacked(name))));
return label(addr, lbl);
}
/// @dev Calculates the deployment address of `who` with nonce `nonce`.
/// @param who The deployer address.
/// @param nonce The deployer nonce.
function getDeploymentAddress(address who, uint64 nonce) internal pure returns (address) {
bytes memory data;
if (nonce == 0x00) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), who, bytes1(0x80));
} else if (nonce <= 0x7f) {
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), who, uint8(nonce));
} else if (nonce <= 0xff) {
data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), who, bytes1(0x81), uint8(nonce));
} else if (nonce <= 0xffff) {
data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), who, bytes1(0x82), uint16(nonce));
} else if (nonce <= 0xffffff) {
data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), who, bytes1(0x83), uint24(nonce));
} else if (nonce <= 0xffffffff) {
data = abi.encodePacked(bytes1(0xda), bytes1(0x94), who, bytes1(0x84), uint32(nonce));
} else if (nonce <= 0xffffffffff) {
data = abi.encodePacked(bytes1(0xdb), bytes1(0x94), who, bytes1(0x85), uint40(nonce));
} else if (nonce <= 0xffffffffffff) {
data = abi.encodePacked(bytes1(0xdc), bytes1(0x94), who, bytes1(0x86), uint48(nonce));
} else if (nonce <= 0xffffffffffffff) {
data = abi.encodePacked(bytes1(0xdd), bytes1(0x94), who, bytes1(0x87), uint56(nonce));
} else if (nonce <= 0xffffffffffffffff) {
data = abi.encodePacked(bytes1(0xde), bytes1(0x94), who, bytes1(0x88), uint64(nonce));
}
return address(uint160(uint256(keccak256(data))));
}
/// @dev Calculates the deployment address of `who` with the current nonce.
/// @param who The deployer address.
function getDeploymentAddress(address who) internal view returns (address) {
return getDeploymentAddress(who, getNonce(who));
}
/// @dev Generates an array of addresses with a specific length.
/// @param length The amount of addresses to generate.
function createMany(uint256 length) internal returns (address[] memory) {
require(length > 0, _formatError("createMany(uint256)", "Invalid length for addresses array"));
address[] memory addresses = new address[](length);
for (uint256 i; i < length; i++) {
addresses[i] = create();
}
return addresses;
}
/// @dev Generates an array of addresses with a specific length and a prefix as label.
/// The label for each address will be `{prefix}_{i}`.
/// @param length The amount of addresses to generate.
/// @param prefix The prefix of the label for each address.
function createMany(uint256 length, string memory prefix) internal returns (address[] memory) {
require(length > 0, "accounts: invalid length for addresses array");
address[] memory addresses = new address[](length);
for (uint256 i; i < length; i++) {
addresses[i] = create(string.concat(prefix, "_", strings.toString(i)));
}
return addresses;
}
function _incrementId() private returns (uint256 count) {
bytes32 slot = keccak256("vulcan.accounts.id.counter");
assembly {
count := sload(slot)
sstore(slot, add(count, 1))
}
}
function _formatError(string memory func, string memory message) private pure returns (string memory) {
return formatError("accounts", func, message);
}
}
library accounts {
using stdStorage for StdStorage;
function stdStore() internal pure returns (StdStorage storage s) {
bytes32 slot = keccak256("vulcan.accounts.stdStore");
assembly {
s.slot := slot
}
}
function readStorage(address who, bytes32 slot) internal view returns (bytes32) {
return accountsSafe.readStorage(who, slot);
}
function sign(uint256 privKey, bytes32 digest) internal pure returns (uint8, bytes32, bytes32) {
return accountsSafe.sign(privKey, digest);
}
function derive(uint256 privKey) internal pure returns (address) {
return accountsSafe.derive(privKey);
}
function deriveKey(string memory mnemonicOrPath, uint32 index) internal pure returns (uint256) {
return accountsSafe.deriveKey(mnemonicOrPath, index);
}
function deriveKey(string memory mnemonicOrPath, string memory derivationPath, uint32 index)
internal
pure
returns (uint256)
{
return accountsSafe.deriveKey(mnemonicOrPath, derivationPath, index);
}
function rememberKey(uint256 privKey) internal returns (address) {
return accountsSafe.rememberKey(privKey);
}
function getNonce(address who) internal view returns (uint64) {
return accountsSafe.getNonce(who);
}
function recordStorage() internal {
return accountsSafe.recordStorage();
}
function getStorageAccesses(address who) internal returns (bytes32[] memory reads, bytes32[] memory writes) {
return accountsSafe.getStorageAccesses(who);
}
function label(address who, string memory lbl) internal returns (address) {
return accountsSafe.label(who, lbl);
}
function create() internal returns (address) {
return accountsSafe.create();
}
function create(string memory name) internal returns (address) {
return accountsSafe.create(name);
}
function create(string memory name, string memory lbl) internal returns (address) {
return accountsSafe.create(name, lbl);
}
/// @dev Calculates the deployment address of `who` with nonce `nonce`.
/// @param who The deployer address.
/// @param nonce The deployer nonce.
function getDeploymentAddress(address who, uint64 nonce) internal pure returns (address) {
return accountsSafe.getDeploymentAddress(who, nonce);
}
/// @dev Calculates the deployment address of `who` with the current nonce.
/// @param who The deployer address.
function getDeploymentAddress(address who) internal view returns (address) {
return accountsSafe.getDeploymentAddress(who);
}
/// @dev Sets the specified `slot` in the storage of the given `self` address to the provided `value`.
/// @param self The address to modify the storage of.
/// @param slot The storage slot to set.
/// @param value The value to set the storage slot to.
/// @return The address that was modified.
function setStorage(address self, bytes32 slot, bytes32 value) internal returns (address) {
vulcan.hevm.store(self, slot, value);
return self;
}
/// @dev Sets the nonce of the given `self` address to the provided value `n`. It will revert if
// the new nonce is lower than the current address nonce.
/// @param self The address to set the nonce for.
/// @param n The value to set the nonce to.
/// @return The updated address with the modified nonce.
function setNonce(address self, uint64 n) internal returns (address) {
vulcan.hevm.setNonce(self, n);
return self;
}
/// @dev Sets the nonce of the given `self` address to the arbitrary provided value `n`.
/// @param self The address to set the nonce for.
/// @param n The value to set the nonce to.
/// @return The updated address with the modified nonce.
function setNonceUnsafe(address self, uint64 n) internal returns (address) {
vulcan.hevm.setNonceUnsafe(self, n);
return self;
}
/// @dev Sets the `msg.sender` of the next call to `self`.
/// @param self The address to impersonate.
/// @return The address that was impersonated.
function impersonateOnce(address self) internal returns (address) {
stopImpersonate();
vulcan.hevm.prank(self);
return self;
}
/// @notice Sets the `msg.sender` of all subsequent calls to `self` until `stopImpersonate` is called
/// @param self The address to impersonate.
/// @return The address being impersonated.
function impersonate(address self) internal returns (address) {
stopImpersonate();
vulcan.hevm.startPrank(self);
return self;
}
/// @dev Sets the `msg.sender` of the next call to `self` and the `tx.origin`
/// to `origin`.
/// @param self The address to impersonate.
/// @param origin The new `tx.origin`.
/// @return The address that was impersonated.
function impersonateOnce(address self, address origin) internal returns (address) {
stopImpersonate();
vulcan.hevm.prank(self, origin);
return self;
}
/// @dev Sets the `msg.sender` and `tx.origin` of all the subsequent calls to `self` and `origin`
/// respectively until `stopImpersonate` is called.
/// @param self The address to impersonate.
/// @param origin The new value for `tx.origin`.
/// @return The address being impersonated.
function impersonate(address self, address origin) internal returns (address) {
stopImpersonate();
vulcan.hevm.startPrank(self, origin);
return self;
}
/// @notice Resets the values of `msg.sender` and `tx.origin` to the original values.
function stopImpersonate() internal {
try vulcan.hevm.stopPrank() {} catch (bytes memory) {}
}
/// @dev Sets the balance of an address and returns the address that was modified.
/// @param self The address to set the balance of.
/// @param bal The new balance to set.
/// @return The address that was modified.
function setBalance(address self, uint256 bal) internal returns (address) {
vulcan.hevm.deal(self, bal);
return self;
}
/// @dev Mints an amount of tokens to an address. This operation modifies the total supply of the token.
/// @dev self The address that will own the tokens.
/// @dev token The token to mint.
/// @dev amount The amount of tokens to mint.
/// @return The adress that owns the minted tokens.
function mintToken(address self, address token, uint256 amount) internal returns (address) {
(, bytes memory balData) = token.call(abi.encodeWithSelector(0x70a08231, self));
uint256 prevBal = abi.decode(balData, (uint256));
setTokenBalance(self, token, prevBal + amount);
(, bytes memory totSupData) = token.call(abi.encodeWithSelector(0x18160ddd));
uint256 totSup = abi.decode(totSupData, (uint256));
setTotalSupply(token, totSup + amount);
return self;
}
/// @dev Burns an amount of tokens from an address.This operation modifies the total supply of the token.
/// @dev self The address that owns the tokens.
/// @dev token The token to burn.
/// @dev amount The amount of tokens to burn.
/// @return The adress that owned the burned tokens.
function burnToken(address self, address token, uint256 amount) internal returns (address) {
(, bytes memory balData) = token.call(abi.encodeWithSelector(0x70a08231, self));
uint256 prevBal = abi.decode(balData, (uint256));
setTokenBalance(self, token, prevBal - amount);
(, bytes memory totSupData) = token.call(abi.encodeWithSelector(0x18160ddd));
uint256 totSup = abi.decode(totSupData, (uint256));
setTotalSupply(token, totSup - amount);
return self;
}
/// @dev Sets the token balance of an address.
/// @param self The address to set the balance of.
/// @param token The token that will be given to `self`.
/// @param bal The new token balance of `self`.
/// @return The address that was modified.
function setTokenBalance(address self, address token, uint256 bal) internal returns (address) {
stdStore().target(token).sig(0x70a08231).with_key(self).checked_write(bal);
return self;
}
/// @dev Sets the token total supply of a token.
/// @param token The token that will be modified.
/// @param totalSupply The new total supply of token.
/// @return The token address.
function setTotalSupply(address token, uint256 totalSupply) private returns (address) {
stdStore().target(token).sig(0x18160ddd).checked_write(totalSupply);
return token;
}
/// @dev Sets the code of an address.
/// @param self The address to set the code for.
/// @param code The new code to set for the address.
/// @return The address that was modified.
function setCode(address self, bytes memory code) internal returns (address) {
vulcan.hevm.etch(self, code);
return self;
}
/// @dev Generates an array of addresses with a specific length.
/// @param length The amount of addresses to generate.
function createMany(uint256 length) internal returns (address[] memory) {
return accountsSafe.createMany(length);
}
}