@aztec/cli 0.7.10 → 0.8.4

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/client.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AztecRPC, createAztecRpcClient } from '@aztec/aztec.js';
1
+ import { PXE, createPXEClient } from '@aztec/aztec.js';
2
2
  import { DebugLogger } from '@aztec/foundation/log';
3
3
  import { fileURLToPath } from '@aztec/foundation/url';
4
4
 
@@ -7,51 +7,42 @@ import { dirname, resolve } from 'path';
7
7
  import { gtr, ltr, satisfies, valid } from 'semver';
8
8
 
9
9
  /**
10
- * Creates an Aztec RPC client with a given set of retries on non-server errors.
11
- * @param rpcUrl - URL of the RPC server.
12
- * @returns An RPC client.
13
- */
14
- export function createClient(rpcUrl: string) {
15
- return createAztecRpcClient(rpcUrl);
16
- }
17
-
18
- /**
19
- * Creates an Aztec RPC client with a given set of retries on non-server errors.
20
- * Checks that the RPC server matches the expected version, and warns if not.
21
- * @param rpcUrl - URL of the RPC server.
10
+ * Creates a PXE client with a given set of retries on non-server errors.
11
+ * Checks that PXE matches the expected version, and warns if not.
12
+ * @param rpcUrl - URL of the RPC server wrapping the PXE.
22
13
  * @param logger - Debug logger to warn version incompatibilities.
23
- * @returns An RPC client.
14
+ * @returns A PXE client.
24
15
  */
25
16
  export async function createCompatibleClient(rpcUrl: string, logger: DebugLogger) {
26
- const client = createClient(rpcUrl);
17
+ const pxe = createPXEClient(rpcUrl);
27
18
  const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../package.json');
28
19
  const packageJsonContents = JSON.parse(readFileSync(packageJsonPath).toString());
29
20
  const expectedVersionRange = packageJsonContents.version; // During sandbox, we'll expect exact matches
30
21
 
31
22
  try {
32
- await checkServerVersion(client, expectedVersionRange);
23
+ await checkServerVersion(pxe, expectedVersionRange);
33
24
  } catch (err) {
34
25
  if (err instanceof VersionMismatchError) {
35
- logger.debug(err.message);
26
+ logger.warn(err.message);
36
27
  } else {
37
28
  throw err;
38
29
  }
39
30
  }
40
31
 
41
- return client;
32
+ return pxe;
42
33
  }
43
34
 
44
35
  /** Mismatch between server and client versions. */
45
36
  class VersionMismatchError extends Error {}
46
37
 
47
38
  /**
48
- * Checks that the RPC server version matches the expected one by this CLI. Throws if not.
49
- * @param rpc - RPC server connection.
39
+ * Checks that Private eXecution Environment (PXE) version matches the expected one by this CLI. Throws if not.
40
+ * @param pxe - PXE client.
50
41
  * @param expectedVersionRange - Expected version by CLI.
51
42
  */
52
- export async function checkServerVersion(rpc: AztecRPC, expectedVersionRange: string) {
43
+ export async function checkServerVersion(pxe: PXE, expectedVersionRange: string) {
53
44
  const serverName = 'Aztec Sandbox';
54
- const { sandboxVersion } = await rpc.getNodeInfo();
45
+ const { sandboxVersion } = await pxe.getNodeInfo();
55
46
  if (!sandboxVersion) {
56
47
  throw new VersionMismatchError(`Couldn't determine ${serverName} version. You may run into issues.`);
57
48
  }
package/src/encoding.ts CHANGED
@@ -91,7 +91,9 @@ function encodeArg(arg: string, abiType: ABIType, name: string): any {
91
91
  export function encodeArgs(args: any[], params: ABIParameter[]) {
92
92
  if (args.length !== params.length) {
93
93
  throw new Error(
94
- `Invalid number of args provided. Expected: ${params.length}, received: ${args.length}\nReceived args: ${args}`,
94
+ `Invalid args provided.\nExpected args: [${params
95
+ .map(param => param.name + ': ' + param.type.kind)
96
+ .join(', ')}]\nReceived args: ${args.join(', ')}`,
95
97
  );
96
98
  }
97
99
  return args.map((arg: any, index) => {
package/src/index.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import {
2
- AztecAddress,
3
2
  Contract,
4
3
  ContractDeployer,
5
4
  Fr,
6
5
  GrumpkinScalar,
7
- Point,
6
+ NotePreimage,
8
7
  generatePublicKey,
9
8
  getSchnorrAccount,
10
9
  isContractDeployed,
@@ -14,12 +13,13 @@ import { JsonStringify } from '@aztec/foundation/json-rpc';
14
13
  import { DebugLogger, LogFn } from '@aztec/foundation/log';
15
14
  import { fileURLToPath } from '@aztec/foundation/url';
16
15
  import { compileContract, generateNoirInterface, generateTypescriptInterface } from '@aztec/noir-compiler/cli';
17
- import { CompleteAddress, ContractData, L2BlockL2Logs, TxHash } from '@aztec/types';
16
+ import { CompleteAddress, ContractData, L2BlockL2Logs } from '@aztec/types';
18
17
 
19
- import { Command } from 'commander';
18
+ import { createSecp256k1PeerId } from '@libp2p/peer-id-factory';
19
+ import { Command, Option } from 'commander';
20
20
  import { readFileSync } from 'fs';
21
- import startCase from 'lodash.startcase';
22
21
  import { dirname, resolve } from 'path';
22
+ import { format } from 'util';
23
23
  import { mnemonicToAccount } from 'viem/accounts';
24
24
 
25
25
  import { createCompatibleClient } from './client.js';
@@ -31,19 +31,20 @@ import {
31
31
  getContractAbi,
32
32
  getExampleContractArtifacts,
33
33
  getTxSender,
34
+ parseAztecAddress,
35
+ parseField,
36
+ parseFields,
37
+ parsePartialAddress,
38
+ parsePrivateKey,
39
+ parsePublicKey,
40
+ parseSaltFromHexString,
41
+ parseTxHash,
34
42
  prepTx,
35
43
  } from './utils.js';
36
44
 
37
45
  const accountCreationSalt = Fr.ZERO;
38
46
 
39
- const stripLeadingHex = (hex: string) => {
40
- if (hex.length > 2 && hex.startsWith('0x')) {
41
- return hex.substring(2);
42
- }
43
- return hex;
44
- };
45
-
46
- const { ETHEREUM_HOST, AZTEC_RPC_HOST, PRIVATE_KEY, API_KEY } = process.env;
47
+ const { ETHEREUM_HOST = 'http://localhost:8545', PRIVATE_KEY, API_KEY } = process.env;
47
48
 
48
49
  /**
49
50
  * Returns commander program that defines the CLI.
@@ -59,36 +60,46 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
59
60
 
60
61
  program.name('aztec-cli').description('CLI for interacting with Aztec.').version(version);
61
62
 
63
+ const pxeOption = new Option('-u, --rpc-url <string>', 'URL of the PXE')
64
+ .env('PXE_URL')
65
+ .default('http://localhost:8080')
66
+ .makeOptionMandatory(true);
67
+
68
+ const createPrivateKeyOption = (description: string, mandatory: boolean) =>
69
+ new Option('-k, --private-key <string>', description)
70
+ .env('PRIVATE_KEY')
71
+ .argParser(parsePrivateKey)
72
+ .makeOptionMandatory(mandatory);
73
+
62
74
  program
63
75
  .command('deploy-l1-contracts')
64
76
  .description('Deploys all necessary Ethereum contracts for Aztec.')
65
- .option(
77
+ .requiredOption(
66
78
  '-u, --rpc-url <string>',
67
79
  'Url of the ethereum host. Chain identifiers localhost and testnet can be used',
68
- ETHEREUM_HOST || 'http://localhost:8545',
80
+ ETHEREUM_HOST,
69
81
  )
70
82
  .option('-a, --api-key <string>', 'Api key for the ethereum host', API_KEY)
71
- .option('-p, --private-key <string>', 'The private key to use for deployment', PRIVATE_KEY)
83
+ .requiredOption('-p, --private-key <string>', 'The private key to use for deployment', PRIVATE_KEY)
72
84
  .option(
73
85
  '-m, --mnemonic <string>',
74
86
  'The mnemonic to use in deployment',
75
87
  'test test test test test test test test test test test junk',
76
88
  )
77
89
  .action(async options => {
78
- const { rollupAddress, registryAddress, inboxAddress, outboxAddress, contractDeploymentEmitterAddress } =
79
- await deployAztecContracts(
80
- options.rpcUrl,
81
- options.apiKey ?? '',
82
- options.privateKey,
83
- options.mnemonic,
84
- debugLogger,
85
- );
90
+ const { l1ContractAddresses } = await deployAztecContracts(
91
+ options.rpcUrl,
92
+ options.apiKey ?? '',
93
+ options.privateKey,
94
+ options.mnemonic,
95
+ debugLogger,
96
+ );
86
97
  log('\n');
87
- log(`Rollup Address: ${rollupAddress.toString()}`);
88
- log(`Registry Address: ${registryAddress.toString()}`);
89
- log(`L1 -> L2 Inbox Address: ${inboxAddress.toString()}`);
90
- log(`L2 -> L1 Outbox address: ${outboxAddress.toString()}`);
91
- log(`Contract Deployment Emitter Address: ${contractDeploymentEmitterAddress.toString()}`);
98
+ log(`Rollup Address: ${l1ContractAddresses.rollupAddress.toString()}`);
99
+ log(`Registry Address: ${l1ContractAddresses.registryAddress.toString()}`);
100
+ log(`L1 -> L2 Inbox Address: ${l1ContractAddresses.inboxAddress.toString()}`);
101
+ log(`L2 -> L1 Outbox address: ${l1ContractAddresses.outboxAddress.toString()}`);
102
+ log(`Contract Deployment Emitter Address: ${l1ContractAddresses.contractDeploymentEmitterAddress.toString()}`);
92
103
  log('\n');
93
104
  });
94
105
 
@@ -118,23 +129,30 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
118
129
  log(`\nPrivate Key: ${privKey}\nPublic Key: ${publicKey.toString()}\n`);
119
130
  });
120
131
 
132
+ program
133
+ .command('generate-p2p-private-key')
134
+ .summary('Generates a LibP2P peer private key.')
135
+ .description('Generates a private key that can be used for running a node on a LibP2P network.')
136
+ .action(async () => {
137
+ const peerId = await createSecp256k1PeerId();
138
+ const exportedPeerId = Buffer.from(peerId.privateKey!).toString('hex');
139
+ log(`Private key: ${exportedPeerId}`);
140
+ log(`Peer Id: ${peerId}`);
141
+ });
142
+
121
143
  program
122
144
  .command('create-account')
123
145
  .description(
124
- 'Creates an aztec account that can be used for sending transactions. Registers the account on the RPC server and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).',
146
+ 'Creates an aztec account that can be used for sending transactions. Registers the account on the PXE and deploys an account contract. Uses a Schnorr single-key account which uses the same key for encryption and authentication (not secure for production usage).',
125
147
  )
126
148
  .summary('Creates an aztec account that can be used for sending transactions.')
127
- .option(
128
- '-k, --private-key <string>',
129
- 'Private key for note encryption and transaction signing. Uses random by default.',
130
- PRIVATE_KEY,
149
+ .addOption(
150
+ createPrivateKeyOption('Private key for note encryption and transaction signing. Uses random by default.', false),
131
151
  )
132
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
152
+ .addOption(pxeOption)
133
153
  .action(async options => {
134
154
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
135
- const privateKey = options.privateKey
136
- ? GrumpkinScalar.fromString(stripLeadingHex(options.privateKey))
137
- : GrumpkinScalar.random();
155
+ const privateKey = options.privateKey ?? GrumpkinScalar.random();
138
156
 
139
157
  const account = getSchnorrAccount(client, privateKey, privateKey, accountCreationSalt);
140
158
  const wallet = await account.waitDeploy();
@@ -155,46 +173,57 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
155
173
  "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts",
156
174
  )
157
175
  .option('-a, --args <constructorArgs...>', 'Contract constructor arguments', [])
158
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
176
+ .addOption(pxeOption)
159
177
  .option(
160
178
  '-k, --public-key <string>',
161
179
  'Optional encryption public key for this address. Set this value only if this contract is expected to receive private notes, which will be encrypted using this public key.',
180
+ parsePublicKey,
162
181
  )
163
- .option('-s, --salt <string>', 'Optional deployment salt as a hex string for generating the deployment address.')
164
- .action(async (abiPath, options: any) => {
182
+ .option(
183
+ '-s, --salt <hex string>',
184
+ 'Optional deployment salt as a hex string for generating the deployment address.',
185
+ parseSaltFromHexString,
186
+ )
187
+ // `options.wait` is default true. Passing `--no-wait` will set it to false.
188
+ // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue
189
+ .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction')
190
+ .action(async (abiPath, { rpcUrl, publicKey, args: rawArgs, salt, wait }) => {
165
191
  const contractAbi = await getContractAbi(abiPath, log);
166
192
  const constructorAbi = contractAbi.functions.find(({ name }) => name === 'constructor');
167
193
 
168
- const client = await createCompatibleClient(options.rpcUrl, debugLogger);
169
- const publicKey = options.publicKey ? Point.fromString(options.publicKey) : undefined;
170
- const salt = options.salt ? Fr.fromBuffer(Buffer.from(stripLeadingHex(options.salt), 'hex')) : undefined;
194
+ const client = await createCompatibleClient(rpcUrl, debugLogger);
171
195
  const deployer = new ContractDeployer(contractAbi, client, publicKey);
172
196
 
173
197
  const constructor = getAbiFunction(contractAbi, 'constructor');
174
198
  if (!constructor) throw new Error(`Constructor not found in contract ABI`);
175
- if (constructor.parameters.length !== options.args.length) {
176
- throw new Error(
177
- `Invalid number of args passed (expected ${constructor.parameters.length} but got ${options.args.length})`,
178
- );
179
- }
180
199
 
181
- debugLogger(`Input arguments: ${options.args.map((x: any) => `"${x}"`).join(', ')}`);
182
- const args = encodeArgs(options.args, constructorAbi!.parameters);
200
+ debugLogger(`Input arguments: ${rawArgs.map((x: any) => `"${x}"`).join(', ')}`);
201
+ const args = encodeArgs(rawArgs, constructorAbi!.parameters);
183
202
  debugLogger(`Encoded arguments: ${args.join(', ')}`);
203
+
184
204
  const tx = deployer.deploy(...args).send({ contractAddressSalt: salt });
185
- debugLogger(`Deploy tx sent with hash ${await tx.getTxHash()}`);
186
- const deployed = await tx.wait();
187
- log(`\nContract deployed at ${deployed.contractAddress!.toString()}\n`);
205
+ const txHash = await tx.getTxHash();
206
+ debugLogger(`Deploy tx sent with hash ${txHash}`);
207
+ if (wait) {
208
+ const deployed = await tx.wait();
209
+ log(`\nContract deployed at ${deployed.contractAddress!.toString()}\n`);
210
+ } else {
211
+ log(`\nDeployment transaction hash: ${txHash}\n`);
212
+ }
188
213
  });
189
214
 
190
215
  program
191
216
  .command('check-deploy')
192
217
  .description('Checks if a contract is deployed to the specified Aztec address.')
193
- .requiredOption('-ca, --contract-address <address>', 'An Aztec address to check if contract has been deployed to.')
194
- .option('-u, --rpc-url <url>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
218
+ .requiredOption(
219
+ '-ca, --contract-address <address>',
220
+ 'An Aztec address to check if contract has been deployed to.',
221
+ parseAztecAddress,
222
+ )
223
+ .addOption(pxeOption)
195
224
  .action(async options => {
196
225
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
197
- const address = AztecAddress.fromString(options.contractAddress);
226
+ const address = options.contractAddress;
198
227
  const isDeployed = await isContractDeployed(client, address);
199
228
  if (isDeployed) log(`\nContract found at ${address.toString()}\n`);
200
229
  else log(`\nNo contract found at ${address.toString()}\n`);
@@ -203,14 +232,13 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
203
232
  program
204
233
  .command('get-tx-receipt')
205
234
  .description('Gets the receipt for the specified transaction hash.')
206
- .argument('<txHash>', 'A transaction hash to get the receipt for.')
207
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
208
- .action(async (_txHash, options) => {
235
+ .argument('<txHash>', 'A transaction hash to get the receipt for.', parseTxHash)
236
+ .addOption(pxeOption)
237
+ .action(async (txHash, options) => {
209
238
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
210
- const txHash = TxHash.fromString(_txHash);
211
239
  const receipt = await client.getTxReceipt(txHash);
212
240
  if (!receipt) {
213
- log(`No receipt found for transaction hash ${_txHash}`);
241
+ log(`No receipt found for transaction hash ${txHash.toString()}`);
214
242
  } else {
215
243
  log(`\nTransaction receipt: \n${JsonStringify(receipt, true)}\n`);
216
244
  }
@@ -219,15 +247,14 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
219
247
  program
220
248
  .command('get-contract-data')
221
249
  .description('Gets information about the Aztec contract deployed at the specified address.')
222
- .argument('<contractAddress>', 'Aztec address of the contract.')
223
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
250
+ .argument('<contractAddress>', 'Aztec address of the contract.', parseAztecAddress)
251
+ .addOption(pxeOption)
224
252
  .option('-b, --include-bytecode <boolean>', "Include the contract's public function bytecode, if any.", false)
225
253
  .action(async (contractAddress, options) => {
226
254
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
227
- const address = AztecAddress.fromString(contractAddress);
228
255
  const contractDataWithOrWithoutBytecode = options.includeBytecode
229
- ? await client.getExtendedContractData(address)
230
- : await client.getContractData(address);
256
+ ? await client.getExtendedContractData(contractAddress)
257
+ : await client.getContractData(contractAddress);
231
258
 
232
259
  if (!contractDataWithOrWithoutBytecode) {
233
260
  log(`No contract data found at ${contractAddress}`);
@@ -253,7 +280,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
253
280
  .description('Gets all the unencrypted logs from L2 blocks in the range specified.')
254
281
  .option('-f, --from <blockNum>', 'Initial block number for getting logs (defaults to 1).')
255
282
  .option('-l, --limit <blockCount>', 'How many blocks to fetch (defaults to 100).')
256
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
283
+ .addOption(pxeOption)
257
284
  .action(async options => {
258
285
  const { from, limit } = options;
259
286
  const fromBlock = from ? parseInt(from) : 1;
@@ -271,25 +298,25 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
271
298
 
272
299
  program
273
300
  .command('register-recipient')
274
- .description('Register a recipient in the Aztec RPC.')
275
- .requiredOption('-a, --address <aztecAddress>', "The account's Aztec address.")
276
- .requiredOption('-p, --public-key <publicKey>', 'The account public key.')
277
- .requiredOption('-pa, --partial-address <partialAddress', 'The partially computed address of the account contract.')
278
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
279
- .action(async options => {
280
- const client = await createCompatibleClient(options.rpcUrl, debugLogger);
281
- const address = AztecAddress.fromString(options.address);
282
- const publicKey = Point.fromString(options.publicKey);
283
- const partialAddress = Fr.fromString(options.partialAddress);
284
-
301
+ .description('Register a recipient in the PXE.')
302
+ .requiredOption('-a, --address <aztecAddress>', "The account's Aztec address.", parseAztecAddress)
303
+ .requiredOption('-p, --public-key <publicKey>', 'The account public key.', parsePublicKey)
304
+ .requiredOption(
305
+ '-pa, --partial-address <partialAddress>',
306
+ 'The partially computed address of the account contract.',
307
+ parsePartialAddress,
308
+ )
309
+ .addOption(pxeOption)
310
+ .action(async ({ address, publicKey, partialAddress, rpcUrl }) => {
311
+ const client = await createCompatibleClient(rpcUrl, debugLogger);
285
312
  await client.registerRecipient(await CompleteAddress.create(address, publicKey, partialAddress));
286
- log(`\nRegistered details for account with address: ${options.address}\n`);
313
+ log(`\nRegistered details for account with address: ${address}\n`);
287
314
  });
288
315
 
289
316
  program
290
317
  .command('get-accounts')
291
- .description('Gets all the Aztec accounts stored in the Aztec RPC.')
292
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
318
+ .description('Gets all the Aztec accounts stored in the PXE.')
319
+ .addOption(pxeOption)
293
320
  .action(async (options: any) => {
294
321
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
295
322
  const accounts = await client.getRegisteredAccounts();
@@ -306,15 +333,14 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
306
333
  program
307
334
  .command('get-account')
308
335
  .description('Gets an account given its Aztec address.')
309
- .argument('<address>', 'The Aztec address to get account for')
310
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
311
- .action(async (_address, options) => {
336
+ .argument('<address>', 'The Aztec address to get account for', parseAztecAddress)
337
+ .addOption(pxeOption)
338
+ .action(async (address, options) => {
312
339
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
313
- const address = AztecAddress.fromString(_address);
314
340
  const account = await client.getRegisteredAccount(address);
315
341
 
316
342
  if (!account) {
317
- log(`Unknown account ${_address}`);
343
+ log(`Unknown account ${address.toString()}`);
318
344
  } else {
319
345
  log(account.toReadableString());
320
346
  }
@@ -322,8 +348,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
322
348
 
323
349
  program
324
350
  .command('get-recipients')
325
- .description('Gets all the recipients stored in the Aztec RPC.')
326
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
351
+ .description('Gets all the recipients stored in the PXE.')
352
+ .addOption(pxeOption)
327
353
  .action(async (options: any) => {
328
354
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
329
355
  const recipients = await client.getRecipients();
@@ -340,15 +366,14 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
340
366
  program
341
367
  .command('get-recipient')
342
368
  .description('Gets a recipient given its Aztec address.')
343
- .argument('<address>', 'The Aztec address to get recipient for')
344
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
345
- .action(async (_address, options) => {
369
+ .argument('<address>', 'The Aztec address to get recipient for', parseAztecAddress)
370
+ .addOption(pxeOption)
371
+ .action(async (address, options) => {
346
372
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
347
- const address = AztecAddress.fromString(_address);
348
373
  const recipient = await client.getRecipient(address);
349
374
 
350
375
  if (!recipient) {
351
- log(`Unknown recipient ${_address}`);
376
+ log(`Unknown recipient ${address.toString()}`);
352
377
  } else {
353
378
  log(recipient.toReadableString());
354
379
  }
@@ -357,45 +382,37 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
357
382
  program
358
383
  .command('send')
359
384
  .description('Calls a function on an Aztec contract.')
360
- .argument('<functionName>', 'Name of Function to view')
385
+ .argument('<functionName>', 'Name of function to execute')
361
386
  .option('-a, --args [functionArgs...]', 'Function arguments', [])
362
387
  .requiredOption(
363
388
  '-c, --contract-abi <fileLocation>',
364
389
  "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts",
365
390
  )
366
- .requiredOption('-ca, --contract-address <address>', 'Aztec address of the contract.')
367
- .option('-k, --private-key <string>', "The sender's private key.", PRIVATE_KEY)
368
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
369
-
391
+ .requiredOption('-ca, --contract-address <address>', 'Aztec address of the contract.', parseAztecAddress)
392
+ .addOption(createPrivateKeyOption("The sender's private key.", true))
393
+ .addOption(pxeOption)
394
+ .option('--no-wait', 'Print transaction hash without waiting for it to be mined')
370
395
  .action(async (functionName, options) => {
371
- const { contractAddress, functionArgs, contractAbi } = await prepTx(
372
- options.contractAbi,
373
- options.contractAddress,
374
- functionName,
375
- options.args,
376
- log,
377
- );
378
-
379
- const fnAbi = getAbiFunction(contractAbi, functionName);
380
- if (fnAbi.parameters.length !== options.args.length) {
381
- throw Error(
382
- `Invalid number of args passed. Expected ${fnAbi.parameters.length}; Received: ${options.args.length}`,
383
- );
384
- }
385
-
386
- const privateKey = GrumpkinScalar.fromString(stripLeadingHex(options.privateKey));
396
+ const { functionArgs, contractAbi } = await prepTx(options.contractAbi, functionName, options.args, log);
397
+ const { contractAddress, privateKey } = options;
387
398
 
388
399
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
389
400
  const wallet = await getSchnorrAccount(client, privateKey, privateKey, accountCreationSalt).getWallet();
390
401
  const contract = await Contract.at(contractAddress, contractAbi, wallet);
391
402
  const tx = contract.methods[functionName](...functionArgs).send();
392
- await tx.wait();
393
- log('\nTransaction has been mined');
394
- const receipt = await tx.getReceipt();
395
403
  log(`Transaction hash: ${(await tx.getTxHash()).toString()}`);
396
- log(`Status: ${receipt.status}\n`);
397
- log(`Block number: ${receipt.blockNumber}`);
398
- log(`Block hash: ${receipt.blockHash?.toString('hex')}`);
404
+ if (options.wait) {
405
+ await tx.wait();
406
+
407
+ log('Transaction has been mined');
408
+
409
+ const receipt = await tx.getReceipt();
410
+ log(`Status: ${receipt.status}\n`);
411
+ log(`Block number: ${receipt.blockNumber}`);
412
+ log(`Block hash: ${receipt.blockHash?.toString('hex')}`);
413
+ } else {
414
+ log('\nTransaction pending. Check status with get-tx-receipt');
415
+ }
399
416
  });
400
417
 
401
418
  program
@@ -403,23 +420,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
403
420
  .description(
404
421
  'Simulates the execution of a view (read-only) function on a deployed contract, without modifying state.',
405
422
  )
406
- .argument('<functionName>', 'Name of Function to view')
423
+ .argument('<functionName>', 'Name of function to call')
407
424
  .option('-a, --args [functionArgs...]', 'Function arguments', [])
408
425
  .requiredOption(
409
426
  '-c, --contract-abi <fileLocation>',
410
427
  "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts",
411
428
  )
412
- .requiredOption('-ca, --contract-address <address>', 'Aztec address of the contract.')
429
+ .requiredOption('-ca, --contract-address <address>', 'Aztec address of the contract.', parseAztecAddress)
413
430
  .option('-f, --from <string>', 'Aztec address of the caller. If empty, will use the first account from RPC.')
414
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
431
+ .addOption(pxeOption)
415
432
  .action(async (functionName, options) => {
416
- const { contractAddress, functionArgs, contractAbi } = await prepTx(
417
- options.contractAbi,
418
- options.contractAddress,
419
- functionName,
420
- options.args,
421
- log,
422
- );
433
+ const { functionArgs, contractAbi } = await prepTx(options.contractAbi, functionName, options.args, log);
434
+
423
435
  const fnAbi = getAbiFunction(contractAbi, functionName);
424
436
  if (fnAbi.parameters.length !== options.args.length) {
425
437
  throw Error(
@@ -428,8 +440,23 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
428
440
  }
429
441
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
430
442
  const from = await getTxSender(client, options.from);
431
- const result = await client.viewTx(functionName, functionArgs, contractAddress, from);
432
- log('\nView result: ', result, '\n');
443
+ const result = await client.viewTx(functionName, functionArgs, options.contractAddress, from);
444
+ log(format('\nView result: ', result, '\n'));
445
+ });
446
+
447
+ program
448
+ .command('add-note')
449
+ .description('Adds a note to the database in the PXE.')
450
+ .argument('<address>', 'The Aztec address of the note owner.', parseAztecAddress)
451
+ .argument('<contractAddress>', 'Aztec address of the contract.', parseAztecAddress)
452
+ .argument('<storageSlot>', 'The storage slot of the note.', parseField)
453
+ .argument('<txHash>', 'The tx hash of the tx containing the note.', parseTxHash)
454
+ .requiredOption('-p, --preimage [notePreimage...]', 'Note preimage.', [])
455
+ .addOption(pxeOption)
456
+ .action(async (address, contractAddress, storageSlot, txHash, options) => {
457
+ const preimage = new NotePreimage(parseFields(options.preimage));
458
+ const client = await createCompatibleClient(options.rpcUrl, debugLogger);
459
+ await client.addNote(address, contractAddress, storageSlot, preimage, txHash);
433
460
  });
434
461
 
435
462
  // Helper for users to decode hex strings into structs if needed.
@@ -459,7 +486,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
459
486
  program
460
487
  .command('block-number')
461
488
  .description('Gets the current Aztec L2 block number.')
462
- .option('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
489
+ .addOption(pxeOption)
463
490
  .action(async (options: any) => {
464
491
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
465
492
  const num = await client.getBlockNumber();
@@ -490,12 +517,16 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command {
490
517
  program
491
518
  .command('get-node-info')
492
519
  .description('Gets the information of an aztec node at a URL.')
493
- .requiredOption('-u, --rpc-url <string>', 'URL of the Aztec RPC', AZTEC_RPC_HOST || 'http://localhost:8080')
520
+ .addOption(pxeOption)
494
521
  .action(async options => {
495
522
  const client = await createCompatibleClient(options.rpcUrl, debugLogger);
496
523
  const info = await client.getNodeInfo();
497
524
  log(`\nNode Info:\n`);
498
- Object.entries(info).map(([key, value]) => log(`${startCase(key)}: ${value}`));
525
+ log(`Sandbox Version: ${info.sandboxVersion}\n`);
526
+ log(`Compatible Nargo Version: ${info.compatibleNargoVersion}\n`);
527
+ log(`Chain Id: ${info.chainId}\n`);
528
+ log(`Protocol Version: ${info.protocolVersion}\n`);
529
+ log(`Rollup Address: ${info.l1ContractAddresses.rollupAddress.toString()}`);
499
530
  });
500
531
 
501
532
  program