@aztec/cli-wallet 0.0.1-commit.b655e406 → 0.0.1-commit.b9865e97

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.
Files changed (83) hide show
  1. package/README.md +2 -2
  2. package/dest/bin/index.d.ts +2 -1
  3. package/dest/bin/index.js +21 -11
  4. package/dest/cmds/authorize_action.d.ts +2 -2
  5. package/dest/cmds/authorize_action.d.ts.map +1 -1
  6. package/dest/cmds/authorize_action.js +5 -3
  7. package/dest/cmds/bridge_fee_juice.d.ts +2 -2
  8. package/dest/cmds/bridge_fee_juice.d.ts.map +1 -1
  9. package/dest/cmds/bridge_fee_juice.js +3 -2
  10. package/dest/cmds/check_tx.d.ts +1 -1
  11. package/dest/cmds/check_tx.d.ts.map +1 -1
  12. package/dest/cmds/check_tx.js +50 -21
  13. package/dest/cmds/create_account.d.ts +7 -5
  14. package/dest/cmds/create_account.d.ts.map +1 -1
  15. package/dest/cmds/create_account.js +43 -26
  16. package/dest/cmds/create_authwit.d.ts +1 -1
  17. package/dest/cmds/create_authwit.js +1 -1
  18. package/dest/cmds/deploy.d.ts +5 -4
  19. package/dest/cmds/deploy.d.ts.map +1 -1
  20. package/dest/cmds/deploy.js +64 -32
  21. package/dest/cmds/deploy_account.d.ts +4 -3
  22. package/dest/cmds/deploy_account.d.ts.map +1 -1
  23. package/dest/cmds/deploy_account.js +37 -20
  24. package/dest/cmds/get_fee_juice_balance.d.ts +5 -0
  25. package/dest/cmds/get_fee_juice_balance.d.ts.map +1 -0
  26. package/dest/cmds/get_fee_juice_balance.js +27 -0
  27. package/dest/cmds/import_test_accounts.d.ts +1 -1
  28. package/dest/cmds/index.d.ts +1 -1
  29. package/dest/cmds/index.d.ts.map +1 -1
  30. package/dest/cmds/index.js +33 -17
  31. package/dest/cmds/profile.d.ts +1 -1
  32. package/dest/cmds/profile.js +1 -1
  33. package/dest/cmds/register_contract.d.ts +1 -1
  34. package/dest/cmds/register_sender.d.ts +1 -1
  35. package/dest/cmds/send.d.ts +5 -4
  36. package/dest/cmds/send.d.ts.map +1 -1
  37. package/dest/cmds/send.js +33 -17
  38. package/dest/cmds/simulate.d.ts +1 -1
  39. package/dest/cmds/simulate.d.ts.map +1 -1
  40. package/dest/cmds/simulate.js +4 -4
  41. package/dest/storage/wallet_db.d.ts +10 -5
  42. package/dest/storage/wallet_db.d.ts.map +1 -1
  43. package/dest/storage/wallet_db.js +61 -32
  44. package/dest/utils/authorizations.d.ts +1 -1
  45. package/dest/utils/cli_wallet_and_node_wrapper.d.ts +1 -1
  46. package/dest/utils/cli_wallet_and_node_wrapper.d.ts.map +1 -1
  47. package/dest/utils/constants.d.ts +4 -0
  48. package/dest/utils/constants.d.ts.map +1 -0
  49. package/dest/utils/constants.js +7 -0
  50. package/dest/utils/ecdsa.d.ts +1 -1
  51. package/dest/utils/options/fees.d.ts +5 -3
  52. package/dest/utils/options/fees.d.ts.map +1 -1
  53. package/dest/utils/options/fees.js +34 -11
  54. package/dest/utils/options/index.d.ts +1 -1
  55. package/dest/utils/options/options.d.ts +1 -1
  56. package/dest/utils/options/options.js +1 -1
  57. package/dest/utils/profiling.d.ts +1 -1
  58. package/dest/utils/profiling.d.ts.map +1 -1
  59. package/dest/utils/profiling.js +9 -1
  60. package/dest/utils/wallet.d.ts +21 -12
  61. package/dest/utils/wallet.d.ts.map +1 -1
  62. package/dest/utils/wallet.js +135 -65
  63. package/package.json +19 -15
  64. package/src/bin/index.ts +19 -13
  65. package/src/cmds/authorize_action.ts +2 -2
  66. package/src/cmds/bridge_fee_juice.ts +3 -2
  67. package/src/cmds/check_tx.ts +55 -25
  68. package/src/cmds/create_account.ts +47 -26
  69. package/src/cmds/create_authwit.ts +1 -1
  70. package/src/cmds/deploy.ts +63 -36
  71. package/src/cmds/deploy_account.ts +38 -19
  72. package/src/cmds/get_fee_juice_balance.ts +37 -0
  73. package/src/cmds/index.ts +63 -9
  74. package/src/cmds/profile.ts +1 -1
  75. package/src/cmds/send.ts +33 -13
  76. package/src/cmds/simulate.ts +5 -8
  77. package/src/storage/wallet_db.ts +65 -38
  78. package/src/utils/cli_wallet_and_node_wrapper.ts +1 -1
  79. package/src/utils/constants.ts +4 -0
  80. package/src/utils/options/fees.ts +43 -12
  81. package/src/utils/options/options.ts +1 -1
  82. package/src/utils/profiling.ts +15 -1
  83. package/src/utils/wallet.ts +147 -81
package/src/cmds/index.ts CHANGED
@@ -10,14 +10,16 @@ import {
10
10
  parseFieldFromHexString,
11
11
  parsePublicKey,
12
12
  } from '@aztec/cli/utils';
13
- import { randomBytes } from '@aztec/foundation/crypto';
13
+ import { randomBytes } from '@aztec/foundation/crypto/random';
14
14
  import type { LogFn, Logger } from '@aztec/foundation/log';
15
+ import { TxStatus } from '@aztec/stdlib/tx';
15
16
 
16
17
  import { type Command, Option } from 'commander';
17
18
  import inquirer from 'inquirer';
18
19
 
19
20
  import type { WalletDB } from '../storage/wallet_db.js';
20
21
  import type { CliWalletAndNodeWrapper } from '../utils/cli_wallet_and_node_wrapper.js';
22
+ import type { AccountType } from '../utils/constants.js';
21
23
  import {
22
24
  ARTIFACT_DESCRIPTION,
23
25
  CLIFeeArgs,
@@ -38,7 +40,17 @@ import {
38
40
  createVerboseOption,
39
41
  integerArgParser,
40
42
  } from '../utils/options/index.js';
41
- import type { AccountType } from '../utils/wallet.js';
43
+
44
+ function parseWaitForStatus(status: string): TxStatus {
45
+ switch (status) {
46
+ case 'proposed':
47
+ return TxStatus.PROPOSED;
48
+ case 'checkpointed':
49
+ return TxStatus.CHECKPOINTED;
50
+ default:
51
+ throw new Error(`Invalid wait-for-status: ${status}. Use 'proposed' or 'checkpointed'.`);
52
+ }
53
+ }
42
54
 
43
55
  // TODO: This function is only used in 1 place so we could just inline this
44
56
  export function injectCommands(
@@ -63,7 +75,7 @@ export function injectCommands(
63
75
  const createAccountCommand = program
64
76
  .command('create-account')
65
77
  .description(
66
- '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).',
78
+ '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 account which uses an immutable key for authentication.',
67
79
  )
68
80
  .summary('Creates an aztec account that can be used for sending transactions.')
69
81
  .addOption(createAccountOption('Alias or address of the account performing the deployment', !db, db))
@@ -90,6 +102,11 @@ export function injectCommands(
90
102
  )
91
103
  .addOption(createAliasOption('Alias for the account. Used for easy reference in subsequent commands.', !db))
92
104
  .addOption(createTypeOption(true))
105
+ .option(
106
+ '-s, --salt <hex string>',
107
+ 'Optional deployment salt as a hex string for generating the deployment address. Defaults to 0.',
108
+ parseFieldFromHexString,
109
+ )
93
110
  .option(
94
111
  '--register-only',
95
112
  'Just register the account on the Wallet. Do not deploy or initialize the account contract.',
@@ -98,6 +115,7 @@ export function injectCommands(
98
115
  // `options.wait` is default true. Passing `--no-wait` will set it to false.
99
116
  // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue
100
117
  .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction')
118
+ .option('--wait-for-status <status>', "Tx status to wait for: 'proposed' or 'checkpointed'", 'proposed')
101
119
  .addOption(createVerboseOption());
102
120
 
103
121
  addOptions(createAccountCommand, CLIFeeArgs.getOptions()).action(async (_options, command) => {
@@ -107,7 +125,9 @@ export function injectCommands(
107
125
  type,
108
126
  from: parsedFromAddress,
109
127
  secretKey,
128
+ salt,
110
129
  wait,
130
+ waitForStatus: waitForStatusStr,
111
131
  registerOnly,
112
132
  skipInitialization,
113
133
  publicDeploy,
@@ -137,6 +157,7 @@ export function injectCommands(
137
157
  node,
138
158
  type,
139
159
  secretKey,
160
+ salt,
140
161
  publicKey,
141
162
  alias,
142
163
  parsedFromAddress,
@@ -146,6 +167,7 @@ export function injectCommands(
146
167
  registerClass,
147
168
  wait,
148
169
  CLIFeeArgs.parse(options, log, db),
170
+ parseWaitForStatus(waitForStatusStr),
149
171
  json,
150
172
  verbose,
151
173
  debugLogger,
@@ -180,12 +202,22 @@ export function injectCommands(
180
202
  '--skip-initialization',
181
203
  'Skip initializing the account contract. Useful for publicly deploying an existing account.',
182
204
  )
205
+ .option('--wait-for-status <status>', "Tx status to wait for: 'proposed' or 'checkpointed'", 'proposed')
183
206
  .addOption(createVerboseOption());
184
207
 
185
208
  addOptions(deployAccountCommand, CLIFeeArgs.getOptions()).action(async (parsedAccount, _options, command) => {
186
209
  const { deployAccount } = await import('./deploy_account.js');
187
210
  const options = command.optsWithGlobals();
188
- const { wait, from: parsedFromAddress, json, registerClass, skipInitialization, publicDeploy, verbose } = options;
211
+ const {
212
+ wait,
213
+ waitForStatus: waitForStatusStr,
214
+ from: parsedFromAddress,
215
+ json,
216
+ registerClass,
217
+ skipInitialization,
218
+ publicDeploy,
219
+ verbose,
220
+ } = options;
189
221
 
190
222
  const { wallet, node } = walletAndNodeWrapper;
191
223
 
@@ -199,6 +231,7 @@ export function injectCommands(
199
231
  publicDeploy,
200
232
  skipInitialization,
201
233
  CLIFeeArgs.parse(options, log, db),
234
+ parseWaitForStatus(waitForStatusStr),
202
235
  json,
203
236
  verbose,
204
237
  debugLogger,
@@ -219,7 +252,7 @@ export function injectCommands(
219
252
  )
220
253
  .option(
221
254
  '-s, --salt <hex string>',
222
- 'Optional deployment salt as a hex string for generating the deployment address.',
255
+ 'Optional deployment salt as a hex string for generating the deployment address. Defaults to random.',
223
256
  parseFieldFromHexString,
224
257
  )
225
258
  .option('--universal', 'Do not mix the sender address into the deployment.')
@@ -238,6 +271,7 @@ export function injectCommands(
238
271
  'The amount of time in seconds to wait for the deployment to post to L2',
239
272
  ).conflicts('wait'),
240
273
  )
274
+ .option('--wait-for-status <status>', "Tx status to wait for: 'proposed' or 'checkpointed'", 'proposed')
241
275
  .addOption(createVerboseOption());
242
276
 
243
277
  addOptions(deployCommand, CLIFeeArgs.getOptions()).action(async (artifactPathPromise, _options, command) => {
@@ -249,6 +283,7 @@ export function injectCommands(
249
283
  args,
250
284
  salt,
251
285
  wait,
286
+ waitForStatus: waitForStatusStr,
252
287
  classRegistration,
253
288
  init,
254
289
  publicDeployment,
@@ -262,12 +297,12 @@ export function injectCommands(
262
297
  const { wallet, node } = walletAndNodeWrapper;
263
298
  const artifactPath = await artifactPathPromise;
264
299
 
265
- debugLogger.info(`Using wallet with address ${parsedFromAddress.toString()}`);
300
+ debugLogger.info(`Using wallet with address ${parsedFromAddress?.toString() ?? '<none>'}`);
266
301
 
267
302
  const address = await deploy(
268
303
  wallet,
269
304
  node,
270
- universal ? undefined : parsedFromAddress,
305
+ parsedFromAddress,
271
306
  artifactPath,
272
307
  json,
273
308
  publicKey,
@@ -279,10 +314,12 @@ export function injectCommands(
279
314
  typeof init === 'string' ? false : init,
280
315
  wait,
281
316
  CLIFeeArgs.parse(options, log, db),
282
- timeout,
317
+ parseWaitForStatus(waitForStatusStr),
283
318
  verbose,
319
+ timeout,
284
320
  debugLogger,
285
321
  log,
322
+ universal,
286
323
  );
287
324
  if (db && address) {
288
325
  await db.storeContract(address, artifactPath, log, alias);
@@ -308,6 +345,7 @@ export function injectCommands(
308
345
  )
309
346
  .addOption(createAccountOption('Alias or address of the account to send the transaction from', !db, db))
310
347
  .option('--no-wait', 'Print transaction hash without waiting for it to be mined')
348
+ .option('--wait-for-status <status>', "Tx status to wait for: 'proposed' or 'checkpointed'", 'proposed')
311
349
  .addOption(createVerboseOption());
312
350
 
313
351
  addOptions(sendCommand, CLIFeeArgs.getOptions()).action(async (functionName, _options, command) => {
@@ -319,6 +357,7 @@ export function injectCommands(
319
357
  contractAddress,
320
358
  from: parsedFromAddress,
321
359
  wait,
360
+ waitForStatus: waitForStatusStr,
322
361
  alias,
323
362
  authWitness: authWitnessArray,
324
363
  verbose,
@@ -342,11 +381,12 @@ export function injectCommands(
342
381
  alias,
343
382
  CLIFeeArgs.parse(options, log, db),
344
383
  authWitnesses,
384
+ parseWaitForStatus(waitForStatusStr),
345
385
  verbose,
346
386
  log,
347
387
  );
348
388
  if (db && sentTx) {
349
- const txAlias = alias ? alias : `${functionName}-${randomBytes(16).toString()}`;
389
+ const txAlias = alias ? alias : `${functionName}-${randomBytes(16).toString('hex')}`;
350
390
  await db.storeTx(sentTx, log, txAlias);
351
391
  }
352
392
  });
@@ -492,6 +532,20 @@ export function injectCommands(
492
532
  }
493
533
  });
494
534
 
535
+ program
536
+ .command('get-fee-juice-balance')
537
+ .description('Checks the Fee Juice balance for a given address.')
538
+ .argument('<address>', 'Aztec address or alias to check balance for', address =>
539
+ aliasedAddressParser('accounts', address, db),
540
+ )
541
+ .option('--json', 'Emit output as json')
542
+ .option('--exact', 'Show exact balance with all 18 decimal places')
543
+ .action(async (address, options) => {
544
+ const { getFeeJuiceBalanceCmd } = await import('./get_fee_juice_balance.js');
545
+ const { json, exact } = options;
546
+ await getFeeJuiceBalanceCmd(walletAndNodeWrapper.node, address, json, exact, log);
547
+ });
548
+
495
549
  program
496
550
  .command('create-authwit')
497
551
  .description(
@@ -28,7 +28,7 @@ export async function profile(
28
28
  ) {
29
29
  const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log);
30
30
 
31
- const contract = await Contract.at(contractAddress, contractArtifact, wallet);
31
+ const contract = Contract.at(contractAddress, contractArtifact, wallet);
32
32
  const call = contract.methods[functionName](...functionArgs);
33
33
 
34
34
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(node, wallet, from);
package/src/cmds/send.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import type { AztecAddress } from '@aztec/aztec.js/addresses';
2
2
  import { AuthWitness } from '@aztec/aztec.js/authorization';
3
- import { Contract, type SendInteractionOptions } from '@aztec/aztec.js/contracts';
4
- import type { AztecNode } from '@aztec/aztec.js/node';
3
+ import { Contract, NO_WAIT, type SendInteractionOptions } from '@aztec/aztec.js/contracts';
4
+ import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
5
5
  import { prepTx } from '@aztec/cli/utils';
6
6
  import type { LogFn } from '@aztec/foundation/log';
7
+ import { TxStatus } from '@aztec/stdlib/tx';
7
8
 
8
9
  import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
9
10
  import { CLIFeeArgs } from '../utils/options/fees.js';
@@ -22,12 +23,13 @@ export async function send(
22
23
  cancellable: boolean,
23
24
  feeOpts: CLIFeeArgs,
24
25
  authWitnesses: AuthWitness[],
26
+ waitForStatus: TxStatus,
25
27
  verbose: boolean,
26
28
  log: LogFn,
27
29
  ) {
28
30
  const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log);
29
31
 
30
- const contract = await Contract.at(contractAddress, contractArtifact, wallet);
32
+ const contract = Contract.at(contractAddress, contractArtifact, wallet);
31
33
  const call = contract.methods[functionName](...functionArgs);
32
34
 
33
35
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(node, wallet, from);
@@ -37,40 +39,58 @@ export async function send(
37
39
  authWitnesses,
38
40
  };
39
41
 
40
- const { estimatedGas, stats } = await call.simulate({
42
+ const localStart = performance.now();
43
+ const sim = await call.simulate({
41
44
  ...sendOptions,
42
45
  fee: { ...sendOptions.fee, estimateGas: true },
43
46
  });
47
+ // estimateGas: true guarantees these fields are present
48
+ const estimatedGas = sim.estimatedGas!;
49
+ const stats = sim.stats!;
44
50
 
45
51
  if (feeOpts.estimateOnly) {
46
52
  return;
47
53
  }
48
54
 
49
- const tx = call.send({ ...sendOptions, fee: { ...sendOptions.fee, gasSettings: estimatedGas } });
50
55
  if (verbose) {
51
56
  printProfileResult(stats!, log);
52
57
  }
53
58
 
54
- const txHash = await tx.getTxHash();
59
+ const { txHash } = await call.send({
60
+ ...sendOptions,
61
+ fee: { ...sendOptions.fee, gasSettings: estimatedGas },
62
+ wait: NO_WAIT,
63
+ });
64
+ const localTimeMs = performance.now() - localStart;
65
+
55
66
  log(`\nTransaction hash: ${txHash.toString()}`);
67
+
56
68
  if (wait) {
57
69
  try {
58
- await tx.wait({ timeout: DEFAULT_TX_TIMEOUT_S });
70
+ const nodeStart = performance.now();
71
+ const receipt = await waitForTx(node, txHash, { timeout: DEFAULT_TX_TIMEOUT_S, waitForStatus });
72
+ const nodeTimeMs = performance.now() - nodeStart;
59
73
 
60
- log('Transaction has been mined');
61
-
62
- const receipt = await tx.getReceipt();
74
+ const statusLabel = waitForStatus === TxStatus.PROPOSED ? 'proposed' : 'checkpointed';
75
+ log(`Transaction has been ${statusLabel}`);
63
76
  log(` Tx fee: ${receipt.transactionFee}`);
64
77
  log(` Status: ${receipt.status}`);
65
78
  log(` Block number: ${receipt.blockNumber}`);
66
79
  log(` Block hash: ${receipt.blockHash?.toString()}`);
80
+ log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
81
+ log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
82
+
83
+ return {
84
+ txHash,
85
+ };
67
86
  } catch (err: any) {
68
87
  log(`Transaction failed\n ${err.message}`);
88
+ throw err;
69
89
  }
70
90
  } else {
71
91
  log('Transaction pending. Check status with check-tx');
92
+ return {
93
+ txHash,
94
+ };
72
95
  }
73
- return {
74
- txHash,
75
- };
76
96
  }
@@ -27,7 +27,7 @@ export async function simulate(
27
27
  ) {
28
28
  const { functionArgs, contractArtifact } = await prepTx(contractArtifactPath, functionName, functionArgsIn, log);
29
29
 
30
- const contract = await Contract.at(contractAddress, contractArtifact, wallet);
30
+ const contract = Contract.at(contractAddress, contractArtifact, wallet);
31
31
  const call = contract.methods[functionName](...functionArgs);
32
32
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(node, wallet, from);
33
33
  const simulationResult = await call.simulate({
@@ -38,17 +38,14 @@ export async function simulate(
38
38
  });
39
39
  if (verbose) {
40
40
  await printAuthorizations(
41
- simulationResult.offchainEffects!,
41
+ simulationResult.offchainEffects,
42
42
  async (address: AztecAddress) => {
43
43
  const metadata = await wallet.getContractMetadata(address);
44
- if (!metadata.contractInstance) {
44
+ if (!metadata.instance) {
45
45
  return undefined;
46
46
  }
47
- const classMetadata = await wallet.getContractClassMetadata(
48
- metadata.contractInstance.currentContractClassId,
49
- true,
50
- );
51
- return classMetadata.artifact;
47
+ const artifact = await wallet.getContractArtifact(metadata.instance.currentContractClassId);
48
+ return artifact;
52
49
  },
53
50
  log,
54
51
  );
@@ -1,17 +1,18 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import type { LogFn } from '@aztec/foundation/log';
3
3
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
4
4
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
5
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
6
6
  import type { TxHash } from '@aztec/stdlib/tx';
7
7
 
8
+ import type { AccountType } from '../utils/constants.js';
8
9
  import { extractECDSAPublicKeyFromBase64String } from '../utils/ecdsa.js';
9
- import type { AccountType } from '../utils/wallet.js';
10
10
 
11
11
  export const Aliases = ['accounts', 'contracts', 'artifacts', 'secrets', 'transactions', 'authwits'] as const;
12
12
  export type AliasType = (typeof Aliases)[number];
13
13
 
14
14
  export class WalletDB {
15
+ #store!: AztecAsyncKVStore;
15
16
  #accounts!: AztecAsyncMap<string, Buffer>;
16
17
  #aliases!: AztecAsyncMap<string, Buffer>;
17
18
  #bridgedFeeJuice!: AztecAsyncMap<string, Buffer>;
@@ -29,6 +30,7 @@ export class WalletDB {
29
30
  }
30
31
 
31
32
  async init(store: AztecAsyncKVStore) {
33
+ this.#store = store;
32
34
  this.#accounts = store.openMap('accounts');
33
35
  this.#aliases = store.openMap('aliases');
34
36
  this.#bridgedFeeJuice = store.openMap('bridgedFeeJuice');
@@ -41,14 +43,17 @@ export class WalletDB {
41
43
  }
42
44
 
43
45
  async pushBridgedFeeJuice(recipient: AztecAddress, secret: Fr, amount: bigint, leafIndex: bigint, log: LogFn) {
44
- let stackPointer = (await this.#bridgedFeeJuice.getAsync(`${recipient.toString()}:stackPointer`))?.readInt8() || 0;
45
- stackPointer++;
46
- await this.#bridgedFeeJuice.set(
47
- `${recipient.toString()}:${stackPointer}`,
48
- Buffer.from(`${amount.toString()}:${secret.toString()}:${leafIndex.toString()}`),
49
- );
50
- await this.#bridgedFeeJuice.set(`${recipient.toString()}:stackPointer`, Buffer.from([stackPointer]));
51
- log(`Pushed ${amount} fee juice for recipient ${recipient.toString()}. Stack pointer ${stackPointer}`);
46
+ await this.#store.transactionAsync(async () => {
47
+ let stackPointer =
48
+ (await this.#bridgedFeeJuice.getAsync(`${recipient.toString()}:stackPointer`))?.readInt8() || 0;
49
+ stackPointer++;
50
+ await this.#bridgedFeeJuice.set(
51
+ `${recipient.toString()}:${stackPointer}`,
52
+ Buffer.from(`${amount.toString()}:${secret.toString()}:${leafIndex.toString()}`),
53
+ );
54
+ await this.#bridgedFeeJuice.set(`${recipient.toString()}:stackPointer`, Buffer.from([stackPointer]));
55
+ log(`Pushed ${amount} fee juice for recipient ${recipient.toString()}. Stack pointer ${stackPointer}`);
56
+ });
52
57
  }
53
58
 
54
59
  async popBridgedFeeJuice(recipient: AztecAddress, log: LogFn) {
@@ -65,6 +70,20 @@ export class WalletDB {
65
70
  return { amount: BigInt(amountStr), secret: secretStr, leafIndex: BigInt(leafIndexStr) };
66
71
  }
67
72
 
73
+ async peekBridgedFeeJuice(recipient: AztecAddress, log: LogFn) {
74
+ const stackPointer =
75
+ (await this.#bridgedFeeJuice.getAsync(`${recipient.toString()}:stackPointer`))?.readInt8() || 0;
76
+ const result = await this.#bridgedFeeJuice.getAsync(`${recipient.toString()}:${stackPointer}`);
77
+ if (!result) {
78
+ throw new Error(
79
+ `No stored fee juice available for recipient ${recipient.toString()}. Please provide claim amount and secret. Stack pointer ${stackPointer}`,
80
+ );
81
+ }
82
+ const [amountStr, secretStr, leafIndexStr] = result.toString().split(':');
83
+ log(`Peeked ${amountStr} fee juice for recipient ${recipient.toString()}. Stack pointer ${stackPointer}`);
84
+ return { amount: BigInt(amountStr), secret: secretStr, leafIndex: BigInt(leafIndexStr) };
85
+ }
86
+
68
87
  async storeAccount(
69
88
  address: AztecAddress,
70
89
  {
@@ -76,19 +95,24 @@ export class WalletDB {
76
95
  }: { type: AccountType; secretKey: Fr; salt: Fr; alias: string | undefined; publicKey: string | undefined },
77
96
  log: LogFn,
78
97
  ) {
79
- if (alias) {
80
- await this.#aliases.set(`accounts:${alias}`, Buffer.from(address.toString()));
81
- }
82
- await this.#accounts.set(`${address.toString()}:type`, Buffer.from(type));
83
- await this.#accounts.set(`${address.toString()}:sk`, secretKey.toBuffer());
84
- await this.#accounts.set(`${address.toString()}:salt`, salt.toBuffer());
98
+ let publicSigningKey: Buffer | undefined;
85
99
  if (type === 'ecdsasecp256r1ssh' && publicKey) {
86
- const publicSigningKey = extractECDSAPublicKeyFromBase64String(publicKey);
87
- await this.storeAccountMetadata(address, 'publicSigningKey', publicSigningKey);
100
+ publicSigningKey = extractECDSAPublicKeyFromBase64String(publicKey);
88
101
  }
89
- await this.#aliases.set('accounts:last', Buffer.from(address.toString()));
90
- log(`Account stored in database with alias${alias ? `es last & ${alias}` : ' last'}`);
91
102
 
103
+ await this.#store.transactionAsync(async () => {
104
+ if (alias) {
105
+ await this.#aliases.set(`accounts:${alias}`, Buffer.from(address.toString()));
106
+ }
107
+ await this.#accounts.set(`${address.toString()}:type`, Buffer.from(type));
108
+ await this.#accounts.set(`${address.toString()}:sk`, secretKey.toBuffer());
109
+ await this.#accounts.set(`${address.toString()}:salt`, salt.toBuffer());
110
+ if (publicSigningKey) {
111
+ await this.#accounts.set(`${address.toString()}:publicSigningKey`, publicSigningKey);
112
+ }
113
+ await this.#aliases.set('accounts:last', Buffer.from(address.toString()));
114
+ });
115
+ log(`Account stored in database with alias${alias ? `es last & ${alias}` : ' last'}`);
92
116
  await this.refreshAliasCache();
93
117
  }
94
118
 
@@ -100,35 +124,38 @@ export class WalletDB {
100
124
  }
101
125
 
102
126
  async storeContract(address: AztecAddress, artifactPath: string, log: LogFn, alias?: string) {
103
- if (alias) {
104
- await this.#aliases.set(`contracts:${alias}`, Buffer.from(address.toString()));
105
- await this.#aliases.set(`artifacts:${alias}`, Buffer.from(artifactPath));
106
- }
107
- await this.#aliases.set(`contracts:last`, Buffer.from(address.toString()));
108
- await this.#aliases.set(`artifacts:last`, Buffer.from(artifactPath));
109
- await this.#aliases.set(`artifacts:${address.toString()}`, Buffer.from(artifactPath));
127
+ await this.#store.transactionAsync(async () => {
128
+ if (alias) {
129
+ await this.#aliases.set(`contracts:${alias}`, Buffer.from(address.toString()));
130
+ await this.#aliases.set(`artifacts:${alias}`, Buffer.from(artifactPath));
131
+ }
132
+ await this.#aliases.set(`contracts:last`, Buffer.from(address.toString()));
133
+ await this.#aliases.set(`artifacts:last`, Buffer.from(artifactPath));
134
+ await this.#aliases.set(`artifacts:${address.toString()}`, Buffer.from(artifactPath));
135
+ });
110
136
  log(`Contract stored in database with alias${alias ? `es last & ${alias}` : ' last'}`);
111
-
112
137
  await this.refreshAliasCache();
113
138
  }
114
139
 
115
140
  async storeAuthwitness(authWit: AuthWitness, log: LogFn, alias?: string) {
116
- if (alias) {
117
- await this.#aliases.set(`authwits:${alias}`, Buffer.from(authWit.toString()));
118
- }
119
- await this.#aliases.set(`authwits:last`, Buffer.from(authWit.toString()));
141
+ await this.#store.transactionAsync(async () => {
142
+ if (alias) {
143
+ await this.#aliases.set(`authwits:${alias}`, Buffer.from(authWit.toString()));
144
+ }
145
+ await this.#aliases.set(`authwits:last`, Buffer.from(authWit.toString()));
146
+ });
120
147
  log(`Authorization witness stored in database with alias${alias ? `es last & ${alias}` : ' last'}`);
121
-
122
148
  await this.refreshAliasCache();
123
149
  }
124
150
 
125
151
  async storeTx({ txHash }: { txHash: TxHash }, log: LogFn, alias?: string) {
126
- if (alias) {
127
- await this.#aliases.set(`transactions:${alias}`, Buffer.from(txHash.toString()));
128
- }
129
- await this.#aliases.set(`transactions:last`, Buffer.from(txHash.toString()));
152
+ await this.#store.transactionAsync(async () => {
153
+ if (alias) {
154
+ await this.#aliases.set(`transactions:${alias}`, Buffer.from(txHash.toString()));
155
+ }
156
+ await this.#aliases.set(`transactions:last`, Buffer.from(txHash.toString()));
157
+ });
130
158
  log(`Transaction hash stored in database with alias${alias ? `es last & ${alias}` : ' last'}`);
131
-
132
159
  await this.refreshAliasCache();
133
160
  }
134
161
 
@@ -21,7 +21,7 @@ export class CliWalletAndNodeWrapper {
21
21
  return this._wallet;
22
22
  }
23
23
 
24
- get node() {
24
+ get node(): AztecNode {
25
25
  if (!this._node) {
26
26
  throw new Error('Node not initialized while it should have been initialized in preSubcommand');
27
27
  }
@@ -0,0 +1,4 @@
1
+ export const MIN_FEE_PADDING = 0.5;
2
+
3
+ export const AccountTypes = ['schnorr', 'ecdsasecp256r1', 'ecdsasecp256r1ssh', 'ecdsasecp256k1'] as const;
4
+ export type AccountType = (typeof AccountTypes)[number];
@@ -1,16 +1,17 @@
1
1
  import type { FeePaymentMethod } from '@aztec/aztec.js/fee';
2
2
  import type { AztecNode } from '@aztec/aztec.js/node';
3
- import type { FeeOptions, Wallet } from '@aztec/aztec.js/wallet';
4
- import { Fr } from '@aztec/foundation/fields';
3
+ import type { Wallet } from '@aztec/aztec.js/wallet';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
5
5
  import type { LogFn } from '@aztec/foundation/log';
6
6
  import type { FieldsOf } from '@aztec/foundation/types';
7
7
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
8
- import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
8
+ import { Gas, GasFees, GasSettings, ManaUsageEstimate } from '@aztec/stdlib/gas';
9
+ import type { FeeOptions } from '@aztec/wallet-sdk/base-wallet';
9
10
 
10
11
  import { Option } from 'commander';
11
12
 
12
13
  import type { WalletDB } from '../../storage/wallet_db.js';
13
- import { BASE_FEE_PADDING } from '../wallet.js';
14
+ import { MIN_FEE_PADDING } from '../constants.js';
14
15
  import { aliasedAddressParser } from './options.js';
15
16
 
16
17
  export type RawCliFeeArgs = {
@@ -114,6 +115,7 @@ export function parsePaymentMethod(
114
115
  payment: string,
115
116
  log: LogFn,
116
117
  db?: WalletDB,
118
+ estimateOnly?: boolean,
117
119
  ): (wallet: Wallet, from: AztecAddress, gasSettings: GasSettings) => Promise<FeePaymentMethod | undefined> {
118
120
  const parsed = payment.split(',').reduce(
119
121
  (acc, item) => {
@@ -148,7 +150,7 @@ export function parsePaymentMethod(
148
150
  amount: claimAmount,
149
151
  secret: claimSecret,
150
152
  leafIndex: messageLeafIndex,
151
- } = await db.popBridgedFeeJuice(from, log));
153
+ } = estimateOnly ? await db.peekBridgedFeeJuice(from, log) : await db.popBridgedFeeJuice(from, log));
152
154
  } else {
153
155
  ({ claimAmount, claimSecret, messageLeafIndex } = parsed);
154
156
  }
@@ -156,10 +158,10 @@ export function parsePaymentMethod(
156
158
  const { FeeJuicePaymentMethodWithClaim } = await import('@aztec/aztec.js/fee');
157
159
  return new FeeJuicePaymentMethodWithClaim(from, {
158
160
  claimAmount: (typeof claimAmount === 'string'
159
- ? Fr.fromHexString(claimAmount)
161
+ ? Fr.fromString(claimAmount)
160
162
  : new Fr(claimAmount)
161
163
  ).toBigInt(),
162
- claimSecret: Fr.fromHexString(claimSecret),
164
+ claimSecret: typeof claimSecret === 'string' ? Fr.fromString(claimSecret) : claimSecret,
163
165
  messageLeafIndex: BigInt(messageLeafIndex),
164
166
  });
165
167
  } else {
@@ -170,6 +172,9 @@ export function parsePaymentMethod(
170
172
  case 'fpc-public': {
171
173
  const fpc = getFpc();
172
174
  const asset = getAsset();
175
+ log(
176
+ `WARNING: fpc-public is deprecated and will not work on mainnet alpha. Use fee_juice or fpc-sponsored instead.`,
177
+ );
173
178
  log(`Using public fee payment with asset ${asset} via paymaster ${fpc}`);
174
179
  const { PublicFeePaymentMethod } = await import('@aztec/aztec.js/fee');
175
180
  return new PublicFeePaymentMethod(fpc, from, wallet, gasSettings);
@@ -177,6 +182,9 @@ export function parsePaymentMethod(
177
182
  case 'fpc-private': {
178
183
  const fpc = getFpc();
179
184
  const asset = getAsset();
185
+ log(
186
+ `WARNING: fpc-private is deprecated and will not work on mainnet alpha. Use fee_juice or fpc-sponsored instead.`,
187
+ );
180
188
  log(`Using private fee payment with asset ${asset} via paymaster ${fpc}`);
181
189
  const { PrivateFeePaymentMethod } = await import('@aztec/aztec.js/fee');
182
190
  return new PrivateFeePaymentMethod(fpc, from, wallet, gasSettings);
@@ -249,8 +257,9 @@ export class CLIFeeArgs {
249
257
  ) {}
250
258
 
251
259
  async toUserFeeOptions(node: AztecNode, wallet: Wallet, from: AztecAddress): Promise<ParsedFeeOptions> {
252
- const maxFeesPerGas = (await node.getCurrentBaseFees()).mul(1 + BASE_FEE_PADDING);
253
- const gasSettings = GasSettings.default({ ...this.gasSettings, maxFeesPerGas });
260
+ const minFees = await this.getMinFees(node);
261
+ const maxFeesPerGas = minFees.mul(1 + MIN_FEE_PADDING);
262
+ const gasSettings = GasSettings.fallback({ ...this.gasSettings, maxFeesPerGas });
254
263
  const paymentMethod = await this.paymentMethod(wallet, from, gasSettings);
255
264
  return {
256
265
  paymentMethod,
@@ -258,10 +267,32 @@ export class CLIFeeArgs {
258
267
  };
259
268
  }
260
269
 
270
+ /**
271
+ * Returns the worst-case min fee across predicted future slots.
272
+ * Falls back to getCurrentMinFees if the node doesn't support getPredictedMinFees.
273
+ */
274
+ private async getMinFees(node: AztecNode): Promise<GasFees> {
275
+ try {
276
+ const predicted = await node.getPredictedMinFees(ManaUsageEstimate.Limit);
277
+ if (predicted.length === 0) {
278
+ return node.getCurrentMinFees();
279
+ }
280
+ return predicted.reduce((worst, fees) => (fees.feePerL2Gas > worst.feePerL2Gas ? fees : worst));
281
+ } catch (err: any) {
282
+ // Fallback for old nodes that don't support getPredictedMinFees.
283
+ // Only fall back on method-not-found errors (JSON-RPC code -32601); rethrow others.
284
+ if (err?.cause?.code === -32601 || err?.message?.includes('Method not found')) {
285
+ return node.getCurrentMinFees();
286
+ }
287
+ throw err;
288
+ }
289
+ }
290
+
261
291
  static parse(args: RawCliFeeArgs, log: LogFn, db?: WalletDB): CLIFeeArgs {
292
+ const estimateOnly = !!args.estimateGasOnly;
262
293
  return new CLIFeeArgs(
263
- !!args.estimateGasOnly,
264
- parsePaymentMethod(args.payment ?? 'method=fee_juice', log, db),
294
+ estimateOnly,
295
+ parsePaymentMethod(args.payment ?? 'method=fee_juice', log, db, estimateOnly),
265
296
  parseGasSettings(args),
266
297
  );
267
298
  }
@@ -287,7 +318,7 @@ function formatGasEstimate(estimate: Pick<GasSettings, 'gasLimits' | 'teardownGa
287
318
  }
288
319
 
289
320
  function getEstimatedCost(estimate: Pick<GasSettings, 'gasLimits' | 'teardownGasLimits'>, maxFeesPerGas: GasFees) {
290
- return GasSettings.default({ ...estimate, maxFeesPerGas })
321
+ return GasSettings.fallback({ ...estimate, maxFeesPerGas })
291
322
  .getFeeLimit()
292
323
  .toBigInt();
293
324
  }