@aztec/end-to-end 0.0.1-commit.b6e433891 → 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 (163) hide show
  1. package/README.md +27 -0
  2. package/dest/bench/client_flows/benchmark.d.ts +15 -1
  3. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  4. package/dest/bench/client_flows/benchmark.js +17 -0
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts +2 -2
  6. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  7. package/dest/bench/client_flows/client_flows_benchmark.js +27 -38
  8. package/dest/bench/utils.d.ts +1 -1
  9. package/dest/bench/utils.d.ts.map +1 -1
  10. package/dest/bench/utils.js +2 -0
  11. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +5 -4
  12. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  13. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +14 -5
  14. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -4
  15. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  16. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +39 -7
  17. package/dest/e2e_deploy_contract/deploy_test.d.ts +3 -3
  18. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  19. package/dest/e2e_deploy_contract/deploy_test.js +2 -1
  20. package/dest/e2e_epochs/epochs_test.d.ts +23 -2
  21. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  22. package/dest/e2e_epochs/epochs_test.js +83 -12
  23. package/dest/e2e_fees/fees_test.d.ts +5 -2
  24. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  25. package/dest/e2e_fees/fees_test.js +32 -6
  26. package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
  27. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  28. package/dest/e2e_nested_contract/nested_contract_test.js +2 -1
  29. package/dest/e2e_p2p/inactivity_slash_test.d.ts +1 -1
  30. package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
  31. package/dest/e2e_p2p/inactivity_slash_test.js +4 -3
  32. package/dest/e2e_p2p/p2p_network.d.ts +10 -9
  33. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  34. package/dest/e2e_p2p/p2p_network.js +46 -27
  35. package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
  36. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  37. package/dest/e2e_p2p/reqresp/utils.js +29 -13
  38. package/dest/e2e_p2p/shared.d.ts +23 -14
  39. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  40. package/dest/e2e_p2p/shared.js +62 -57
  41. package/dest/e2e_token_contract/token_contract_test.d.ts +3 -3
  42. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  43. package/dest/e2e_token_contract/token_contract_test.js +4 -2
  44. package/dest/fixtures/authwit_proxy.d.ts +1 -1
  45. package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
  46. package/dest/fixtures/authwit_proxy.js +4 -0
  47. package/dest/fixtures/e2e_prover_test.d.ts +6 -5
  48. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  49. package/dest/fixtures/e2e_prover_test.js +9 -13
  50. package/dest/fixtures/fixtures.d.ts +70 -1
  51. package/dest/fixtures/fixtures.d.ts.map +1 -1
  52. package/dest/fixtures/fixtures.js +65 -0
  53. package/dest/fixtures/get_bb_config.d.ts +1 -1
  54. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  55. package/dest/fixtures/get_bb_config.js +5 -5
  56. package/dest/fixtures/ha_setup.d.ts +2 -2
  57. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  58. package/dest/fixtures/ha_setup.js +1 -1
  59. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  60. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  61. package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
  62. package/dest/fixtures/setup.d.ts +44 -11
  63. package/dest/fixtures/setup.d.ts.map +1 -1
  64. package/dest/fixtures/setup.js +157 -28
  65. package/dest/fixtures/setup_p2p_test.d.ts +10 -6
  66. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  67. package/dest/fixtures/setup_p2p_test.js +8 -8
  68. package/dest/fixtures/token_utils.d.ts +1 -1
  69. package/dest/fixtures/token_utils.d.ts.map +1 -1
  70. package/dest/fixtures/token_utils.js +2 -5
  71. package/dest/fixtures/utils.d.ts +2 -2
  72. package/dest/fixtures/utils.d.ts.map +1 -1
  73. package/dest/fixtures/utils.js +1 -1
  74. package/dest/forward-compatibility/wallet_rpc_client.d.ts +7 -0
  75. package/dest/forward-compatibility/wallet_rpc_client.d.ts.map +1 -0
  76. package/dest/forward-compatibility/wallet_rpc_client.js +15 -0
  77. package/dest/forward-compatibility/wallet_service.d.ts +3 -0
  78. package/dest/forward-compatibility/wallet_service.d.ts.map +1 -0
  79. package/dest/forward-compatibility/wallet_service.js +109 -0
  80. package/dest/install_legacy_contracts.d.cts +10 -0
  81. package/dest/install_legacy_contracts.d.cts.map +1 -0
  82. package/dest/legacy-jest-resolver.d.cts +3 -0
  83. package/dest/legacy-jest-resolver.d.cts.map +1 -0
  84. package/dest/shared/cross_chain_test_harness.d.ts +4 -2
  85. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  86. package/dest/shared/cross_chain_test_harness.js +9 -5
  87. package/dest/shared/gas_portal_test_harness.d.ts +8 -5
  88. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  89. package/dest/shared/gas_portal_test_harness.js +17 -8
  90. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  91. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  92. package/dest/shared/uniswap_l1_l2.js +50 -32
  93. package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
  94. package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
  95. package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
  96. package/dest/simulators/lending_simulator.d.ts +10 -3
  97. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  98. package/dest/simulators/lending_simulator.js +24 -12
  99. package/dest/spartan/setup_test_wallets.d.ts +1 -1
  100. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  101. package/dest/spartan/setup_test_wallets.js +72 -48
  102. package/dest/spartan/tx_metrics.d.ts +18 -4
  103. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  104. package/dest/spartan/tx_metrics.js +73 -20
  105. package/dest/spartan/utils/config.d.ts +9 -35
  106. package/dest/spartan/utils/config.d.ts.map +1 -1
  107. package/dest/spartan/utils/nodes.js +2 -2
  108. package/dest/test-wallet/test_wallet.d.ts +17 -8
  109. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  110. package/dest/test-wallet/test_wallet.js +134 -75
  111. package/dest/test-wallet/utils.d.ts +3 -3
  112. package/dest/test-wallet/utils.d.ts.map +1 -1
  113. package/dest/test-wallet/utils.js +2 -7
  114. package/dest/test-wallet/wallet_worker_script.js +7 -2
  115. package/dest/test-wallet/worker_wallet.d.ts +5 -4
  116. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  117. package/dest/test-wallet/worker_wallet.js +6 -2
  118. package/dest/test-wallet/worker_wallet_schema.d.ts +110 -224
  119. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  120. package/dest/test-wallet/worker_wallet_schema.js +14 -2
  121. package/package.json +46 -41
  122. package/src/bench/client_flows/benchmark.ts +19 -0
  123. package/src/bench/client_flows/client_flows_benchmark.ts +64 -53
  124. package/src/bench/utils.ts +2 -1
  125. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +24 -10
  126. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +49 -11
  127. package/src/e2e_deploy_contract/deploy_test.ts +3 -2
  128. package/src/e2e_epochs/epochs_test.ts +85 -9
  129. package/src/e2e_fees/fees_test.ts +37 -4
  130. package/src/e2e_nested_contract/nested_contract_test.ts +3 -1
  131. package/src/e2e_p2p/inactivity_slash_test.ts +4 -3
  132. package/src/e2e_p2p/p2p_network.ts +57 -39
  133. package/src/e2e_p2p/reqresp/utils.ts +30 -12
  134. package/src/e2e_p2p/shared.ts +60 -71
  135. package/src/e2e_token_contract/token_contract_test.ts +12 -2
  136. package/src/fixtures/authwit_proxy.ts +4 -0
  137. package/src/fixtures/e2e_prover_test.ts +15 -18
  138. package/src/fixtures/fixtures.ts +83 -0
  139. package/src/fixtures/get_bb_config.ts +7 -6
  140. package/src/fixtures/ha_setup.ts +4 -2
  141. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  142. package/src/fixtures/setup.ts +186 -33
  143. package/src/fixtures/setup_p2p_test.ts +13 -9
  144. package/src/fixtures/token_utils.ts +1 -4
  145. package/src/fixtures/utils.ts +2 -0
  146. package/src/forward-compatibility/wallet_rpc_client.ts +14 -0
  147. package/src/forward-compatibility/wallet_service.ts +104 -0
  148. package/src/guides/up_quick_start.sh +0 -2
  149. package/src/install_legacy_contracts.cjs +75 -0
  150. package/src/legacy-jest-resolver.cjs +112 -0
  151. package/src/shared/cross_chain_test_harness.ts +14 -4
  152. package/src/shared/gas_portal_test_harness.ts +20 -10
  153. package/src/shared/uniswap_l1_l2.ts +84 -40
  154. package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
  155. package/src/simulators/lending_simulator.ts +28 -12
  156. package/src/spartan/setup_test_wallets.ts +69 -43
  157. package/src/spartan/tx_metrics.ts +81 -23
  158. package/src/spartan/utils/nodes.ts +2 -2
  159. package/src/test-wallet/test_wallet.ts +160 -78
  160. package/src/test-wallet/utils.ts +3 -7
  161. package/src/test-wallet/wallet_worker_script.ts +5 -2
  162. package/src/test-wallet/worker_wallet.ts +10 -5
  163. package/src/test-wallet/worker_wallet_schema.ts +2 -2
@@ -1,6 +1,6 @@
1
1
  import type { AztecNode } from '@aztec/aztec.js/node';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
- import type { L2Block } from '@aztec/stdlib/block';
3
+ import type { BlockResponse } from '@aztec/stdlib/interfaces/client';
4
4
  import type { TopicType } from '@aztec/stdlib/p2p';
5
5
  import { Tx, type TxReceipt } from '@aztec/stdlib/tx';
6
6
 
@@ -134,9 +134,12 @@ export class ProvingMetrics {
134
134
 
135
135
  export type TxInclusionData = {
136
136
  txHash: string;
137
- sentAt: number;
138
- minedAt: number;
139
- attestedAt: number;
137
+ /** Wall-clock at client when the tx was submitted, in ms (Date.now()). */
138
+ sentAtMs: number;
139
+ /** Wall-clock at client when the block containing the tx first became visible, in ms (Date.now()). -1 if never observed. */
140
+ minedAtMs: number;
141
+ /** Reserved for future attestation-observed-at signal; -1 today. */
142
+ attestedAtMs: number;
140
143
  blocknumber: number;
141
144
  priorityFee: number;
142
145
  totalFee: number;
@@ -147,7 +150,7 @@ export type TxInclusionData = {
147
150
  export class TxInclusionMetrics {
148
151
  private data = new Map<string, TxInclusionData>();
149
152
  private groups = new Set<string>();
150
- private blocks = new Map<number, Promise<L2Block | undefined>>();
153
+ private blocks = new Map<number, Promise<BlockResponse<{ includeTransactions: true }> | undefined>>();
151
154
 
152
155
  private p2pGossipLatencyByTopic: Partial<Record<TopicType, { p50: number; p95: number }>> = {};
153
156
 
@@ -174,9 +177,9 @@ export class TxInclusionMetrics {
174
177
 
175
178
  this.data.set(txHash, {
176
179
  txHash,
177
- sentAt: Math.trunc(Date.now() / 1000),
178
- minedAt: -1,
179
- attestedAt: -1,
180
+ sentAtMs: Date.now(),
181
+ minedAtMs: -1,
182
+ attestedAtMs: -1,
180
183
  blocknumber: -1,
181
184
  priorityFee: Number(priorityFees.feePerDaGas + priorityFees.feePerL2Gas),
182
185
  totalFee: -1,
@@ -186,6 +189,28 @@ export class TxInclusionMetrics {
186
189
  this.groups.add(group);
187
190
  }
188
191
 
192
+ /**
193
+ * Stamp mined-at metadata for any tracked tx contained in this block, using
194
+ * `observedAtMs` (caller-supplied wall-clock at the moment they first saw the
195
+ * block). Idempotent: existing minedAtMs is preserved so the first observer
196
+ * wins (typically the block-watcher; recordMinedTx is a fallback).
197
+ */
198
+ observeBlockForMinedTxs(
199
+ blockNumber: number,
200
+ txHashes: ReadonlyArray<{ toString(): string }>,
201
+ observedAtMs: number,
202
+ ): void {
203
+ txHashes.forEach((txHash, position) => {
204
+ const data = this.data.get(txHash.toString());
205
+ if (!data || data.minedAtMs !== -1) {
206
+ return;
207
+ }
208
+ data.blocknumber = blockNumber;
209
+ data.minedAtMs = observedAtMs;
210
+ data.positionInBlock = position;
211
+ });
212
+ }
213
+
189
214
  async recordMinedTx(txReceipt: TxReceipt): Promise<void> {
190
215
  const { txHash, blockNumber } = txReceipt;
191
216
  if (!txReceipt.isMined() || !txReceipt.hasExecutionSucceeded() || !blockNumber) {
@@ -197,26 +222,43 @@ export class TxInclusionMetrics {
197
222
  return;
198
223
  }
199
224
 
200
- if (!this.blocks.has(blockNumber)) {
201
- this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber));
202
- }
203
-
204
- const block = await this.blocks.get(blockNumber)!;
205
- if (!block) {
206
- this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
207
- return;
208
- }
209
225
  const data = this.data.get(txHash.toString());
210
226
  if (!data) {
211
227
  const message = `Missing sent tx record for mined tx ${txHash.toString()}`;
212
228
  this.logger?.warn(message, { txHash: txHash.toString(), blockNumber });
213
229
  throw new Error(message);
214
230
  }
215
- data.blocknumber = blockNumber;
216
- data.minedAt = Number(block.header.globalVariables.timestamp);
217
- data.attestedAt = -1;
218
231
  data.totalFee = Number(txReceipt.transactionFee ?? 0n);
219
- data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
232
+
233
+ // Fallback path for txs the block-watcher missed (e.g. observed only after
234
+ // the watcher stopped). Stamp with the block's L2 slot timestamp; this is
235
+ // earlier than the true client-observed time by attestation+propagation
236
+ // lag, but it's the only deterministic timestamp available post-hoc.
237
+ if (data.minedAtMs === -1) {
238
+ if (!this.blocks.has(blockNumber)) {
239
+ this.blocks.set(blockNumber, this.aztecNode.getBlock(blockNumber, { includeTransactions: true }));
240
+ }
241
+ const block = await this.blocks.get(blockNumber)!;
242
+ if (!block) {
243
+ this.logger?.warn('Failed to load block for mined tx receipt', { txHash: txHash.toString(), blockNumber });
244
+ return;
245
+ }
246
+ data.blocknumber = blockNumber;
247
+ data.minedAtMs = Number(block.header.globalVariables.timestamp) * 1000;
248
+ data.positionInBlock = block.body.txEffects.findIndex(txEffect => txEffect.txHash.equals(txHash));
249
+ }
250
+ }
251
+
252
+ /** Per-tx inclusion records for a group. Used to serialise out for downstream tooling. */
253
+ getInclusionRecords(group?: string): TxInclusionData[] {
254
+ const out: TxInclusionData[] = [];
255
+ for (const tx of this.data.values()) {
256
+ if (group !== undefined && tx.group !== group) {
257
+ continue;
258
+ }
259
+ out.push({ ...tx });
260
+ }
261
+ return out;
220
262
  }
221
263
 
222
264
  public inclusionTimeInSeconds(group: string): {
@@ -229,12 +271,28 @@ export class TxInclusionMetrics {
229
271
  p99: number;
230
272
  } {
231
273
  const histogram = createHistogram({});
274
+ let nonPositive = 0;
232
275
  for (const tx of this.data.values()) {
233
- if (!tx.blocknumber || tx.group !== group || tx.minedAt === -1) {
276
+ if (!tx.blocknumber || tx.group !== group || tx.minedAtMs === -1) {
234
277
  continue;
235
278
  }
236
279
 
237
- histogram.record(tx.minedAt - tx.sentAt);
280
+ // Both timestamps are client wall-clock (ms). A negative delta should be
281
+ // impossible since the watcher stamps minedAtMs strictly after sentAtMs,
282
+ // but the fallback path (recordMinedTx via L2 slot timestamp) can stamp
283
+ // earlier than sentAtMs. perf_hooks.createHistogram rejects <=0; skip
284
+ // those instead of crashing.
285
+ const deltaMs = tx.minedAtMs - tx.sentAtMs;
286
+ if (deltaMs <= 0) {
287
+ nonPositive++;
288
+ continue;
289
+ }
290
+ // Histogram is recorded in seconds (rounded) to match the existing
291
+ // toGithubActionBenchmarkJSON output unit; per-tx records carry the raw ms.
292
+ histogram.record(Math.max(1, Math.round(deltaMs / 1000)));
293
+ }
294
+ if (nonPositive > 0) {
295
+ this.logger?.debug(`Dropped ${nonPositive} tx inclusion samples with non-positive delta`, { group });
238
296
  }
239
297
 
240
298
  if (histogram.count === 0) {
@@ -71,7 +71,7 @@ export async function waitForProvenToAdvance(
71
71
  // Get current proven block number
72
72
  let initialProvenBlock: number;
73
73
  try {
74
- const tips = await node.getL2Tips();
74
+ const tips = await node.getChainTips();
75
75
  initialProvenBlock = Number(tips.proven.block.number);
76
76
  log.info(`Current proven block: ${initialProvenBlock}. Waiting for it to increase...`);
77
77
  } catch (err) {
@@ -82,7 +82,7 @@ export async function waitForProvenToAdvance(
82
82
  await retryUntil(
83
83
  async () => {
84
84
  try {
85
- const tips = await node.getL2Tips();
85
+ const tips = await node.getChainTips();
86
86
  const currentProvenBlock = Number(tips.proven.block.number);
87
87
  const proposedBlock = Number(tips.proposed.number);
88
88
 
@@ -1,7 +1,9 @@
1
1
  import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa';
2
2
  import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
3
- import { StubAccountContractArtifact, createStubAccount } from '@aztec/accounts/stub';
4
- import { type Account, type AccountContract, SignerlessAccount } from '@aztec/aztec.js/account';
3
+ import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec/accounts/stub/ecdsa';
4
+ import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub/schnorr';
5
+ import { type Account, type AccountContract, NO_FROM } from '@aztec/aztec.js/account';
6
+ import type { CompleteAddress } from '@aztec/aztec.js/addresses';
5
7
  import {
6
8
  type CallIntent,
7
9
  type ContractFunctionInteractionCallIntent,
@@ -13,7 +15,9 @@ import {
13
15
  } from '@aztec/aztec.js/authorization';
14
16
  import type { AztecNode } from '@aztec/aztec.js/node';
15
17
  import { AccountManager, type SendOptions } from '@aztec/aztec.js/wallet';
18
+ import { TxSimulationResultWithAppOffset } from '@aztec/aztec.js/wallet';
16
19
  import type { DefaultAccountEntrypointOptions } from '@aztec/entrypoints/account';
20
+ import { DefaultEntrypoint } from '@aztec/entrypoints/default';
17
21
  import { Fq, Fr } from '@aztec/foundation/curves/bn254';
18
22
  import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
19
23
  import type { NotesFilter } from '@aztec/pxe/client/lazy';
@@ -21,13 +25,22 @@ import { type PXEConfig, getPXEConfig } from '@aztec/pxe/config';
21
25
  import { PXE, type PXECreationOptions, createPXE } from '@aztec/pxe/server';
22
26
  import { AuthWitness } from '@aztec/stdlib/auth-witness';
23
27
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
24
- import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
28
+ import { getContractClassFromArtifact } from '@aztec/stdlib/contract';
25
29
  import { deriveSigningKey } from '@aztec/stdlib/keys';
26
30
  import type { NoteDao } from '@aztec/stdlib/note';
27
- import type { BlockHeader, SimulationOverrides, TxHash, TxReceipt, TxSimulationResult } from '@aztec/stdlib/tx';
31
+ import {
32
+ type BlockHeader,
33
+ type ContractOverrides,
34
+ SimulationOverrides,
35
+ type TxExecutionRequest,
36
+ type TxHash,
37
+ type TxReceipt,
38
+ } from '@aztec/stdlib/tx';
28
39
  import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
29
40
  import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
41
+ import type { AccountType } from '@aztec/wallets/embedded';
30
42
 
43
+ import { DEFAULT_MIN_FEE_PADDING } from '../fixtures/fixtures.js';
31
44
  import { AztecNodeProxy, ProvenTx } from './utils.js';
32
45
 
33
46
  /**
@@ -36,6 +49,7 @@ import { AztecNodeProxy, ProvenTx } from './utils.js';
36
49
  export interface AccountData {
37
50
  secret: Fr;
38
51
  salt: Fr;
52
+ type?: AccountType;
39
53
  contract: AccountContract;
40
54
  }
41
55
 
@@ -50,6 +64,7 @@ export class TestWallet extends BaseWallet {
50
64
  private readonly nodeRef: AztecNodeProxy,
51
65
  ) {
52
66
  super(pxe, nodeRef);
67
+ this.minFeePadding = DEFAULT_MIN_FEE_PADDING;
53
68
  }
54
69
 
55
70
  static async create(
@@ -63,7 +78,9 @@ export class TestWallet extends BaseWallet {
63
78
  ...overridePXEConfig,
64
79
  });
65
80
  const pxe = await createPXE(nodeRef, pxeConfig, options);
66
- return new TestWallet(pxe, nodeRef);
81
+ const wallet = new TestWallet(pxe, nodeRef);
82
+ await wallet.initStubClasses();
83
+ return wallet;
67
84
  }
68
85
 
69
86
  /**
@@ -76,53 +93,95 @@ export class TestWallet extends BaseWallet {
76
93
 
77
94
  createSchnorrAccount(secret: Fr, salt: Fr, signingKey?: Fq): Promise<AccountManager> {
78
95
  signingKey = signingKey ?? deriveSigningKey(secret);
79
- const accountData = {
80
- secret,
81
- salt,
82
- contract: new SchnorrAccountContract(signingKey),
83
- };
84
- return this.createAccount(accountData);
96
+ return this.createAccount({ secret, salt, type: 'schnorr', contract: new SchnorrAccountContract(signingKey) });
85
97
  }
86
98
 
87
99
  createECDSARAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
88
- const accountData = {
100
+ return this.createAccount({
89
101
  secret,
90
102
  salt,
103
+ type: 'ecdsasecp256r1',
91
104
  contract: new EcdsaRAccountContract(signingKey),
92
- };
93
- return this.createAccount(accountData);
105
+ });
94
106
  }
95
107
 
96
108
  createECDSAKAccount(secret: Fr, salt: Fr, signingKey: Buffer): Promise<AccountManager> {
97
- const accountData = {
109
+ return this.createAccount({
98
110
  secret,
99
111
  salt,
112
+ type: 'ecdsasecp256k1',
100
113
  contract: new EcdsaKAccountContract(signingKey),
101
- };
102
- return this.createAccount(accountData);
114
+ });
103
115
  }
104
116
 
105
- async getFakeAccountDataFor(address: AztecAddress) {
106
- const originalAccount = await this.getAccountFromAddress(address);
107
- if (originalAccount instanceof SignerlessAccount) {
108
- throw new Error(`Cannot create fake account data for SignerlessAccount at address: ${address}`);
109
- }
110
- const originalAddress = (originalAccount as Account).getCompleteAddress();
111
- const contractInstance = await this.pxe.getContractInstance(originalAddress.address);
112
- if (!contractInstance) {
113
- throw new Error(`No contract instance found for address: ${originalAddress.address}`);
117
+ // Stub class ids, populated on wallet startup
118
+ // to avoid redundant work per simulation
119
+ private stubClassIds = new Map<AccountType, Fr>();
120
+
121
+ /**
122
+ * Hashes and registers the stub class for every supported account type with PXE, populating
123
+ * stubClassIds. Called on wallet initialization.
124
+ */
125
+ private async initStubClasses(): Promise<void> {
126
+ const { id: schnorrClassId } = await getContractClassFromArtifact(StubSchnorrAccountContractArtifact);
127
+ await this.pxe.registerContractClass(StubSchnorrAccountContractArtifact);
128
+
129
+ // ecdsa stubs share the same class id
130
+ const { id: ecdsaClassId } = await getContractClassFromArtifact(StubEcdsaAccountContractArtifact);
131
+ await this.pxe.registerContractClass(StubEcdsaAccountContractArtifact);
132
+
133
+ this.stubClassIds.set('schnorr', schnorrClassId);
134
+ this.stubClassIds.set('ecdsasecp256k1', ecdsaClassId);
135
+ this.stubClassIds.set('ecdsasecp256r1', ecdsaClassId);
136
+ }
137
+
138
+ /**
139
+ * Builds contract overrides for all provided addresses by replacing their account contracts with stub implementations.
140
+ */
141
+ protected async buildAccountOverrides(addresses: AztecAddress[]): Promise<ContractOverrides> {
142
+ const accounts = await this.getAccounts();
143
+ const contracts: ContractOverrides = {};
144
+
145
+ const filtered = accounts.filter(acc => addresses.some(addr => addr.equals(acc.item)));
146
+
147
+ for (const account of filtered) {
148
+ const address = account.item;
149
+ const originalAccount = await this.getAccountFromAddress(address);
150
+ const completeAddress = originalAccount.getCompleteAddress();
151
+ const contractInstance = await this.pxe.getContractInstance(completeAddress.address);
152
+ if (!contractInstance) {
153
+ throw new Error(
154
+ `No contract instance found for address: ${completeAddress.address} during account override building. This is a bug!`,
155
+ );
156
+ }
157
+
158
+ const type = this.getTypeFor(address);
159
+ const stubClassId = this.stubClassIds.get(type);
160
+ if (!stubClassId) {
161
+ throw new Error(
162
+ `Stub class for account type '${type}' was not registered at wallet init. This is a bug — initStubClasses should cover every supported AccountType.`,
163
+ );
164
+ }
165
+
166
+ contracts[address.toString()] = {
167
+ instance: { ...contractInstance, currentContractClassId: stubClassId },
168
+ };
114
169
  }
115
- const stubAccount = createStubAccount(originalAddress);
116
- const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {
117
- salt: Fr.random(),
118
- });
119
- return {
120
- account: stubAccount,
121
- instance,
122
- artifact: StubAccountContractArtifact,
123
- };
170
+
171
+ return contracts;
172
+ }
173
+
174
+ protected accounts: Map<string, { account: Account; type: AccountType }> = new Map();
175
+
176
+ private getTypeFor(address: AztecAddress): AccountType {
177
+ return this.accounts.get(address.toString())?.type ?? 'schnorr';
178
+ }
179
+
180
+ private getStubAccountFor(address: AztecAddress, completeAddress: CompleteAddress) {
181
+ return this.getTypeFor(address) === 'schnorr'
182
+ ? createStubSchnorrAccount(completeAddress)
183
+ : createStubEcdsaAccount(completeAddress);
124
184
  }
125
- protected accounts: Map<string, Account> = new Map();
126
185
 
127
186
  /**
128
187
  * Controls how the test wallet simulates transactions:
@@ -137,41 +196,40 @@ export class TestWallet extends BaseWallet {
137
196
  }
138
197
 
139
198
  setMinFeePadding(value?: number) {
140
- this.minFeePadding = value ?? 0.5;
199
+ this.minFeePadding = value ?? DEFAULT_MIN_FEE_PADDING;
141
200
  }
142
201
 
143
202
  protected getAccountFromAddress(address: AztecAddress): Promise<Account> {
144
- let account: Account | undefined;
145
- if (address.equals(AztecAddress.ZERO)) {
146
- account = new SignerlessAccount();
147
- } else {
148
- account = this.accounts.get(address?.toString() ?? '');
149
- }
203
+ const entry = this.accounts.get(address?.toString() ?? '');
150
204
 
151
- if (!account) {
205
+ if (!entry) {
152
206
  throw new Error(`Account not found in wallet for address: ${address}`);
153
207
  }
154
208
 
155
- return Promise.resolve(account);
209
+ return Promise.resolve(entry.account);
156
210
  }
157
211
 
158
212
  getAccounts() {
159
- return Promise.resolve(Array.from(this.accounts.values()).map(acc => ({ alias: '', item: acc.getAddress() })));
213
+ return Promise.resolve(
214
+ Array.from(this.accounts.values()).map(entry => ({ alias: '', item: entry.account.getAddress() })),
215
+ );
160
216
  }
161
217
 
162
218
  async createAccount(accountData?: AccountData): Promise<AccountManager> {
163
219
  const secret = accountData?.secret ?? Fr.random();
164
220
  const salt = accountData?.salt ?? Fr.random();
221
+ const type = accountData?.type ?? 'schnorr';
165
222
  const contract = accountData?.contract ?? new SchnorrAccountContract(GrumpkinScalar.random());
166
223
 
167
- const accountManager = await AccountManager.create(this, secret, contract, salt);
224
+ const accountManager = await AccountManager.create(this, secret, contract, { salt });
168
225
 
169
226
  const instance = accountManager.getInstance();
170
227
  const artifact = await contract.getContractArtifact();
171
228
 
172
229
  await this.registerContract(instance, artifact, secret);
173
230
 
174
- this.accounts.set(accountManager.address.toString(), await accountManager.getAccount());
231
+ const address = accountManager.address.toString();
232
+ this.accounts.set(address, { account: await accountManager.getAccount(), type });
175
233
 
176
234
  return accountManager;
177
235
  }
@@ -217,53 +275,77 @@ export class TestWallet extends BaseWallet {
217
275
  protected override async simulateViaEntrypoint(
218
276
  executionPayload: ExecutionPayload,
219
277
  opts: SimulateViaEntrypointOptions,
220
- ): Promise<TxSimulationResult> {
221
- const { from, feeOptions, scopes, skipTxValidation, skipFeeEnforcement } = opts;
278
+ ): Promise<TxSimulationResultWithAppOffset> {
279
+ const { from, feeOptions, additionalScopes, skipTxValidation, skipFeeEnforcement, sendMessagesAs } = opts;
280
+ const scopes = this.scopesFrom(from, additionalScopes);
222
281
  const skipKernels = this.simulationMode !== 'full';
223
- const useOverride = this.simulationMode === 'kernelless-override' && !from.equals(AztecAddress.ZERO);
224
-
225
- let overrides: SimulationOverrides | undefined;
226
- let fromAccount: Account;
227
- if (useOverride) {
228
- const { account, instance, artifact } = await this.getFakeAccountDataFor(from);
229
- fromAccount = account;
230
- overrides = {
231
- contracts: { [from.toString()]: { instance, artifact } },
232
- };
233
- } else {
234
- fromAccount = await this.getAccountFromAddress(from);
235
- }
282
+ const useOverride = this.simulationMode === 'kernelless-override';
236
283
 
237
284
  const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload();
238
- const executionOptions: DefaultAccountEntrypointOptions = {
239
- txNonce: Fr.random(),
240
- cancellable: this.cancellableTransactions,
241
- feePaymentMethodOptions: feeOptions.accountFeePaymentMethodOptions,
242
- };
243
285
  const finalExecutionPayload = feeExecutionPayload
244
286
  ? mergeExecutionPayloads([feeExecutionPayload, executionPayload])
245
287
  : executionPayload;
246
288
  const chainInfo = await this.getChainInfo();
247
- const txRequest = await fromAccount.createTxExecutionRequest(
248
- finalExecutionPayload,
249
- feeOptions.gasSettings,
250
- chainInfo,
251
- executionOptions,
252
- );
253
- return this.pxe.simulateTx(txRequest, {
289
+
290
+ let overrides = opts.overrides;
291
+ let txRequest: TxExecutionRequest;
292
+ if (useOverride) {
293
+ const accountOverrides = await this.buildAccountOverrides(scopes);
294
+ overrides = new SimulationOverrides({
295
+ publicStorage: overrides?.publicStorage,
296
+ contracts: { ...overrides?.contracts, ...accountOverrides },
297
+ });
298
+ }
299
+
300
+ if (from === NO_FROM) {
301
+ const entrypoint = new DefaultEntrypoint();
302
+ txRequest = await entrypoint.createTxExecutionRequest(finalExecutionPayload, feeOptions.gasSettings, chainInfo);
303
+ } else {
304
+ let fromAccount: Account;
305
+ if (useOverride) {
306
+ const originalAccount = await this.getAccountFromAddress(from);
307
+ fromAccount = this.getStubAccountFor(from, originalAccount.getCompleteAddress());
308
+ } else {
309
+ fromAccount = await this.getAccountFromAddress(from);
310
+ }
311
+ const executionOptions: DefaultAccountEntrypointOptions = {
312
+ txNonce: Fr.random(),
313
+ cancellable: this.cancellableTransactions,
314
+ // If from is an address, feeOptions include the way the account contract should handle the fee payment
315
+ feePaymentMethodOptions: feeOptions.accountFeePaymentMethodOptions!,
316
+ };
317
+ txRequest = await fromAccount.createTxExecutionRequest(
318
+ finalExecutionPayload,
319
+ feeOptions.gasSettings,
320
+ chainInfo,
321
+ executionOptions,
322
+ );
323
+ }
324
+
325
+ const result = await this.pxe.simulateTx(txRequest, {
254
326
  simulatePublic: true,
255
327
  skipKernels,
256
328
  skipFeeEnforcement,
257
329
  skipTxValidation,
258
330
  overrides,
259
331
  scopes,
332
+ senderForTags: this.senderForTagsFrom(from, sendMessagesAs),
260
333
  });
334
+ const appCallOffset = await this.computeAppCallOffset(from, feeOptions);
335
+ return TxSimulationResultWithAppOffset.fromResultAndOffset(result, appCallOffset);
261
336
  }
262
337
 
263
338
  async proveTx(exec: ExecutionPayload, opts: Omit<SendOptions, 'wait'>): Promise<ProvenTx> {
264
- const fee = await this.completeFeeOptions(opts.from, exec.feePayer, opts.fee?.gasSettings);
339
+ const fee = await this.completeFeeOptions({
340
+ from: opts.from,
341
+ feePayer: exec.feePayer,
342
+ gasSettings: opts.fee?.gasSettings,
343
+ });
265
344
  const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(exec, opts.from, fee);
266
- const txProvingResult = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes));
345
+ const txProvingResult = await this.pxe.proveTx(txRequest, {
346
+ scopes: this.scopesFrom(opts.from, opts.additionalScopes),
347
+ senderForTags: this.senderForTagsFrom(opts.from, opts.sendMessagesAs),
348
+ });
267
349
  return new ProvenTx(
268
350
  this.aztecNode,
269
351
  await txProvingResult.toTx(),
@@ -285,7 +367,7 @@ export class TestWallet extends BaseWallet {
285
367
  }
286
368
 
287
369
  sync(): Promise<void> {
288
- return this.pxe.debug.sync();
370
+ return this.pxe.sync();
289
371
  }
290
372
 
291
373
  stop(): Promise<void> {
@@ -1,4 +1,5 @@
1
1
  import {
2
+ BatchCall,
2
3
  ContractFunctionInteraction,
3
4
  DeployMethod,
4
5
  type DeployOptions,
@@ -62,15 +63,10 @@ export class ProvenTx extends Tx {
62
63
 
63
64
  export async function proveInteraction(
64
65
  wallet: TestWallet,
65
- interaction: ContractFunctionInteraction | DeployMethod,
66
+ interaction: ContractFunctionInteraction | DeployMethod | BatchCall,
66
67
  options: SendInteractionOptions | DeployOptions,
67
68
  ) {
68
- let execPayload;
69
- if (interaction instanceof DeployMethod) {
70
- execPayload = await interaction.request(interaction.convertDeployOptionsToRequestOptions(options));
71
- } else {
72
- execPayload = await interaction.request(options);
73
- }
69
+ const execPayload = await interaction.request(options);
74
70
  return wallet.proveTx(execPayload, toSendOptions(options));
75
71
  }
76
72
 
@@ -1,9 +1,10 @@
1
1
  import { createAztecNodeClient } from '@aztec/aztec.js/node';
2
2
  import type { SendOptions } from '@aztec/aztec.js/wallet';
3
+ import { BackendType, BarretenbergSync } from '@aztec/bb.js';
3
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
4
5
  import { createLogger } from '@aztec/foundation/log';
5
6
  import type { ApiSchema, Fr } from '@aztec/foundation/schemas';
6
- import { parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
7
+ import { getSchemaParameters, parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
7
8
  import { NodeListener, TransportServer } from '@aztec/foundation/transport';
8
9
  import { ExecutionPayload, Tx } from '@aztec/stdlib/tx';
9
10
 
@@ -19,6 +20,8 @@ try {
19
20
 
20
21
  logger.info('Initializing worker wallet', { nodeUrl });
21
22
  const node = createAztecNodeClient(nodeUrl);
23
+ // Worker sync bb use is limited to crypto and proof serialization helpers.
24
+ await BarretenbergSync.initSingleton({ backend: BackendType.Wasm });
22
25
  const wallet = await TestWallet.create(node, pxeConfig);
23
26
  logger.info('Worker wallet initialized');
24
27
 
@@ -46,7 +49,7 @@ try {
46
49
  throw new Error(`Unknown method: ${msg.fn}`);
47
50
  }
48
51
  const jsonParams = JSON.parse(msg.args) as unknown[];
49
- const args: any[] = await parseWithOptionals(jsonParams, schema[msg.fn].parameters());
52
+ const args: any[] = await parseWithOptionals(jsonParams, getSchemaParameters(schema[msg.fn]));
50
53
  // we have to erase the fn type in order to be able to spread ...args
51
54
  const handler: ((...args: any[]) => Promise<any>) | undefined =
52
55
  msg.fn in customMethods ? customMethods[msg.fn as keyof typeof customMethods] : undefined;
@@ -13,6 +13,7 @@ import type {
13
13
  ProfileOptions,
14
14
  SendOptions,
15
15
  SimulateOptions,
16
+ TxSimulationResultWithAppOffset,
16
17
  Wallet,
17
18
  WalletCapabilities,
18
19
  } from '@aztec/aztec.js/wallet';
@@ -21,7 +22,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
21
22
  import { jsonStringify } from '@aztec/foundation/json-rpc';
22
23
  import { createLogger } from '@aztec/foundation/log';
23
24
  import { promiseWithResolvers } from '@aztec/foundation/promise';
24
- import type { ApiSchema } from '@aztec/foundation/schemas';
25
+ import { type ApiSchema, getSchemaReturnType } from '@aztec/foundation/schemas';
25
26
  import { sleep } from '@aztec/foundation/sleep';
26
27
  import { NodeConnector, TransportClient } from '@aztec/foundation/transport';
27
28
  import type { PXEConfig } from '@aztec/pxe/config';
@@ -29,7 +30,7 @@ import type { ContractArtifact, EventMetadataDefinition, FunctionCall } from '@a
29
30
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
30
31
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
31
32
  import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
32
- import type { ExecutionPayload, TxProfileResult, TxSimulationResult, UtilityExecutionResult } from '@aztec/stdlib/tx';
33
+ import type { ExecutionPayload, TxProfileResult, UtilityExecutionResult } from '@aztec/stdlib/tx';
33
34
  import { Tx } from '@aztec/stdlib/tx';
34
35
 
35
36
  import { Worker } from 'worker_threads';
@@ -70,7 +71,7 @@ export class WorkerWallet implements Wallet {
70
71
  env: {
71
72
  ...parentEnv,
72
73
  ...(process.stderr.isTTY || process.env.FORCE_COLOR ? { FORCE_COLOR: '1' } : {}),
73
- LOG_LEVEL: process.env.WORKER_LOG_LEVEL ?? 'warn',
74
+ LOG_LEVEL: process.env.WORKER_LOG_LEVEL ?? 'info',
74
75
  },
75
76
  });
76
77
 
@@ -123,7 +124,7 @@ export class WorkerWallet implements Wallet {
123
124
  private async call(fn: string, ...args: any[]): Promise<any> {
124
125
  const resultJson = await this.callRaw(fn, ...args);
125
126
  const methodSchema = (WorkerWalletSchema as ApiSchema)[fn];
126
- return methodSchema.returnType().parseAsync(JSON.parse(resultJson));
127
+ return getSchemaReturnType(methodSchema).parseAsync(JSON.parse(resultJson));
127
128
  }
128
129
 
129
130
  getChainInfo(): Promise<ChainInfo> {
@@ -165,7 +166,11 @@ export class WorkerWallet implements Wallet {
165
166
  return this.call('registerContract', instance, artifact, secretKey);
166
167
  }
167
168
 
168
- simulateTx(exec: ExecutionPayload, opts: SimulateOptions): Promise<TxSimulationResult> {
169
+ registerContractClass(artifact: ContractArtifact): Promise<void> {
170
+ return this.call('registerContractClass', artifact);
171
+ }
172
+
173
+ simulateTx(exec: ExecutionPayload, opts: SimulateOptions): Promise<TxSimulationResultWithAppOffset> {
169
174
  return this.call('simulateTx', exec, opts);
170
175
  }
171
176
 
@@ -8,6 +8,6 @@ import { z } from 'zod';
8
8
  /** Schema for the WorkerWallet API — extends WalletSchema with proveTx and registerAccount. */
9
9
  export const WorkerWalletSchema = {
10
10
  ...WalletSchema,
11
- proveTx: z.function().args(ExecutionPayloadSchema, SendOptionsSchema).returns(Tx.schema),
12
- registerAccount: z.function().args(schemas.Fr, schemas.Fr).returns(AztecAddress.schema),
11
+ proveTx: z.function({ input: z.tuple([ExecutionPayloadSchema, SendOptionsSchema]), output: Tx.schema }),
12
+ registerAccount: z.function({ input: z.tuple([schemas.Fr, schemas.Fr]), output: AztecAddress.schema }),
13
13
  };