@aztec/cli 0.1.0-alpha21 → 0.1.0-alpha23

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/src/index.ts CHANGED
@@ -1,23 +1,27 @@
1
1
  #!/usr/bin/env -S node --no-warnings
2
2
  import { Command } from 'commander';
3
+ import { mnemonicToAccount } from 'viem/accounts';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
5
  import { createDebugLogger } from '@aztec/foundation/log';
5
6
  import {
6
7
  AztecAddress,
7
8
  Contract,
8
9
  ContractDeployer,
10
+ Fr,
9
11
  Point,
10
- TxHash,
11
12
  createAccounts,
12
13
  createAztecRpcClient,
13
- pointToPublicKey,
14
+ getAccountWallet,
14
15
  } from '@aztec/aztec.js';
16
+ import { StructType } from '@aztec/foundation/abi';
17
+ import { randomBytes } from '@aztec/foundation/crypto';
18
+ import { JsonStringify } from '@aztec/foundation/json-rpc';
19
+ import { ContractData, TxHash, L2BlockL2Logs } from '@aztec/types';
15
20
 
16
21
  import { encodeArgs, parseStructString } from './cli_encoder.js';
17
- import { deployAztecContracts, getContractAbi, prepTx } from './utils.js';
18
- import { JsonStringify } from '@aztec/foundation/json-rpc';
19
- import { StructType } from '@aztec/foundation/abi';
20
- import { ContractData, L2BlockL2Logs } from '@aztec/types';
22
+ import { deployAztecContracts, getContractAbi, getTxSender, prepTx } from './utils.js';
23
+
24
+ const accountCreationSalt = Fr.ZERO;
21
25
 
22
26
  const debugLogger = createDebugLogger('aztec:cli');
23
27
  const log = createLogger();
@@ -26,65 +30,127 @@ const program = new Command();
26
30
 
27
31
  program.name('azti').description('CLI for interacting with Aztec.').version('0.1.0');
28
32
 
33
+ const { ETHEREUM_HOST, AZTEC_RPC_HOST, PRIVATE_KEY, PUBLIC_KEY, API_KEY } = process.env;
34
+
29
35
  /**
30
- * A placeholder for the Aztec-cli.
36
+ * Main function for the Aztec CLI.
31
37
  */
32
38
  async function main() {
33
39
  program
34
40
  .command('deploy-l1-contracts')
41
+ .description('Deploys all necessary Ethereum contracts for Aztec.')
35
42
  .argument(
36
43
  '[rpcUrl]',
37
44
  'Url of the ethereum host. Chain identifiers localhost and testnet can be used',
38
- 'http://localhost:8545',
45
+ ETHEREUM_HOST || 'http://localhost:8545',
39
46
  )
40
- .option('-a, --api-key <string>', 'Api key for the ethereum host', undefined)
41
- .option('-p, --private-key <string>', 'The private key to use for deployment')
47
+ .option('-a, --api-key <string>', 'Api key for the ethereum host', API_KEY)
48
+ .option('-p, --private-key <string>', 'The private key to use for deployment', PRIVATE_KEY)
42
49
  .option(
43
50
  '-m, --mnemonic <string>',
44
51
  'The mnemonic to use in deployment',
45
52
  'test test test test test test test test test test test junk',
46
53
  )
47
54
  .action(async (rpcUrl: string, options) => {
48
- await deployAztecContracts(rpcUrl, options.apiKey ?? '', options.privateKey, options.mnemonic, debugLogger);
55
+ const { rollupAddress, registryAddress, inboxAddress, outboxAddress, contractDeploymentEmitterAddress } =
56
+ await deployAztecContracts(rpcUrl, options.apiKey ?? '', options.privateKey, options.mnemonic, debugLogger);
57
+ log('\n');
58
+ log(`Rollup Address: ${rollupAddress.toString()}`);
59
+ log(`Registry Address: ${registryAddress.toString()}`);
60
+ log(`L1 -> L2 Inbox Address: ${inboxAddress.toString()}`);
61
+ log(`L2 -> L1 Outbox address: ${outboxAddress.toString()}`);
62
+ log(`Contract Deployment Emitter Address: ${contractDeploymentEmitterAddress.toString()}`);
63
+ log('\n');
64
+ });
65
+
66
+ program
67
+ .command('create-private-key')
68
+ .description('Generates a 32-byte private key.')
69
+ .option('-m, --mnemonic', 'A mnemonic string that can be used for the private key generation.')
70
+ .action(options => {
71
+ let privKey;
72
+ if (options.mnemonic) {
73
+ const acc = mnemonicToAccount(options.mnemonic);
74
+ privKey = Buffer.from(acc.getHdKey().privateKey!).toString('hex');
75
+ } else {
76
+ privKey = randomBytes(32).toString('hex');
77
+ }
78
+ log(`\n${privKey}\n`);
79
+ });
80
+
81
+ program
82
+ .command('create-account')
83
+ .description('Creates an aztec account that can be used for transactions.')
84
+ .option(
85
+ '-k, --private-key <string>',
86
+ 'Private Key to use for the 1st account generation. Uses random by default.',
87
+ PRIVATE_KEY,
88
+ )
89
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
90
+ .action(async options => {
91
+ const client = createAztecRpcClient(options.rpcUrl);
92
+ const privateKey = options.privateKey && Buffer.from(options.privateKey.replace(/^0x/i, ''), 'hex');
93
+ const wallet = await createAccounts(client, privateKey, accountCreationSalt, 1);
94
+ const accounts = await wallet.getAccounts();
95
+ const pubKeys = await Promise.all(accounts.map(acc => wallet.getAccountPublicKey(acc)));
96
+ log(`\nCreated account(s).`);
97
+ accounts.map((acc, i) => log(`\nAddress: ${acc.toString()}\nPublic Key: ${pubKeys[i].toString()}\n`));
49
98
  });
50
99
 
51
100
  program
52
101
  .command('deploy')
102
+ .description('Deploys a compiled Noir contract to Aztec.')
53
103
  .argument('<contractAbi>', "A compiled Noir contract's ABI in JSON format", undefined)
54
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
55
- .option('-k, --public-key <string>')
56
- .option('-a, --constructor-args [args...]', 'Contract constructor arguments', [])
57
- .action(async (contractFile: string, options: any) => {
104
+ .argument('[constructorArgs...]', 'Contract constructor arguments', [])
105
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
106
+ .option(
107
+ '-k, --public-key <string>',
108
+ 'Public key of the deployer. If not provided, it will check the RPC for existing ones.',
109
+ PUBLIC_KEY,
110
+ )
111
+ .action(async (contractFile: string, args: string[], options: any) => {
58
112
  const contractAbi = getContractAbi(contractFile, log);
59
113
  const constructorAbi = contractAbi.functions.find(({ name }) => name === 'constructor');
60
114
 
61
- const publicKey = Point.fromString(options.publicKey);
62
115
  const client = createAztecRpcClient(options.rpcUrl);
116
+ let publicKey;
117
+ if (options.publicKey) {
118
+ publicKey = Point.fromString(options.publicKey);
119
+ } else {
120
+ const accounts = await client.getAccounts();
121
+ if (!accounts) {
122
+ throw new Error('No public key provided or found in Aztec RPC.');
123
+ }
124
+ publicKey = await client.getAccountPublicKey(accounts[0]);
125
+ }
126
+
127
+ log(`Using Public Key: ${publicKey.toString()}`);
128
+
63
129
  const deployer = new ContractDeployer(contractAbi, client);
64
130
 
65
- const tx = deployer
66
- .deploy(...encodeArgs(options.constructorArgs, constructorAbi!.parameters), pointToPublicKey(publicKey))
67
- .send();
131
+ const tx = deployer.deploy(...encodeArgs(args, constructorAbi!.parameters), publicKey.toBigInts()).send();
68
132
  await tx.isMined();
69
133
  const receipt = await tx.getReceipt();
70
- log(`Contract deployed at ${receipt.contractAddress?.toString()}`);
134
+ log(`\nAztec Contract deployed at ${receipt.contractAddress?.toString()}\n`);
71
135
  });
72
136
 
73
137
  program
74
138
  .command('check-deploy')
139
+ .description('Checks if a contract is deployed to the specified Aztec address.')
75
140
  .argument('<contractAddress>', 'An Aztec address to check if contract has been deployed to.')
76
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
141
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
77
142
  .action(async (_contractAddress, options) => {
78
143
  const client = createAztecRpcClient(options.rpcUrl);
79
144
  const address = AztecAddress.fromString(_contractAddress);
80
145
  const isDeployed = await client.isContractDeployed(address);
81
- log(isDeployed.toString());
146
+ log(`\n${isDeployed.toString()}\n`);
82
147
  });
83
148
 
84
149
  program
85
150
  .command('get-tx-receipt')
151
+ .description('Gets the receipt for the specified transaction hash.')
86
152
  .argument('<txHash>', 'A TX hash to get the receipt for.')
87
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
153
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
88
154
  .action(async (_txHash, options) => {
89
155
  const client = createAztecRpcClient(options.rpcUrl);
90
156
  const txHash = TxHash.fromString(_txHash);
@@ -92,14 +158,15 @@ async function main() {
92
158
  if (!receipt) {
93
159
  log(`No receipt found for tx hash ${_txHash}`);
94
160
  } else {
95
- log(`TX Receipt: \n${JsonStringify(receipt, true)}`);
161
+ log(`\nTX Receipt: \n${JsonStringify(receipt, true)}\n`);
96
162
  }
97
163
  });
98
164
 
99
165
  program
100
166
  .command('get-contract-data')
167
+ .description('Gets information about the Aztec contract deployed at the specified address.')
101
168
  .argument('<contractAddress>', 'Aztec address of the contract.')
102
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
169
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
103
170
  .option('-b, --include-bytecode', "Include the contract's public function bytecode, if any.")
104
171
  .action(async (_contractAddress, options) => {
105
172
  const client = createAztecRpcClient(options.rpcUrl);
@@ -119,18 +186,20 @@ async function main() {
119
186
  } else {
120
187
  contractData = contractDataOrInfo;
121
188
  }
122
- log(`Contract Data: \nAddress: ${contractData.contractAddress.toString()}`);
189
+ log(`\nContract Data: \nAddress: ${contractData.contractAddress.toString()}`);
123
190
  log(`Portal: ${contractData.portalContractAddress.toString()}`);
124
191
  if ('bytecode' in contractDataOrInfo) {
125
192
  log(`Bytecode: ${contractDataOrInfo.bytecode}`);
126
193
  }
194
+ log('\n');
127
195
  });
128
196
 
129
197
  program
130
198
  .command('get-logs')
199
+ .description('Gets all the unencrypted logs from L2 blocks in the range specified.')
131
200
  .argument('<from>', 'Block num start for getting logs.')
132
201
  .argument('<take>', 'How many block logs to fetch.')
133
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
202
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
134
203
  .action(async (_from, _take, options) => {
135
204
  let from: number;
136
205
  let take: number;
@@ -151,26 +220,9 @@ async function main() {
151
220
  }
152
221
  });
153
222
 
154
- // NOTE: This implementation should change soon but keeping it here for quick account creation.
155
- program
156
- .command('create-account')
157
- .option('-k, --private-key', 'Private Key to use for the 1st account generation.')
158
- .option('-n, --num-addresses <number>', 'Number of addresses the account can control')
159
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
160
- .action(async options => {
161
- const client = createAztecRpcClient(options.rpcUrl);
162
- const privateKey = options.privateKey && Buffer.from(options.privateKeystr.replace(/^0x/i, ''), 'hex');
163
- const numAccounts = options.numAddresses ? parseInt(options.numAddresses) : 1;
164
- const wallet = await createAccounts(client, privateKey, numAccounts);
165
- const accounts = await wallet.getAccounts();
166
- const pubKeys = await Promise.all(accounts.map(acc => wallet.getAccountPublicKey(acc)));
167
- log(`Created account(s).`);
168
- accounts.map((acc, i) => log(`\nAddress: ${acc.toString()}\nPublic Key: ${pubKeys[i].toString()}\n`));
169
- });
170
-
171
223
  program
172
224
  .command('get-accounts')
173
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
225
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
174
226
  .action(async (options: any) => {
175
227
  const client = createAztecRpcClient(options.rpcUrl);
176
228
  const accounts = await client.getAccounts();
@@ -178,14 +230,15 @@ async function main() {
178
230
  log('No accounts found.');
179
231
  } else {
180
232
  log(`Accounts found: \n`);
181
- accounts.forEach(acc => log(`${acc}\n`));
233
+ accounts.forEach(async acc => log(`Address: ${acc}\nPublic Key: ${await client.getAccountPublicKey(acc)}\n`));
182
234
  }
183
235
  });
184
236
 
185
237
  program
186
238
  .command('get-account-public-key')
239
+ .description("Gets an account's public key, given its Aztec address.")
187
240
  .argument('<address>', 'The Aztec address to get the public key for')
188
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
241
+ .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
189
242
  .action(async (_address, options) => {
190
243
  const client = createAztecRpcClient(options.rpcUrl);
191
244
  const address = AztecAddress.fromString(_address);
@@ -199,59 +252,68 @@ async function main() {
199
252
 
200
253
  program
201
254
  .command('call-fn')
255
+ .description('Calls a function on an Aztec contract.')
202
256
  .argument('<contractAbi>', "The compiled contract's ABI in JSON format", undefined)
203
257
  .argument('<contractAddress>', 'Address of the contract')
204
258
  .argument('<functionName>', 'Name of Function to view')
205
- .argument('[from]', 'The caller of the transaction', undefined)
206
259
  .argument('[functionArgs...]', 'Function arguments', [])
207
- .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
208
- .action(async (contractFile, _contractAddress, functionName, _from, _functionArgs, options) => {
209
- const { contractAddress, functionArgs, from, contractAbi } = prepTx(
260
+ .option('-k, --private-key <string>', "The sender's private key.", PRIVATE_KEY)
261
+ .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
262
+
263
+ .action(async (contractFile, _contractAddress, functionName, _functionArgs, options) => {
264
+ const { contractAddress, functionArgs, contractAbi } = prepTx(
210
265
  contractFile,
211
266
  _contractAddress,
212
267
  functionName,
213
- _from,
214
268
  _functionArgs,
215
269
  log,
216
270
  );
271
+
217
272
  const client = createAztecRpcClient(options.rpcUrl);
218
- const wallet = await createAccounts(client);
273
+ const wallet = await getAccountWallet(client, Buffer.from(options.privateKey, 'hex'), accountCreationSalt);
219
274
  const contract = new Contract(contractAddress, contractAbi, wallet);
220
- const tx = contract.methods[functionName](...functionArgs).send({ from });
275
+ const origin = (await wallet.getAccounts()).find(addr => addr.equals(wallet.getAddress()));
276
+ const tx = contract.methods[functionName](...functionArgs).send({
277
+ origin,
278
+ });
221
279
  await tx.isMined();
222
- log('TX has been mined');
280
+ log('\nTX has been mined');
223
281
  const receipt = await tx.getReceipt();
224
282
  log(`TX Hash: ${(await tx.getTxHash()).toString()}`);
225
283
  log(`Block Num: ${receipt.blockNumber}`);
226
284
  log(`Block Hash: ${receipt.blockHash?.toString('hex')}`);
227
- log(`TX Status: ${receipt.status}`);
285
+ log(`TX Status: ${receipt.status}\n`);
228
286
  });
229
287
 
230
288
  program
231
- .command('view-tx')
289
+ .command('view-fn')
290
+ .description(
291
+ 'Simulates the execution of a view (read-only) function on a deployed contract, without modifying state.',
292
+ )
232
293
  .argument('<contractAbi>', "The compiled contract's ABI in JSON format", undefined)
233
294
  .argument('<contractAddress>', 'Address of the contract')
234
295
  .argument('<functionName>', 'Name of Function to view')
235
- .argument('[from]', 'The caller of the transaction', undefined)
236
296
  .argument('[functionArgs...]', 'Function arguments', [])
237
- .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
238
- .action(async (contractFile, _contractAddress, functionName, _from, _functionArgs, options) => {
239
- const { contractAddress, functionArgs, from } = prepTx(
297
+ .option('-f, --from <string>', 'Public key of the TX viewer. If empty, will try to find account in RPC.')
298
+ .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
299
+ .action(async (contractFile, _contractAddress, functionName, _functionArgs, options) => {
300
+ const { contractAddress, functionArgs } = prepTx(
240
301
  contractFile,
241
302
  _contractAddress,
242
303
  functionName,
243
- _from,
244
304
  _functionArgs,
245
305
  log,
246
306
  );
247
307
  const client = createAztecRpcClient(options.rpcUrl);
308
+ const from = await getTxSender(client, options.from);
248
309
  const result = await client.viewTx(functionName, functionArgs, contractAddress, from);
249
- log('View TX returned result: ', JsonStringify(result, true));
310
+ log('\nView TX result: ', JsonStringify(result, true), '\n');
250
311
  });
251
312
 
252
313
  // Helper for users to decode hex strings into structs if needed
253
314
  program
254
315
  .command('parse-parameter-struct')
316
+ .description("Helper for parsing an encoded string into a contract's parameter struct.")
255
317
  .argument('<encodedString>', 'The encoded hex string')
256
318
  .argument('<contractAbi>', "The compiled contract's ABI in JSON format")
257
319
  .argument('<parameterName>', 'The name of the struct parameter to decode into')
@@ -266,16 +328,17 @@ async function main() {
266
328
  return;
267
329
  }
268
330
  const data = parseStructString(encodedString, parameterAbitype.type as StructType);
269
- log(`Struct Data: \n${JsonStringify(data, true)}`);
331
+ log(`\nStruct Data: \n${JsonStringify(data, true)}\n`);
270
332
  });
271
333
 
272
334
  program
273
335
  .command('block-num')
274
- .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', 'http://localhost:8080')
336
+ .description('Gets the current Aztec L2 number.')
337
+ .option('-u, --rpcUrl <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
275
338
  .action(async (options: any) => {
276
339
  const client = createAztecRpcClient(options.rpcUrl);
277
340
  const num = await client.getBlockNum();
278
- log(num);
341
+ log(`${num}\n`);
279
342
  });
280
343
 
281
344
  await program.parseAsync(process.argv);
package/src/utils.ts CHANGED
@@ -3,7 +3,7 @@ import fs from 'fs';
3
3
  import { createEthereumChain, deployL1Contracts } from '@aztec/ethereum';
4
4
  import { DebugLogger, Logger } from '@aztec/foundation/log';
5
5
  import { ContractAbi } from '@aztec/foundation/abi';
6
- import { AztecAddress } from '@aztec/aztec.js';
6
+ import { AztecAddress, AztecRPC } from '@aztec/aztec.js';
7
7
  import { encodeArgs } from './cli_encoder.js';
8
8
 
9
9
  /**
@@ -22,7 +22,7 @@ export async function deployAztecContracts(
22
22
  ) {
23
23
  const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`0x${privateKey}`);
24
24
  const chain = createEthereumChain(rpcUrl, apiKey);
25
- await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
25
+ return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger);
26
26
  }
27
27
 
28
28
  /**
@@ -42,12 +42,36 @@ export function getContractAbi(fileDir: string, log: Logger) {
42
42
  return contractAbi;
43
43
  }
44
44
 
45
+ /**
46
+ * Utility to select a TX sender either from user input
47
+ * or from the first account that is found in an Aztec RPC instance.
48
+ * @param client - The Aztec RPC instance that will be checked for an account.
49
+ * @param _from - The user input.
50
+ * @returns An Aztec address. Will throw if one can't be found in either options.
51
+ */
52
+ export async function getTxSender(client: AztecRPC, _from?: string) {
53
+ let from: AztecAddress;
54
+ if (_from) {
55
+ try {
56
+ from = AztecAddress.fromString(_from);
57
+ } catch {
58
+ throw new Error(`Invalid option 'from' passed: ${_from}`);
59
+ }
60
+ } else {
61
+ const accounts = await client.getAccounts();
62
+ if (!accounts.length) {
63
+ throw new Error('No accounts found in Aztec RPC insance.');
64
+ }
65
+ from = accounts[0];
66
+ }
67
+ return from;
68
+ }
69
+
45
70
  /**
46
71
  * Performs necessary checks, conversions & operations to call a contract fn from the CLI.
47
72
  * @param contractFile - Directory of the compiled contract ABI.
48
73
  * @param _contractAddress - Aztec Address of the contract.
49
74
  * @param functionName - Name of the function to be called.
50
- * @param _from - The caller's address.
51
75
  * @param _functionArgs - Arguments to call the function with.
52
76
  * @param log - Logger instance that will output to the CLI
53
77
  * @returns Formatted contract address, function arguments and caller's aztec address.
@@ -56,7 +80,6 @@ export function prepTx(
56
80
  contractFile: string,
57
81
  _contractAddress: string,
58
82
  functionName: string,
59
- _from: string,
60
83
  _functionArgs: string[],
61
84
  log: Logger,
62
85
  ) {
@@ -73,14 +96,6 @@ export function prepTx(
73
96
  }
74
97
 
75
98
  const functionArgs = encodeArgs(_functionArgs, functionAbi.parameters);
76
- let from;
77
- if (_from) {
78
- try {
79
- from = AztecAddress.fromString(_from);
80
- } catch {
81
- throw new Error(`Unable to parse caller address ${_from}.`);
82
- }
83
- }
84
99
 
85
- return { contractAddress, functionArgs, from, contractAbi };
100
+ return { contractAddress, functionArgs, contractAbi };
86
101
  }