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

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 (86) 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 +57 -26
  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 +47 -31
  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 +65 -36
  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 +38 -24
  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/import_test_accounts.d.ts.map +1 -1
  29. package/dest/cmds/import_test_accounts.js +1 -1
  30. package/dest/cmds/index.d.ts +1 -1
  31. package/dest/cmds/index.d.ts.map +1 -1
  32. package/dest/cmds/index.js +33 -17
  33. package/dest/cmds/profile.d.ts +1 -1
  34. package/dest/cmds/profile.js +1 -1
  35. package/dest/cmds/register_contract.d.ts +1 -1
  36. package/dest/cmds/register_sender.d.ts +1 -1
  37. package/dest/cmds/send.d.ts +5 -4
  38. package/dest/cmds/send.d.ts.map +1 -1
  39. package/dest/cmds/send.js +34 -21
  40. package/dest/cmds/simulate.d.ts +1 -1
  41. package/dest/cmds/simulate.d.ts.map +1 -1
  42. package/dest/cmds/simulate.js +5 -4
  43. package/dest/storage/wallet_db.d.ts +10 -5
  44. package/dest/storage/wallet_db.d.ts.map +1 -1
  45. package/dest/storage/wallet_db.js +61 -32
  46. package/dest/utils/authorizations.d.ts +1 -1
  47. package/dest/utils/cli_wallet_and_node_wrapper.d.ts +1 -1
  48. package/dest/utils/cli_wallet_and_node_wrapper.d.ts.map +1 -1
  49. package/dest/utils/constants.d.ts +4 -0
  50. package/dest/utils/constants.d.ts.map +1 -0
  51. package/dest/utils/constants.js +8 -0
  52. package/dest/utils/ecdsa.d.ts +1 -1
  53. package/dest/utils/options/fees.d.ts +5 -3
  54. package/dest/utils/options/fees.d.ts.map +1 -1
  55. package/dest/utils/options/fees.js +38 -12
  56. package/dest/utils/options/index.d.ts +1 -1
  57. package/dest/utils/options/options.d.ts +1 -1
  58. package/dest/utils/options/options.js +1 -1
  59. package/dest/utils/profiling.d.ts +1 -1
  60. package/dest/utils/profiling.d.ts.map +1 -1
  61. package/dest/utils/profiling.js +9 -1
  62. package/dest/utils/wallet.d.ts +30 -12
  63. package/dest/utils/wallet.d.ts.map +1 -1
  64. package/dest/utils/wallet.js +154 -68
  65. package/package.json +19 -15
  66. package/src/bin/index.ts +19 -13
  67. package/src/cmds/authorize_action.ts +2 -2
  68. package/src/cmds/bridge_fee_juice.ts +3 -2
  69. package/src/cmds/check_tx.ts +65 -34
  70. package/src/cmds/create_account.ts +51 -28
  71. package/src/cmds/create_authwit.ts +1 -1
  72. package/src/cmds/deploy.ts +64 -37
  73. package/src/cmds/deploy_account.ts +39 -20
  74. package/src/cmds/get_fee_juice_balance.ts +37 -0
  75. package/src/cmds/import_test_accounts.ts +5 -1
  76. package/src/cmds/index.ts +63 -9
  77. package/src/cmds/profile.ts +1 -1
  78. package/src/cmds/send.ts +34 -14
  79. package/src/cmds/simulate.ts +6 -8
  80. package/src/storage/wallet_db.ts +65 -38
  81. package/src/utils/cli_wallet_and_node_wrapper.ts +1 -1
  82. package/src/utils/constants.ts +10 -0
  83. package/src/utils/options/fees.ts +46 -13
  84. package/src/utils/options/options.ts +1 -1
  85. package/src/utils/profiling.ts +15 -1
  86. package/src/utils/wallet.ts +170 -79
@@ -1,12 +1,16 @@
1
1
  import type { ContractArtifact } from '@aztec/aztec.js/abi';
2
- import type { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
3
3
  import { Fr } from '@aztec/aztec.js/fields';
4
4
  import type { AztecNode } from '@aztec/aztec.js/node';
5
5
  import { ProtocolContractAddress } from '@aztec/aztec.js/protocol';
6
6
  import type { TxHash } from '@aztec/aztec.js/tx';
7
7
  import type { LogFn } from '@aztec/foundation/log';
8
- import { siloNullifier } from '@aztec/stdlib/hash';
9
- import { UniqueNote } from '@aztec/stdlib/note';
8
+ import {
9
+ computeSiloedPrivateInitializationNullifier,
10
+ computeSiloedPublicInitializationNullifier,
11
+ siloNullifier,
12
+ } from '@aztec/stdlib/hash';
13
+ import { NoteDao } from '@aztec/stdlib/note';
10
14
 
11
15
  import type { CLIWallet } from '../utils/wallet.js';
12
16
 
@@ -29,19 +33,22 @@ export async function checkTx(
29
33
  // dependency when dropping PXE JSON RPC Server.
30
34
 
31
35
  async function inspectTx(wallet: CLIWallet, aztecNode: AztecNode, txHash: TxHash, log: LogFn) {
32
- const [receipt, effectsInBlock] = await Promise.all([aztecNode.getTxReceipt(txHash), aztecNode.getTxEffect(txHash)]);
36
+ const receipt = await aztecNode.getTxReceipt(txHash, { includeTxEffect: true });
33
37
  // Base tx data
34
38
  log(`Tx ${txHash.toString()}`);
35
- log(` Status: ${receipt.status} ${effectsInBlock ? `(${effectsInBlock.data.revertCode.getDescription()})` : ''}`);
39
+ log(` Status: ${receipt.status}`);
40
+ if (receipt.executionResult) {
41
+ log(` Execution result: ${receipt.executionResult}`);
42
+ }
36
43
  if (receipt.error) {
37
44
  log(` Error: ${receipt.error}`);
38
45
  }
39
46
 
40
- if (!effectsInBlock) {
47
+ if (!receipt.isMined() || !receipt.txEffect) {
41
48
  return;
42
49
  }
43
50
 
44
- const effects = effectsInBlock.data;
51
+ const effects = receipt.txEffect;
45
52
  const artifactMap = await getKnownArtifacts(wallet);
46
53
 
47
54
  log(` Block: ${receipt.blockNumber} (${receipt.blockHash?.toString()})`);
@@ -80,12 +87,13 @@ async function inspectTx(wallet: CLIWallet, aztecNode: AztecNode, txHash: TxHash
80
87
  // Nullifiers
81
88
  const nullifierCount = effects.nullifiers.length;
82
89
  const { deployNullifiers, initNullifiers, classNullifiers } = await getKnownNullifiers(wallet, artifactMap);
90
+ const accounts = (await wallet.getAccounts()).map(a => a.item);
83
91
  if (nullifierCount > 0) {
84
92
  log(' Nullifiers:');
85
93
  for (const nullifier of effects.nullifiers) {
86
94
  const deployed = deployNullifiers[nullifier.toString()];
87
95
  const note = deployed
88
- ? (await wallet.getNotes({ siloedNullifier: nullifier, contractAddress: deployed }))[0]
96
+ ? (await wallet.getNotes({ siloedNullifier: nullifier, contractAddress: deployed, scopes: accounts }))[0]
89
97
  : undefined;
90
98
  const initialized = initNullifiers[nullifier.toString()];
91
99
  const registered = classNullifiers[nullifier.toString()];
@@ -121,11 +129,10 @@ async function inspectTx(wallet: CLIWallet, aztecNode: AztecNode, txHash: TxHash
121
129
  }
122
130
  }
123
131
 
124
- function inspectNote(note: UniqueNote, artifactMap: ArtifactMap, log: LogFn, text = 'Note') {
132
+ function inspectNote(note: NoteDao, artifactMap: ArtifactMap, log: LogFn, text = 'Note') {
125
133
  const artifact = artifactMap[note.contractAddress.toString()];
126
134
  const contract = artifact?.name ?? note.contractAddress.toString();
127
135
  log(` ${text} at ${contract}`);
128
- log(` Recipient: ${toFriendlyAddress(note.recipient, artifactMap)}`);
129
136
  for (const field of note.note.items) {
130
137
  log(` ${field.toString()}`);
131
138
  }
@@ -142,46 +149,70 @@ function toFriendlyAddress(address: AztecAddress, artifactMap: ArtifactMap) {
142
149
 
143
150
  async function getKnownNullifiers(wallet: CLIWallet, artifactMap: ArtifactMap) {
144
151
  const knownContracts = await wallet.getContracts();
145
- const deployerAddress = ProtocolContractAddress.ContractInstanceRegistry;
146
- const classRegistryAddress = ProtocolContractAddress.ContractClassRegistry;
152
+
153
+ const [contractResults, classResults] = await Promise.all([
154
+ Promise.all(knownContracts.map(contract => getContractNullifiers(wallet, contract))),
155
+ Promise.all(Object.values(artifactMap).map(artifact => getClassNullifier(artifact))),
156
+ ]);
157
+
147
158
  const initNullifiers: Record<string, AztecAddress> = {};
148
159
  const deployNullifiers: Record<string, AztecAddress> = {};
149
160
  const classNullifiers: Record<string, string> = {};
150
- for (const contract of knownContracts) {
151
- initNullifiers[(await siloNullifier(contract, contract.toField())).toString()] = contract;
152
- deployNullifiers[(await siloNullifier(deployerAddress, contract.toField())).toString()] = contract;
161
+
162
+ for (const { contract, deployNullifier, privateInitNullifier, publicInitNullifier } of contractResults) {
163
+ deployNullifiers[deployNullifier.toString()] = contract;
164
+ if (privateInitNullifier) {
165
+ initNullifiers[privateInitNullifier.toString()] = contract;
166
+ }
167
+ initNullifiers[publicInitNullifier.toString()] = contract;
153
168
  }
154
- for (const artifact of Object.values(artifactMap)) {
155
- classNullifiers[(await siloNullifier(classRegistryAddress, artifact.classId)).toString()] =
156
- `${artifact.name}Class<${artifact.classId}>`;
169
+ for (const { nullifier, label } of classResults) {
170
+ classNullifiers[nullifier.toString()] = label;
157
171
  }
172
+
158
173
  return { initNullifiers, deployNullifiers, classNullifiers };
159
174
  }
160
175
 
176
+ async function getContractNullifiers(wallet: CLIWallet, contract: AztecAddress) {
177
+ const deployerAddress = ProtocolContractAddress.ContractInstanceRegistry;
178
+ const deployNullifier = await siloNullifier(deployerAddress, contract.toField());
179
+
180
+ const metadata = await wallet.getContractMetadata(contract);
181
+ const privateInitNullifier = metadata.instance
182
+ ? await computeSiloedPrivateInitializationNullifier(contract, metadata.instance.initializationHash)
183
+ : undefined;
184
+ const publicInitNullifier = await computeSiloedPublicInitializationNullifier(contract);
185
+
186
+ return { contract, deployNullifier, privateInitNullifier, publicInitNullifier };
187
+ }
188
+
189
+ async function getClassNullifier(artifact: ContractArtifactWithClassId) {
190
+ const classRegistryAddress = ProtocolContractAddress.ContractClassRegistry;
191
+ const nullifier = await siloNullifier(classRegistryAddress, artifact.classId);
192
+ return { nullifier, label: `${artifact.name}Class<${artifact.classId}>` };
193
+ }
194
+
161
195
  type ArtifactMap = Record<string, ContractArtifactWithClassId>;
162
196
  type ContractArtifactWithClassId = ContractArtifact & { classId: Fr };
163
197
 
164
198
  async function getKnownArtifacts(wallet: CLIWallet): Promise<ArtifactMap> {
165
199
  const knownContractAddresses = await wallet.getContracts();
166
- const knownContracts = (
167
- await Promise.all(knownContractAddresses.map(contractAddress => wallet.getContractMetadata(contractAddress)))
168
- ).map(contractMetadata => contractMetadata.contractInstance);
169
- const classIds = [...new Set(knownContracts.map(contract => contract?.currentContractClassId))];
170
- const knownArtifacts = (
171
- await Promise.all(classIds.map(classId => (classId ? wallet.getContractClassMetadata(classId) : undefined)))
172
- ).map(contractClassMetadata =>
173
- contractClassMetadata
174
- ? { ...contractClassMetadata.artifact, classId: contractClassMetadata.contractClass?.id }
175
- : undefined,
200
+ const knownContracts = await Promise.all(
201
+ knownContractAddresses.map(contractAddress => wallet.getContractMetadata(contractAddress)),
176
202
  );
203
+ const classIdFor = (metadata: (typeof knownContracts)[number]): Fr | undefined =>
204
+ metadata.updatedContractClassId ?? metadata.instance?.originalContractClassId;
205
+ const classIds = [...new Set(knownContracts.map(classIdFor))];
206
+ const knownArtifacts = (
207
+ await Promise.all(classIds.map(classId => (classId ? wallet.getContractArtifact(classId) : undefined)))
208
+ ).map((artifact, index) => (artifact ? { ...artifact, classId: classIds[index] } : undefined));
177
209
  const map: Record<string, ContractArtifactWithClassId> = {};
178
- for (const instance of knownContracts) {
179
- if (instance) {
180
- const artifact = knownArtifacts.find(a =>
181
- a?.classId?.equals(instance.currentContractClassId),
182
- ) as ContractArtifactWithClassId;
210
+ for (const metadata of knownContracts) {
211
+ const classId = classIdFor(metadata);
212
+ if (metadata.instance && classId) {
213
+ const artifact = knownArtifacts.find(a => a?.classId?.equals(classId)) as ContractArtifactWithClassId;
183
214
  if (artifact) {
184
- map[instance.address.toString()] = artifact;
215
+ map[metadata.instance.address.toString()] = artifact;
185
216
  }
186
217
  }
187
218
  }
@@ -1,20 +1,25 @@
1
+ import { NO_FROM } from '@aztec/aztec.js/account';
1
2
  import { AztecAddress } from '@aztec/aztec.js/addresses';
2
- import type { AztecNode } from '@aztec/aztec.js/node';
3
+ import { NO_WAIT } from '@aztec/aztec.js/contracts';
4
+ import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
3
5
  import type { DeployAccountOptions } from '@aztec/aztec.js/wallet';
4
6
  import { prettyPrintJSON } from '@aztec/cli/cli-utils';
5
- import { Fr } from '@aztec/foundation/fields';
7
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
8
  import type { LogFn, Logger } from '@aztec/foundation/log';
9
+ import { type TxHash, type TxReceipt, TxStatus } from '@aztec/stdlib/tx';
7
10
 
8
11
  import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
12
+ import type { AccountType } from '../utils/constants.js';
9
13
  import { CLIFeeArgs } from '../utils/options/fees.js';
10
14
  import { printProfileResult } from '../utils/profiling.js';
11
- import { type AccountType, CLIWallet } from '../utils/wallet.js';
15
+ import { CLIWallet } from '../utils/wallet.js';
12
16
 
13
17
  export async function createAccount(
14
18
  wallet: CLIWallet,
15
19
  aztecNode: AztecNode,
16
20
  accountType: AccountType,
17
21
  secretKey: Fr | undefined,
22
+ salt: Fr | undefined,
18
23
  publicKey: string | undefined,
19
24
  alias: string | undefined,
20
25
  deployer: AztecAddress | undefined,
@@ -24,6 +29,7 @@ export async function createAccount(
24
29
  registerClass: boolean,
25
30
  wait: boolean,
26
31
  feeOpts: CLIFeeArgs,
32
+ waitForStatus: TxStatus,
27
33
  json: boolean,
28
34
  verbose: boolean,
29
35
  debugLogger: Logger,
@@ -35,10 +41,10 @@ export async function createAccount(
35
41
  undefined /* address, we don't have it yet */,
36
42
  secretKey,
37
43
  accountType,
38
- Fr.ZERO,
44
+ salt,
39
45
  publicKey,
40
46
  );
41
- const { salt } = account.getInstance();
47
+ const instanceSalt = account.getInstance().salt;
42
48
  const { address, publicKeys, partialAddress } = await account.getCompleteAddress();
43
49
 
44
50
  const out: Record<string, any> = {};
@@ -49,7 +55,7 @@ export async function createAccount(
49
55
  out.secretKey = secretKey;
50
56
  }
51
57
  out.partialAddress = partialAddress;
52
- out.salt = salt;
58
+ out.salt = instanceSalt;
53
59
  out.initHash = account.getInstance().initializationHash;
54
60
  } else {
55
61
  log(`\nNew account:\n`);
@@ -59,17 +65,19 @@ export async function createAccount(
59
65
  log(`Secret key: ${secretKey.toString()}`);
60
66
  }
61
67
  log(`Partial address: ${partialAddress.toString()}`);
62
- log(`Salt: ${salt.toString()}`);
68
+ log(`Salt: ${instanceSalt.toString()}`);
63
69
  log(`Init hash: ${account.getInstance().initializationHash.toString()}`);
64
70
  }
65
71
 
66
- let tx;
67
- let txReceipt;
68
- if (!registerOnly) {
72
+ let txHash: TxHash | undefined;
73
+ let txReceipt: TxReceipt | undefined;
74
+ // Initializerless accounts have no deployment tx — creating one only registers it locally — so there is
75
+ // nothing to deploy on-chain.
76
+ if (!registerOnly && accountType !== 'schnorr_initializerless') {
69
77
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(aztecNode, wallet, address);
70
78
 
71
79
  const delegatedDeployment = deployer && !account.address.equals(deployer);
72
- const from = delegatedDeployment ? deployer : AztecAddress.ZERO;
80
+ const from = delegatedDeployment ? deployer : NO_FROM;
73
81
 
74
82
  const deployAccountOpts: DeployAccountOptions = {
75
83
  skipClassPublication: !registerClass,
@@ -79,11 +87,15 @@ export async function createAccount(
79
87
  fee: { paymentMethod, gasSettings },
80
88
  };
81
89
 
90
+ const localStart = performance.now();
82
91
  const deployMethod = await account.getDeployMethod();
83
- const { estimatedGas, stats } = await deployMethod.simulate({
92
+ const sim = await deployMethod.simulate({
84
93
  ...deployAccountOpts,
85
- fee: { ...deployAccountOpts.fee, estimateGas: true },
94
+ includeMetadata: true,
86
95
  });
96
+ // includeMetadata: true guarantees these fields are present
97
+ const estimatedGas = await wallet.estimateGasLimits(sim.gasUsed!);
98
+ const stats = sim.stats!;
87
99
 
88
100
  if (feeOpts.estimateOnly) {
89
101
  if (json) {
@@ -99,7 +111,14 @@ export async function createAccount(
99
111
  };
100
112
  }
101
113
  } else {
102
- tx = deployMethod.send({
114
+ if (verbose) {
115
+ printProfileResult(stats, log);
116
+ }
117
+
118
+ if (!json) {
119
+ log(`\nWaiting for account contract deployment...`);
120
+ }
121
+ const sendOpts = {
103
122
  ...deployAccountOpts,
104
123
  fee: deployAccountOpts.fee
105
124
  ? {
@@ -107,37 +126,41 @@ export async function createAccount(
107
126
  gasSettings: estimatedGas,
108
127
  }
109
128
  : undefined,
110
- });
111
- if (verbose) {
112
- printProfileResult(stats, log);
113
- }
129
+ };
130
+
131
+ ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
132
+ const localTimeMs = performance.now() - localStart;
114
133
 
115
- const txHash = await tx.getTxHash();
116
- debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
117
- out.txHash = txHash;
118
134
  if (wait) {
119
- if (!json) {
120
- log(`\nWaiting for account contract deployment...`);
121
- }
122
- txReceipt = await tx.wait({ timeout: DEFAULT_TX_TIMEOUT_S });
135
+ const nodeStart = performance.now();
136
+ txReceipt = await waitForTx(aztecNode, txHash, { timeout: DEFAULT_TX_TIMEOUT_S, waitForStatus });
137
+ const nodeTimeMs = performance.now() - nodeStart;
138
+
123
139
  out.txReceipt = {
124
140
  status: txReceipt.status,
125
141
  transactionFee: txReceipt.transactionFee,
126
142
  };
143
+
144
+ if (!json) {
145
+ log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
146
+ log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
147
+ }
127
148
  }
149
+ debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
150
+ out.txHash = txHash;
128
151
  }
129
152
  }
130
153
 
131
154
  if (json) {
132
155
  log(prettyPrintJSON(out));
133
156
  } else {
134
- if (tx) {
135
- log(`Deploy tx hash: ${(await tx.getTxHash()).toString()}`);
157
+ if (txHash) {
158
+ log(`Deploy tx hash: ${txHash.toString()}`);
136
159
  }
137
160
  if (txReceipt) {
138
161
  log(`Deploy tx fee: ${txReceipt.transactionFee}`);
139
162
  }
140
163
  }
141
164
 
142
- return { alias, address, secretKey, salt };
165
+ return { alias, address, secretKey, salt: instanceSalt };
143
166
  }
@@ -28,7 +28,7 @@ export async function createAuthwit(
28
28
  );
29
29
  }
30
30
 
31
- const contract = await Contract.at(contractAddress, contractArtifact, wallet);
31
+ const contract = Contract.at(contractAddress, contractArtifact, wallet);
32
32
  const call = await contract.methods[functionName](...functionArgs).getFunctionCall();
33
33
 
34
34
  const witness = await wallet.createAuthWit(from, { caller, call });
@@ -1,12 +1,15 @@
1
- import { AztecAddress } from '@aztec/aztec.js/addresses';
1
+ import { NO_FROM } from '@aztec/aztec.js/account';
2
+ import type { AztecAddress } from '@aztec/aztec.js/addresses';
2
3
  import type { DeployOptions } from '@aztec/aztec.js/contracts';
4
+ import { NO_WAIT } from '@aztec/aztec.js/contracts';
3
5
  import { ContractDeployer } from '@aztec/aztec.js/deployment';
4
6
  import { Fr } from '@aztec/aztec.js/fields';
5
- import type { AztecNode } from '@aztec/aztec.js/node';
7
+ import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
6
8
  import { encodeArgs, getContractArtifact, prettyPrintJSON } from '@aztec/cli/utils';
7
9
  import type { LogFn, Logger } from '@aztec/foundation/log';
8
10
  import { getAllFunctionAbis, getInitializer } from '@aztec/stdlib/abi';
9
11
  import { PublicKeys } from '@aztec/stdlib/keys';
12
+ import type { TxStatus } from '@aztec/stdlib/tx';
10
13
 
11
14
  import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
12
15
  import { CLIFeeArgs } from '../utils/options/fees.js';
@@ -28,10 +31,12 @@ export async function deploy(
28
31
  skipInitialization: boolean | undefined,
29
32
  wait: boolean,
30
33
  feeOpts: CLIFeeArgs,
34
+ waitForStatus: TxStatus,
31
35
  verbose: boolean,
32
36
  timeout: number = DEFAULT_TX_TIMEOUT_S,
33
37
  debugLogger: Logger,
34
38
  log: LogFn,
39
+ universal?: boolean,
35
40
  ) {
36
41
  const out: Record<string, any> = {};
37
42
  salt ??= Fr.random();
@@ -41,12 +46,7 @@ export async function deploy(
41
46
 
42
47
  // TODO(#12081): Add contractArtifact.noirVersion and check here (via Noir.lock)?
43
48
 
44
- const contractDeployer = new ContractDeployer(
45
- contractArtifact,
46
- wallet,
47
- publicKeys ?? PublicKeys.default(),
48
- initializer,
49
- );
49
+ const contractDeployer = new ContractDeployer(contractArtifact, wallet, initializer);
50
50
 
51
51
  let args = [];
52
52
  if (rawArgs.length > 0) {
@@ -58,22 +58,28 @@ export async function deploy(
58
58
  debugLogger.debug(`Encoded arguments: ${args.join(', ')}`);
59
59
  }
60
60
 
61
- const deploy = contractDeployer.deploy(...args);
61
+ const deployInteraction = contractDeployer.deploy(args, {
62
+ salt,
63
+ publicKeys: publicKeys ?? PublicKeys.default(),
64
+ ...(universal ? { universalDeploy: true as const } : {}),
65
+ });
62
66
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(node, wallet, deployer);
63
67
  const deployOpts: DeployOptions = {
64
68
  fee: { gasSettings, paymentMethod },
65
- from: deployer ?? AztecAddress.ZERO,
66
- contractAddressSalt: salt,
67
- universalDeploy: !deployer,
69
+ from: deployer ?? NO_FROM,
68
70
  skipClassPublication,
69
71
  skipInitialization,
70
72
  skipInstancePublication,
71
73
  };
72
74
 
73
- const { estimatedGas, stats } = await deploy.simulate({
75
+ const localStart = performance.now();
76
+ const sim = await deployInteraction.simulate({
74
77
  ...deployOpts,
75
- fee: { ...deployOpts.fee, estimateGas: true },
78
+ includeMetadata: true,
76
79
  });
80
+ // includeMetadata: true guarantees these fields are present
81
+ const estimatedGas = await wallet.estimateGasLimits(sim.gasUsed!);
82
+ const stats = sim.stats!;
77
83
 
78
84
  if (feeOpts.estimateOnly) {
79
85
  if (json) {
@@ -89,42 +95,63 @@ export async function deploy(
89
95
  };
90
96
  }
91
97
  } else {
92
- const tx = deploy.send(deployOpts);
93
98
  if (verbose) {
94
99
  printProfileResult(stats, log);
95
100
  }
96
101
 
97
- const txHash = await tx.getTxHash();
102
+ const instance = await deployInteraction.getInstance();
103
+ const address = instance.address;
104
+ const partialAddress = await deployInteraction.getPartialAddress();
105
+
106
+ const { txHash } = await deployInteraction.send({ ...deployOpts, wait: NO_WAIT });
107
+ const localTimeMs = performance.now() - localStart;
98
108
  debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`);
99
109
  out.hash = txHash;
100
- const { address, partialAddress } = deploy;
101
- const instance = await deploy.getInstance();
102
- if (!json) {
103
- log(`Contract deployed at ${address?.toString()}`);
104
- log(`Contract partial address ${(await partialAddress)?.toString()}`);
105
- log(`Contract init hash ${instance.initializationHash.toString()}`);
106
- log(`Deployment tx hash: ${txHash.toString()}`);
107
- log(`Deployment salt: ${salt.toString()}`);
108
- log(`Deployer: ${instance.deployer.toString()}`);
109
- } else {
110
- out.contract = {
111
- address: address?.toString(),
112
- partialAddress: (await partialAddress)?.toString(),
113
- initializationHash: instance.initializationHash.toString(),
114
- salt: salt.toString(),
115
- };
116
- }
110
+
117
111
  if (wait) {
118
- const deployed = await tx.wait({ timeout });
112
+ const nodeStart = performance.now();
113
+ const receipt = await waitForTx(node, txHash, { timeout, waitForStatus });
114
+ const nodeTimeMs = performance.now() - nodeStart;
115
+
116
+ if (!json) {
117
+ log(`Contract deployed at ${address.toString()}`);
118
+ log(`Contract partial address ${partialAddress.toString()}`);
119
+ log(`Contract init hash ${instance.initializationHash.toString()}`);
120
+ log(`Deployment tx hash: ${txHash.toString()}`);
121
+ log(`Deployment salt: ${salt.toString()}`);
122
+ log(`Deployer: ${instance.deployer.toString()}`);
123
+ log(`Transaction fee: ${receipt.transactionFee?.toString()}`);
124
+ log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
125
+ log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
126
+ } else {
127
+ out.contract = {
128
+ address: address.toString(),
129
+ partialAddress: partialAddress.toString(),
130
+ initializationHash: instance.initializationHash.toString(),
131
+ salt: salt.toString(),
132
+ transactionFee: receipt.transactionFee?.toString(),
133
+ };
134
+ }
135
+ } else {
119
136
  if (!json) {
120
- log(`Transaction fee: ${deployed.transactionFee?.toString()}`);
137
+ log(`Contract deployed at ${address.toString()}`);
138
+ log(`Contract partial address ${partialAddress.toString()}`);
139
+ log(`Contract init hash ${instance.initializationHash.toString()}`);
140
+ log(`Deployment tx hash: ${txHash.toString()}`);
141
+ log(`Deployment salt: ${salt.toString()}`);
142
+ log(`Deployer: ${instance.deployer.toString()}`);
121
143
  } else {
122
- out.contract.transactionFee = deployed.transactionFee?.toString();
144
+ out.contract = {
145
+ address: address.toString(),
146
+ partialAddress: partialAddress.toString(),
147
+ initializationHash: instance.initializationHash.toString(),
148
+ salt: salt.toString(),
149
+ };
123
150
  }
124
151
  }
125
152
  }
126
153
  if (json) {
127
154
  log(prettyPrintJSON(out));
128
155
  }
129
- return deploy.address;
156
+ return await deployInteraction.getAddress();
130
157
  }
@@ -1,8 +1,11 @@
1
+ import { NO_FROM } from '@aztec/aztec.js/account';
1
2
  import { AztecAddress } from '@aztec/aztec.js/addresses';
2
- import type { AztecNode } from '@aztec/aztec.js/node';
3
+ import { NO_WAIT } from '@aztec/aztec.js/contracts';
4
+ import { type AztecNode, waitForTx } from '@aztec/aztec.js/node';
3
5
  import type { DeployAccountOptions } from '@aztec/aztec.js/wallet';
4
6
  import { prettyPrintJSON } from '@aztec/cli/cli-utils';
5
7
  import type { LogFn, Logger } from '@aztec/foundation/log';
8
+ import type { TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
6
9
 
7
10
  import { DEFAULT_TX_TIMEOUT_S } from '../utils/cli_wallet_and_node_wrapper.js';
8
11
  import type { CLIFeeArgs } from '../utils/options/fees.js';
@@ -19,6 +22,7 @@ export async function deployAccount(
19
22
  publicDeploy: boolean,
20
23
  skipInitialization: boolean,
21
24
  feeOpts: CLIFeeArgs,
25
+ waitForStatus: TxStatus,
22
26
  json: boolean,
23
27
  verbose: boolean,
24
28
  debugLogger: Logger,
@@ -45,12 +49,12 @@ export async function deployAccount(
45
49
  log(`Init hash: ${initializationHash.toString()}`);
46
50
  }
47
51
 
48
- let tx;
49
- let txReceipt;
52
+ let txHash: TxHash | undefined;
53
+ let txReceipt: TxReceipt | undefined;
50
54
  const { paymentMethod, gasSettings } = await feeOpts.toUserFeeOptions(aztecNode, wallet, address);
51
55
 
52
56
  const delegatedDeployment = deployer && !account.address.equals(deployer);
53
- const from = delegatedDeployment ? deployer : AztecAddress.ZERO;
57
+ const from = delegatedDeployment ? deployer : NO_FROM;
54
58
 
55
59
  const deployAccountOpts: DeployAccountOptions = {
56
60
  skipClassPublication: !registerClass,
@@ -60,11 +64,15 @@ export async function deployAccount(
60
64
  fee: { paymentMethod, gasSettings },
61
65
  };
62
66
 
67
+ const localStart = performance.now();
63
68
  const deployMethod = await account.getDeployMethod();
64
- const { estimatedGas, stats } = await deployMethod.simulate({
69
+ const sim = await deployMethod.simulate({
65
70
  ...deployAccountOpts,
66
- fee: { ...deployAccountOpts.fee, estimateGas: true },
71
+ includeMetadata: true,
67
72
  });
73
+ // includeMetadata: true guarantees these fields are present
74
+ const estimatedGas = await wallet.estimateGasLimits(sim.gasUsed!);
75
+ const stats = sim.stats!;
68
76
 
69
77
  if (feeOpts.estimateOnly) {
70
78
  if (json) {
@@ -80,7 +88,14 @@ export async function deployAccount(
80
88
  };
81
89
  }
82
90
  } else {
83
- tx = deployMethod.send({
91
+ if (verbose) {
92
+ printProfileResult(stats, log);
93
+ }
94
+
95
+ if (!json) {
96
+ log(`\nWaiting for account contract deployment...`);
97
+ }
98
+ const sendOpts = {
84
99
  ...deployAccountOpts,
85
100
  fee: deployAccountOpts.fee
86
101
  ? {
@@ -88,31 +103,35 @@ export async function deployAccount(
88
103
  gasSettings: estimatedGas,
89
104
  }
90
105
  : undefined,
91
- });
92
- if (verbose) {
93
- printProfileResult(stats, log);
94
- }
106
+ };
107
+
108
+ ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT }));
109
+ const localTimeMs = performance.now() - localStart;
95
110
 
96
- const txHash = await tx.getTxHash();
97
- debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
98
- out.txHash = txHash;
99
111
  if (wait) {
100
- if (!json) {
101
- log(`\nWaiting for account contract deployment...`);
102
- }
103
- txReceipt = await tx.wait({ timeout: DEFAULT_TX_TIMEOUT_S });
112
+ const nodeStart = performance.now();
113
+ txReceipt = await waitForTx(aztecNode, txHash, { timeout: DEFAULT_TX_TIMEOUT_S, waitForStatus });
114
+ const nodeTimeMs = performance.now() - nodeStart;
115
+
104
116
  out.txReceipt = {
105
117
  status: txReceipt.status,
106
118
  transactionFee: txReceipt.transactionFee,
107
119
  };
120
+
121
+ if (!json) {
122
+ log(` Local processing time: ${(localTimeMs / 1000).toFixed(1)}s`);
123
+ log(` Node inclusion time: ${(nodeTimeMs / 1000).toFixed(1)}s`);
124
+ }
108
125
  }
126
+ debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`);
127
+ out.txHash = txHash;
109
128
  }
110
129
 
111
130
  if (json) {
112
131
  log(prettyPrintJSON(out));
113
132
  } else {
114
- if (tx) {
115
- log(`Deploy tx hash: ${(await tx.getTxHash()).toString()}`);
133
+ if (txHash) {
134
+ log(`Deploy tx hash: ${txHash.toString()}`);
116
135
  }
117
136
  if (txReceipt) {
118
137
  log(`Deploy tx fee: ${txReceipt.transactionFee}`);
@@ -0,0 +1,37 @@
1
+ import type { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import type { AztecNode } from '@aztec/aztec.js/node';
3
+ import { getFeeJuiceBalance } from '@aztec/aztec.js/utils';
4
+ import { prettyPrintJSON } from '@aztec/cli/cli-utils';
5
+ import type { LogFn } from '@aztec/foundation/log';
6
+
7
+ const FEE_JUICE_DECIMALS = 18;
8
+ const FEE_JUICE_UNIT = 10n ** BigInt(FEE_JUICE_DECIMALS);
9
+
10
+ /** Formats a raw FeeJuice balance (18 decimals) for human-readable display. */
11
+ function formatFeeJuice(raw: bigint, exact: boolean): string {
12
+ const whole = raw / FEE_JUICE_UNIT;
13
+ const fractional = raw % FEE_JUICE_UNIT;
14
+ const fracStr = fractional.toString().padStart(FEE_JUICE_DECIMALS, '0');
15
+ if (exact) {
16
+ return `${whole}.${fracStr} FJ`;
17
+ }
18
+ if (fractional === 0n) {
19
+ return `${whole} FJ`;
20
+ }
21
+ return `${whole}.${fracStr.replace(/0+$/, '')} FJ`;
22
+ }
23
+
24
+ export async function getFeeJuiceBalanceCmd(
25
+ node: AztecNode,
26
+ address: AztecAddress,
27
+ json: boolean,
28
+ exact: boolean,
29
+ log: LogFn,
30
+ ) {
31
+ const balance = await getFeeJuiceBalance(address, node);
32
+ if (json) {
33
+ log(prettyPrintJSON({ address: address.toString(), balance: balance.toString() }));
34
+ } else {
35
+ log(`Fee Juice balance for ${address.toString()}: ${formatFeeJuice(balance, exact)}`);
36
+ }
37
+ }