@aztec/aztec.js 0.1.0-alpha41 → 0.1.0-alpha42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tsbuildinfo +1 -1
- package/dest/main.js +1 -1
- package/dest/utils/abi_types.d.ts +2 -1
- package/dest/utils/abi_types.d.ts.map +1 -1
- package/dest/utils/cheat_codes.d.ts +191 -0
- package/dest/utils/cheat_codes.d.ts.map +1 -0
- package/dest/utils/cheat_codes.js +270 -0
- package/dest/utils/index.d.ts +1 -0
- package/dest/utils/index.d.ts.map +1 -1
- package/dest/utils/index.js +2 -1
- package/package.json +4 -4
- package/src/utils/abi_types.ts +1 -1
- package/src/utils/cheat_codes.ts +284 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { AztecAddress, CircuitsWasm, EthAddress, Fr } from '@aztec/circuits.js';
|
|
2
|
+
import { pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg';
|
|
3
|
+
import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer';
|
|
4
|
+
import { keccak } from '@aztec/foundation/crypto';
|
|
5
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
6
|
+
import { AztecRPC } from '@aztec/types';
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
|
|
10
|
+
const toFr = (value: Fr | bigint): Fr => {
|
|
11
|
+
return typeof value === 'bigint' ? new Fr(value) : value;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A class that provides utility functions for interacting with the chain.
|
|
16
|
+
*/
|
|
17
|
+
export class CheatCodes {
|
|
18
|
+
constructor(
|
|
19
|
+
/**
|
|
20
|
+
* The L1 cheat codes.
|
|
21
|
+
*/
|
|
22
|
+
public l1: L1CheatCodes,
|
|
23
|
+
/**
|
|
24
|
+
* The L2 cheat codes.
|
|
25
|
+
*/
|
|
26
|
+
public l2: L2CheatCodes,
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
static async create(rpcUrl: string, aztecRpc: AztecRPC): Promise<CheatCodes> {
|
|
30
|
+
const l1CheatCodes = new L1CheatCodes(rpcUrl);
|
|
31
|
+
const l2CheatCodes = new L2CheatCodes(aztecRpc, await CircuitsWasm.get(), l1CheatCodes);
|
|
32
|
+
return new CheatCodes(l1CheatCodes, l2CheatCodes);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A class that provides utility functions for interacting with the L1 chain.
|
|
38
|
+
*/
|
|
39
|
+
export class L1CheatCodes {
|
|
40
|
+
constructor(
|
|
41
|
+
/**
|
|
42
|
+
* The RPC client to use for interacting with the chain
|
|
43
|
+
*/
|
|
44
|
+
public rpcUrl: string,
|
|
45
|
+
/**
|
|
46
|
+
* The logger to use for the l1 cheatcodes
|
|
47
|
+
*/
|
|
48
|
+
public logger = createDebugLogger('aztec:cheat_codes:l1'),
|
|
49
|
+
) {}
|
|
50
|
+
|
|
51
|
+
async rpcCall(method: string, params: any[]) {
|
|
52
|
+
const paramsString = JSON.stringify(params);
|
|
53
|
+
const content = {
|
|
54
|
+
body: `{"jsonrpc":"2.0", "method": "${method}", "params": ${paramsString}, "id": 1}`,
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
};
|
|
58
|
+
return await (await fetch(this.rpcUrl, content)).json();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get the current blocknumber
|
|
63
|
+
* @returns The current block number
|
|
64
|
+
*/
|
|
65
|
+
public async blockNumber(): Promise<number> {
|
|
66
|
+
const res = await this.rpcCall('eth_blockNumber', []);
|
|
67
|
+
return parseInt(res.result, 16);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get the current chainId
|
|
72
|
+
* @returns The current chainId
|
|
73
|
+
*/
|
|
74
|
+
public async chainId(): Promise<number> {
|
|
75
|
+
const res = await this.rpcCall('eth_chainId', []);
|
|
76
|
+
return parseInt(res.result, 16);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the current timestamp
|
|
81
|
+
* @returns The current timestamp
|
|
82
|
+
*/
|
|
83
|
+
public async timestamp(): Promise<number> {
|
|
84
|
+
const res = await this.rpcCall('eth_getBlockByNumber', ['latest', true]);
|
|
85
|
+
return parseInt(res.result.timestamp, 16);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Advance the chain by a number of blocks
|
|
90
|
+
* @param numberOfBlocks - The number of blocks to mine
|
|
91
|
+
* @returns The current chainId
|
|
92
|
+
*/
|
|
93
|
+
public async mine(numberOfBlocks = 1): Promise<void> {
|
|
94
|
+
const res = await this.rpcCall('anvil_mine', [numberOfBlocks]);
|
|
95
|
+
if (res.error) throw new Error(`Error mining: ${res.error.message}`);
|
|
96
|
+
this.logger(`Mined ${numberOfBlocks} blocks`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set the next block timestamp
|
|
101
|
+
* @param timestamp - The timestamp to set the next block to
|
|
102
|
+
*/
|
|
103
|
+
public async setNextBlockTimestamp(timestamp: number): Promise<void> {
|
|
104
|
+
const res = await this.rpcCall('anvil_setNextBlockTimestamp', [timestamp]);
|
|
105
|
+
if (res.error) throw new Error(`Error setting next block timestamp: ${res.error.message}`);
|
|
106
|
+
this.logger(`Set next block timestamp to ${timestamp}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Dumps the current chain state to a file.
|
|
111
|
+
* @param fileName - The file name to dump state into
|
|
112
|
+
*/
|
|
113
|
+
public async dumpChainState(fileName: string): Promise<void> {
|
|
114
|
+
const res = await this.rpcCall('anvil_dumpState', []);
|
|
115
|
+
if (res.error) throw new Error(`Error dumping state: ${res.error.message}`);
|
|
116
|
+
const jsonContent = JSON.stringify(res.result);
|
|
117
|
+
fs.writeFileSync(`${fileName}.json`, jsonContent, 'utf8');
|
|
118
|
+
this.logger(`Dumped state to ${fileName}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Loads the chain state from a file.
|
|
123
|
+
* @param fileName - The file name to load state from
|
|
124
|
+
*/
|
|
125
|
+
public async loadChainState(fileName: string): Promise<void> {
|
|
126
|
+
const data = JSON.parse(fs.readFileSync(`${fileName}.json`, 'utf8'));
|
|
127
|
+
const res = await this.rpcCall('anvil_loadState', [data]);
|
|
128
|
+
if (res.error) throw new Error(`Error loading state: ${res.error.message}`);
|
|
129
|
+
this.logger(`Loaded state from ${fileName}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Load the value at a storage slot of a contract address on L1
|
|
134
|
+
* @param contract - The contract address
|
|
135
|
+
* @param slot - The storage slot
|
|
136
|
+
* @returns - The value at the storage slot
|
|
137
|
+
*/
|
|
138
|
+
public async load(contract: EthAddress, slot: bigint): Promise<bigint> {
|
|
139
|
+
const res = await this.rpcCall('eth_getStorageAt', [contract.toString(), toHex(slot), 'latest']);
|
|
140
|
+
return BigInt(res.result);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Set the value at a storage slot of a contract address on L1
|
|
145
|
+
* @param contract - The contract address
|
|
146
|
+
* @param slot - The storage slot
|
|
147
|
+
* @param value - The value to set the storage slot to
|
|
148
|
+
*/
|
|
149
|
+
public async store(contract: EthAddress, slot: bigint, value: bigint): Promise<void> {
|
|
150
|
+
// for the rpc call, we need to change value to be a 32 byte hex string.
|
|
151
|
+
const res = await this.rpcCall('anvil_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]);
|
|
152
|
+
if (res.error) throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${res.error.message}`);
|
|
153
|
+
this.logger(`Set storage for contract ${contract} at ${slot} to ${value}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Computes the slot value for a given map and key.
|
|
158
|
+
* @param baseSlot - The base slot of the map (specified in noir contract)
|
|
159
|
+
* @param key - The key to lookup in the map
|
|
160
|
+
* @returns The storage slot of the value in the map
|
|
161
|
+
*/
|
|
162
|
+
public keccak256(baseSlot: bigint, key: bigint): bigint {
|
|
163
|
+
// abi encode (removing the 0x) - concat key and baseSlot (both padded to 32 bytes)
|
|
164
|
+
const abiEncoded = toHex(key, true).substring(2) + toHex(baseSlot, true).substring(2);
|
|
165
|
+
return toBigIntBE(keccak(Buffer.from(abiEncoded, 'hex')));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Send transactions impersonating an externally owned account or contract.
|
|
170
|
+
* @param who - The address to impersonate
|
|
171
|
+
*/
|
|
172
|
+
public async startPrank(who: EthAddress): Promise<void> {
|
|
173
|
+
const res = await this.rpcCall('anvil_impersonateAccount', [who.toString()]);
|
|
174
|
+
if (res.error) throw new Error(`Error pranking ${who}: ${res.error.message}`);
|
|
175
|
+
this.logger(`Impersonating ${who}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Stop impersonating an account that you are currently impersonating.
|
|
180
|
+
* @param who - The address to stop impersonating
|
|
181
|
+
*/
|
|
182
|
+
public async stopPrank(who: EthAddress): Promise<void> {
|
|
183
|
+
const res = await this.rpcCall('anvil_stopImpersonatingAccount', [who.toString()]);
|
|
184
|
+
if (res.error) throw new Error(`Error pranking ${who}: ${res.error.message}`);
|
|
185
|
+
this.logger(`Stopped impersonating ${who}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Set the bytecode for a contract
|
|
190
|
+
* @param contract - The contract address
|
|
191
|
+
* @param bytecode - The bytecode to set
|
|
192
|
+
*/
|
|
193
|
+
public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise<void> {
|
|
194
|
+
const res = await this.rpcCall('anvil_setCode', [contract.toString(), bytecode]);
|
|
195
|
+
if (res.error) throw new Error(`Error setting bytecode for ${contract}: ${res.error.message}`);
|
|
196
|
+
this.logger(`Set bytecode for ${contract} to ${bytecode}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get the bytecode for a contract
|
|
201
|
+
* @param contract - The contract address
|
|
202
|
+
* @returns The bytecode for the contract
|
|
203
|
+
*/
|
|
204
|
+
public async getBytecode(contract: EthAddress): Promise<`0x${string}`> {
|
|
205
|
+
const res = await this.rpcCall('eth_getCode', [contract.toString(), 'latest']);
|
|
206
|
+
return res.result;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* A class that provides utility functions for interacting with the L2 chain.
|
|
212
|
+
*/
|
|
213
|
+
export class L2CheatCodes {
|
|
214
|
+
constructor(
|
|
215
|
+
/**
|
|
216
|
+
* The RPC client to use for interacting with the chain
|
|
217
|
+
*/
|
|
218
|
+
public aztecRpc: AztecRPC,
|
|
219
|
+
/**
|
|
220
|
+
* The circuits wasm module used for pedersen hashing
|
|
221
|
+
*/
|
|
222
|
+
public wasm: CircuitsWasm,
|
|
223
|
+
/**
|
|
224
|
+
* The L1 cheat codes.
|
|
225
|
+
*/
|
|
226
|
+
public l1: L1CheatCodes,
|
|
227
|
+
/**
|
|
228
|
+
* The logger to use for the l2 cheatcodes
|
|
229
|
+
*/
|
|
230
|
+
public logger = createDebugLogger('aztec:cheat_codes:l2'),
|
|
231
|
+
) {}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Computes the slot value for a given map and key.
|
|
235
|
+
* @param baseSlot - The base slot of the map (specified in noir contract)
|
|
236
|
+
* @param key - The key to lookup in the map
|
|
237
|
+
* @returns The storage slot of the value in the map
|
|
238
|
+
*/
|
|
239
|
+
public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint): Fr {
|
|
240
|
+
// Based on `at` function in
|
|
241
|
+
// aztec3-packages/yarn-project/noir-contracts/src/contracts/noir-aztec/src/state_vars/map.nr
|
|
242
|
+
return Fr.fromBuffer(
|
|
243
|
+
pedersenPlookupCommitInputs(
|
|
244
|
+
this.wasm,
|
|
245
|
+
[toFr(baseSlot), toFr(key)].map(f => f.toBuffer()),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get the current blocknumber
|
|
252
|
+
* @returns The current block number
|
|
253
|
+
*/
|
|
254
|
+
public async blockNumber(): Promise<number> {
|
|
255
|
+
return await this.aztecRpc.getBlockNum();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Set time of the next execution on L2.
|
|
260
|
+
* It also modifies time on L1 for next execution and stores this time as the last rollup block on the rollup contract.
|
|
261
|
+
* @param to - The timestamp to set the next block to (must be greater than current time)
|
|
262
|
+
*/
|
|
263
|
+
public async warp(to: number): Promise<void> {
|
|
264
|
+
const rollupContract = (await this.aztecRpc.getNodeInfo()).rollupAddress;
|
|
265
|
+
await this.l1.setNextBlockTimestamp(to);
|
|
266
|
+
// also store this time on the rollup contract (slot 1 tracks `lastBlockTs`).
|
|
267
|
+
// This is because when the sequencer executes public functions, it uses the timestamp stored in the rollup contract.
|
|
268
|
+
await this.l1.store(rollupContract, 1n, BigInt(to));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Loads the value stored at the given slot in the public storage of the given contract.
|
|
273
|
+
* @param who - The address of the contract
|
|
274
|
+
* @param slot - The storage slot to lookup
|
|
275
|
+
* @returns The value stored at the given slot
|
|
276
|
+
*/
|
|
277
|
+
public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise<Fr> {
|
|
278
|
+
const storageValue = await this.aztecRpc.getPublicStorageAt(who, toFr(slot));
|
|
279
|
+
if (storageValue === undefined) {
|
|
280
|
+
throw new Error(`Storage slot ${slot} not found`);
|
|
281
|
+
}
|
|
282
|
+
return Fr.fromBuffer(storageValue);
|
|
283
|
+
}
|
|
284
|
+
}
|
package/src/utils/index.ts
CHANGED