@aztec/end-to-end 0.0.1-commit.e0f15ab9b → 0.0.1-commit.e304674f1

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 (53) hide show
  1. package/README.md +27 -0
  2. package/dest/bench/client_flows/client_flows_benchmark.d.ts +1 -1
  3. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  4. package/dest/bench/client_flows/client_flows_benchmark.js +11 -26
  5. package/dest/e2e_epochs/epochs_test.js +3 -3
  6. package/dest/e2e_fees/fees_test.js +1 -1
  7. package/dest/e2e_p2p/inactivity_slash_test.js +3 -3
  8. package/dest/e2e_p2p/p2p_network.d.ts +5 -7
  9. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  10. package/dest/e2e_p2p/p2p_network.js +8 -12
  11. package/dest/e2e_p2p/reqresp/utils.js +1 -1
  12. package/dest/e2e_p2p/shared.d.ts +5 -7
  13. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  14. package/dest/e2e_p2p/shared.js +16 -42
  15. package/dest/fixtures/authwit_proxy.d.ts +1 -1
  16. package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
  17. package/dest/fixtures/authwit_proxy.js +4 -0
  18. package/dest/fixtures/e2e_prover_test.d.ts +1 -1
  19. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  20. package/dest/fixtures/e2e_prover_test.js +4 -7
  21. package/dest/fixtures/setup.d.ts +5 -4
  22. package/dest/fixtures/setup.d.ts.map +1 -1
  23. package/dest/fixtures/setup.js +7 -6
  24. package/dest/fixtures/setup_p2p_test.d.ts +6 -6
  25. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  26. package/dest/fixtures/setup_p2p_test.js +8 -8
  27. package/dest/fixtures/token_utils.d.ts +1 -1
  28. package/dest/fixtures/token_utils.d.ts.map +1 -1
  29. package/dest/fixtures/token_utils.js +2 -5
  30. package/dest/legacy-jest-resolver.d.cts +3 -0
  31. package/dest/legacy-jest-resolver.d.cts.map +1 -0
  32. package/dest/spartan/setup_test_wallets.d.ts +1 -1
  33. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  34. package/dest/spartan/setup_test_wallets.js +58 -40
  35. package/dest/test-wallet/test_wallet.d.ts +6 -7
  36. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  37. package/dest/test-wallet/test_wallet.js +36 -27
  38. package/package.json +40 -39
  39. package/src/bench/client_flows/client_flows_benchmark.ts +29 -21
  40. package/src/e2e_epochs/epochs_test.ts +3 -3
  41. package/src/e2e_fees/fees_test.ts +1 -1
  42. package/src/e2e_p2p/inactivity_slash_test.ts +3 -3
  43. package/src/e2e_p2p/p2p_network.ts +16 -23
  44. package/src/e2e_p2p/reqresp/utils.ts +1 -1
  45. package/src/e2e_p2p/shared.ts +16 -57
  46. package/src/fixtures/authwit_proxy.ts +4 -0
  47. package/src/fixtures/e2e_prover_test.ts +7 -6
  48. package/src/fixtures/setup.ts +12 -13
  49. package/src/fixtures/setup_p2p_test.ts +9 -9
  50. package/src/fixtures/token_utils.ts +1 -4
  51. package/src/legacy-jest-resolver.cjs +135 -0
  52. package/src/spartan/setup_test_wallets.ts +59 -36
  53. package/src/test-wallet/test_wallet.ts +53 -28
@@ -104,15 +104,13 @@ export class FullProverTest {
104
104
  await publicDeployAccounts(this.wallet, this.accounts.slice(0, 2));
105
105
 
106
106
  this.logger.info('Applying base setup: deploying token contract');
107
- const {
108
- receipt: { contract: asset, instance },
109
- } = await TokenContract.deploy(
107
+ const { contract: asset, instance } = await TokenContract.deploy(
110
108
  this.wallet,
111
109
  this.accounts[0],
112
110
  FullProverTest.TOKEN_NAME,
113
111
  FullProverTest.TOKEN_SYMBOL,
114
112
  FullProverTest.TOKEN_DECIMALS,
115
- ).send({ from: this.accounts[0], wait: { returnReceipt: true } });
113
+ ).send({ from: this.accounts[0] });
116
114
  this.logger.verbose(`Token deployed to ${asset.address}`);
117
115
 
118
116
  this.fakeProofsAsset = asset;
@@ -225,8 +223,11 @@ export class FullProverTest {
225
223
 
226
224
  this.logger.verbose('Starting prover node');
227
225
  const sponsoredFPCAddress = await getSponsoredFPCAddress();
228
- const { prefilledPublicData } = await getGenesisValues(
226
+ const { genesis } = await getGenesisValues(
229
227
  this.context.initialFundedAccounts.map(a => a.address).concat(sponsoredFPCAddress),
228
+ undefined,
229
+ undefined,
230
+ this.context.genesis!.genesisTimestamp,
230
231
  );
231
232
 
232
233
  const proverNodeConfig: Parameters<typeof AztecNodeService.createAndSync>[0] = {
@@ -254,7 +255,7 @@ export class FullProverTest {
254
255
  this.proverAztecNode = await AztecNodeService.createAndSync(
255
256
  proverNodeConfig,
256
257
  { dateProvider: this.context.dateProvider, p2pClientDeps: { rpcTxProviders: [this.aztecNode] } },
257
- { prefilledPublicData },
258
+ { genesis },
258
259
  );
259
260
  this.logger.warn(`Proofs are now enabled`, { realProofs: this.realProofs });
260
261
  return this;
@@ -52,6 +52,7 @@ import { type ContractInstanceWithAddress, getContractInstanceFromInstantiationP
52
52
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
53
53
  import { tryStop } from '@aztec/stdlib/interfaces/server';
54
54
  import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
55
+ import type { GenesisData } from '@aztec/stdlib/world-state';
55
56
  import {
56
57
  type TelemetryClient,
57
58
  type TelemetryClientConfig,
@@ -249,8 +250,8 @@ export type EndToEndContext = {
249
250
  sequencerDelayer: Delayer | undefined;
250
251
  /** Delayer for prover node L1 txs (only when enableDelayer and startProverNode are true). */
251
252
  proverDelayer: Delayer | undefined;
252
- /** Prefilled public data used for setting up nodes. */
253
- prefilledPublicData: PublicDataTreeLeaf[] | undefined;
253
+ /** Genesis data used for setting up nodes. */
254
+ genesis: GenesisData | undefined;
254
255
  /** ACVM config (only set if running locally). */
255
256
  acvmConfig: Awaited<ReturnType<typeof getACVMConfig>>;
256
257
  /** BB config (only set if running locally). */
@@ -276,7 +277,7 @@ export async function setup(
276
277
  let anvil: Anvil | undefined;
277
278
  try {
278
279
  opts.aztecTargetCommitteeSize ??= 0;
279
- opts.slasherFlavor ??= 'none';
280
+ opts.slasherEnabled ??= false;
280
281
 
281
282
  const config: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts };
282
283
  // use initialValidators for the node config
@@ -375,10 +376,12 @@ export async function setup(
375
376
  addressesToFund.push(sponsoredFPCAddress);
376
377
  }
377
378
 
378
- const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues(
379
+ const genesisTimestamp = BigInt(Math.floor(Date.now() / 1000));
380
+ const { genesisArchiveRoot, genesis, fundingNeeded } = await getGenesisValues(
379
381
  addressesToFund,
380
382
  opts.initialAccountFeeJuice,
381
383
  opts.genesisPublicData,
384
+ genesisTimestamp,
382
385
  );
383
386
 
384
387
  const wasAutomining = await ethCheatCodes.isAutoMining();
@@ -496,11 +499,7 @@ export async function setup(
496
499
  }
497
500
 
498
501
  const aztecNodeService = await withLoggerBindings({ actor: 'node-0' }, () =>
499
- AztecNodeService.createAndSync(
500
- config,
501
- { dateProvider, telemetry: telemetryClient, p2pClientDeps },
502
- { prefilledPublicData },
503
- ),
502
+ AztecNodeService.createAndSync(config, { dateProvider, telemetry: telemetryClient, p2pClientDeps }, { genesis }),
504
503
  );
505
504
  const sequencerClient = aztecNodeService.getSequencer();
506
505
 
@@ -524,7 +523,7 @@ export async function setup(
524
523
  dataDirectory: proverNodeDataDirectory,
525
524
  },
526
525
  { dateProvider, p2pClientDeps, telemetry: telemetryClient },
527
- { prefilledPublicData },
526
+ { genesis },
528
527
  ));
529
528
  }
530
529
 
@@ -629,7 +628,7 @@ export async function setup(
629
628
  initialFundedAccounts,
630
629
  logger,
631
630
  mockGossipSubNetwork,
632
- prefilledPublicData,
631
+ genesis,
633
632
  proverNode,
634
633
  sequencerDelayer,
635
634
  proverDelayer,
@@ -729,7 +728,7 @@ export function createAndSyncProverNode(
729
728
  dateProvider: DateProvider;
730
729
  p2pClientDeps?: P2PClientDeps;
731
730
  },
732
- options: { prefilledPublicData: PublicDataTreeLeaf[]; dontStart?: boolean },
731
+ options: { genesis?: GenesisData; dontStart?: boolean },
733
732
  ): Promise<{ proverNode: AztecNodeService }> {
734
733
  return withLoggerBindings({ actor: 'prover-0' }, async () => {
735
734
  const proverNode = await AztecNodeService.createAndSync(
@@ -742,7 +741,7 @@ export function createAndSyncProverNode(
742
741
  proverPublisherPrivateKeys: [new SecretValue(proverNodePrivateKey)],
743
742
  },
744
743
  deps,
745
- { ...options, dontStartProverNode: options.dontStart },
744
+ { genesis: options.genesis, dontStartProverNode: options.dontStart },
746
745
  );
747
746
 
748
747
  if (!proverNode.getProverNode()) {
@@ -7,7 +7,7 @@ import { SecretValue } from '@aztec/foundation/config';
7
7
  import { withLoggerBindings } from '@aztec/foundation/log/server';
8
8
  import { bufferToHex } from '@aztec/foundation/string';
9
9
  import type { DateProvider } from '@aztec/foundation/timer';
10
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
10
+ import type { GenesisData } from '@aztec/stdlib/world-state';
11
11
 
12
12
  import getPort from 'get-port';
13
13
 
@@ -40,7 +40,7 @@ export async function createNodes(
40
40
  bootstrapNodeEnr: string,
41
41
  numNodes: number,
42
42
  bootNodePort: number,
43
- prefilledPublicData?: PublicDataTreeLeaf[],
43
+ genesis?: GenesisData,
44
44
  dataDirectory?: string,
45
45
  metricsPort?: number,
46
46
  indexOffset = 0,
@@ -65,7 +65,7 @@ export async function createNodes(
65
65
  port,
66
66
  bootstrapNodeEnr,
67
67
  validatorIndices,
68
- prefilledPublicData,
68
+ genesis,
69
69
  dataDir,
70
70
  metricsPort,
71
71
  );
@@ -97,7 +97,7 @@ export async function createNode(
97
97
  tcpPort: number,
98
98
  bootstrapNode: string | undefined,
99
99
  addressIndex: number | number[],
100
- prefilledPublicData?: PublicDataTreeLeaf[],
100
+ genesis?: GenesisData,
101
101
  dataDirectory?: string,
102
102
  metricsPort?: number,
103
103
  ) {
@@ -108,7 +108,7 @@ export async function createNode(
108
108
  return await AztecNodeService.createAndSync(
109
109
  validatorConfig,
110
110
  { telemetry, dateProvider },
111
- { prefilledPublicData, dontStartSequencer: config.dontStartSequencer },
111
+ { genesis, dontStartSequencer: config.dontStartSequencer },
112
112
  );
113
113
  });
114
114
  }
@@ -119,7 +119,7 @@ export async function createNonValidatorNode(
119
119
  dateProvider: DateProvider,
120
120
  tcpPort: number,
121
121
  bootstrapNode: string | undefined,
122
- prefilledPublicData?: PublicDataTreeLeaf[],
122
+ genesis?: GenesisData,
123
123
  dataDirectory?: string,
124
124
  metricsPort?: number,
125
125
  ) {
@@ -133,7 +133,7 @@ export async function createNonValidatorNode(
133
133
  sequencerPublisherPrivateKeys: [],
134
134
  };
135
135
  const telemetry = await getEndToEndTestTelemetryClient(metricsPort);
136
- return await AztecNodeService.createAndSync(config, { telemetry, dateProvider }, { prefilledPublicData });
136
+ return await AztecNodeService.createAndSync(config, { telemetry, dateProvider }, { genesis });
137
137
  });
138
138
  }
139
139
 
@@ -143,7 +143,7 @@ export async function createProverNode(
143
143
  bootstrapNode: string | undefined,
144
144
  addressIndex: number,
145
145
  deps: { dateProvider: DateProvider },
146
- prefilledPublicData?: PublicDataTreeLeaf[],
146
+ genesis?: GenesisData,
147
147
  dataDirectory?: string,
148
148
  metricsPort?: number,
149
149
  ): Promise<{ proverNode: AztecNodeService }> {
@@ -159,7 +159,7 @@ export async function createProverNode(
159
159
  { ...config, ...p2pConfig },
160
160
  { dataDirectory },
161
161
  { ...deps, telemetry },
162
- { prefilledPublicData: prefilledPublicData ?? [] },
162
+ { genesis },
163
163
  );
164
164
  });
165
165
  }
@@ -6,11 +6,8 @@ import { TokenContract } from '@aztec/noir-contracts.js/Token';
6
6
 
7
7
  export async function deployToken(wallet: Wallet, admin: AztecAddress, initialAdminBalance: bigint, logger: Logger) {
8
8
  logger.info(`Deploying Token contract...`);
9
- const {
10
- receipt: { contract, instance },
11
- } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({
9
+ const { contract, instance } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({
12
10
  from: admin,
13
- wait: { returnReceipt: true },
14
11
  });
15
12
 
16
13
  if (initialAdminBalance > 0n) {
@@ -0,0 +1,135 @@
1
+ // Custom Jest resolver. When CONTRACT_ARTIFACTS_VERSION is set, redirects *only* JSON artifact files under
2
+ // @aztec/noir-contracts.js/artifacts/ and @aztec/noir-test-contracts.js/artifacts/ to a local cache of the pinned
3
+ // legacy versions. TypeScript wrapper classes (e.g. Token.ts) continue to load from the current workspace and use the
4
+ // current @aztec/aztec.js — only the artifact JSON (the deployed-contract ABI / bytecode / notes surface) is swapped.
5
+ //
6
+ // Why JSON-only: the JSON artifact is the actual interchange surface a "deployed contract" exposes. The TS wrapper is
7
+ // generated client-side ergonomics that's tightly coupled to the current @aztec/aztec.js API. Redirecting the wrapper
8
+ // would couple this test to a moving aztec.js surface and break at import time on unrelated breaking changes; we want
9
+ // to fail only on actual artifact-compat regressions.
10
+ //
11
+ // The cache is populated on demand by running `npm install` into .legacy-contracts/<version>/.
12
+ //
13
+ // Activated by env var; passthrough otherwise.
14
+ /* eslint-disable @typescript-eslint/no-require-imports */
15
+
16
+ const path = require('path');
17
+ const fs = require('fs');
18
+ const { execSync } = require('child_process');
19
+
20
+ const version = process.env.CONTRACT_ARTIFACTS_VERSION;
21
+ const REDIRECTED = ['@aztec/noir-contracts.js', '@aztec/noir-test-contracts.js'];
22
+
23
+ // Jest sets rootDir to <e2e>/src; this file lives there too.
24
+ const e2eRoot = path.resolve(__dirname, '..');
25
+ const cacheRoot = version ? path.join(e2eRoot, '.legacy-contracts', version) : null;
26
+
27
+ function pkgJsonPath(name) {
28
+ return path.join(cacheRoot, 'node_modules', name, 'package.json');
29
+ }
30
+
31
+ function ensureCache() {
32
+ const missing = REDIRECTED.some(p => !fs.existsSync(pkgJsonPath(p)));
33
+ if (!missing) {
34
+ return;
35
+ }
36
+ fs.mkdirSync(cacheRoot, { recursive: true });
37
+ // Seed a standalone package.json so `npm install --prefix` treats cacheRoot as its own project. Without this, npm
38
+ // walks up and finds the yarn-project workspace root, which breaks on `workspace:` protocol deps and risks
39
+ // clobbering the monorepo's node_modules.
40
+ const seed = path.join(cacheRoot, 'package.json');
41
+ if (!fs.existsSync(seed)) {
42
+ fs.writeFileSync(seed, JSON.stringify({ name: 'legacy-contracts-cache', private: true }));
43
+ }
44
+
45
+ const specs = REDIRECTED.map(p => `${p}@${version}`).join(' ');
46
+ process.stderr.write(`[legacy-contracts] installing ${specs} into ${cacheRoot}\n`);
47
+ // --prefix: install into cacheRoot instead of cwd, so the cache is isolated from the monorepo.
48
+ // --no-save: don't write the installed packages back to the seeded package.json.
49
+ // --ignore-scripts: skip lifecycle scripts (preinstall/postinstall) of the legacy packages and their transitive
50
+ // deps; we only want the files on disk, not to run any build steps.
51
+ // --legacy-peer-deps: tolerate peer-dependency mismatches between the pinned legacy @aztec/* graph and whatever
52
+ // current versions npm would otherwise try to reconcile.
53
+ execSync(`npm install --prefix "${cacheRoot}" --no-save --ignore-scripts --legacy-peer-deps ${specs}`, {
54
+ stdio: 'inherit',
55
+ });
56
+
57
+ // Verify versions on disk match the requested version.
58
+ for (const p of REDIRECTED) {
59
+ const onDisk = JSON.parse(fs.readFileSync(pkgJsonPath(p), 'utf8')).version;
60
+ if (onDisk !== version) {
61
+ throw new Error(`[legacy-contracts] ${p} on disk is ${onDisk}, expected ${version}`);
62
+ }
63
+ }
64
+ }
65
+
66
+ if (version) {
67
+ ensureCache();
68
+ }
69
+
70
+ let bannerPrinted = false;
71
+ const seen = new Set();
72
+
73
+ function printBannerOnce() {
74
+ if (bannerPrinted || !version) {
75
+ return;
76
+ }
77
+ bannerPrinted = true;
78
+ const lines = ['='.repeat(60), `[legacy-contracts][jest] CONTRACT_ARTIFACTS_VERSION=${version}`];
79
+ for (const p of REDIRECTED) {
80
+ const v = JSON.parse(fs.readFileSync(pkgJsonPath(p), 'utf8')).version;
81
+ if (v !== version) {
82
+ throw new Error(`[legacy-contracts] ${p} on disk is ${v}, expected ${version}`);
83
+ }
84
+ lines.push(`[legacy-contracts][jest] redirecting ${p}/artifacts/*.json -> .legacy-contracts/${version}/...`);
85
+ }
86
+ lines.push('='.repeat(60));
87
+ process.stderr.write(lines.join('\n') + '\n');
88
+ }
89
+
90
+ // Match a resolved absolute path against the workspace artifacts dirs and return the legacy cache equivalent, or null
91
+ // if it's not an artifact path we should redirect.
92
+ function legacyArtifactPath(resolved) {
93
+ if (!resolved.endsWith('.json')) {
94
+ return null;
95
+ }
96
+ for (const pkg of REDIRECTED) {
97
+ // pkg = '@aztec/noir-contracts.js' -> match '/noir-contracts.js/artifacts/'
98
+ const dirName = pkg.split('/')[1];
99
+ const marker = `/${dirName}/artifacts/`;
100
+ const idx = resolved.indexOf(marker);
101
+ if (idx === -1) {
102
+ continue;
103
+ }
104
+ const basename = resolved.slice(idx + marker.length);
105
+ return path.join(cacheRoot, 'node_modules', pkg, 'artifacts', basename);
106
+ }
107
+ return null;
108
+ }
109
+
110
+ module.exports = function legacyResolver(request, options) {
111
+ // Always run the default resolver first. We only inspect (and possibly rewrite) the *result*; this catches both
112
+ // bare-specifier imports of `@aztec/noir-contracts.js/artifacts/foo.json` and the relative `../artifacts/foo.json`
113
+ // imports inside the workspace TS wrapper classes — both resolve to the same workspace artifact path that we then
114
+ // redirect.
115
+ const resolved = options.defaultResolver(request, options);
116
+ if (!version) {
117
+ return resolved;
118
+ }
119
+ printBannerOnce();
120
+ const legacy = legacyArtifactPath(resolved);
121
+ if (!legacy) {
122
+ return resolved;
123
+ }
124
+ if (!fs.existsSync(legacy)) {
125
+ throw new Error(
126
+ `[legacy-contracts] artifact ${path.basename(legacy)} not present in legacy cache @${version}; ` +
127
+ `the contract may have been added after that release. Pin a newer CONTRACT_ARTIFACTS_VERSION or skip this test.`,
128
+ );
129
+ }
130
+ if (!seen.has(resolved)) {
131
+ seen.add(resolved);
132
+ process.stderr.write(`[legacy-contracts][jest] redirected ${path.basename(legacy)} -> ${legacy}\n`);
133
+ }
134
+ return legacy;
135
+ };
@@ -11,7 +11,7 @@ import type { Wallet } from '@aztec/aztec.js/wallet';
11
11
  import { createEthereumChain } from '@aztec/ethereum/chain';
12
12
  import { createExtendedL1Client } from '@aztec/ethereum/client';
13
13
  import type { Logger } from '@aztec/foundation/log';
14
- import { retryUntil } from '@aztec/foundation/retry';
14
+ import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
15
15
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
16
16
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
17
17
  import { registerInitialLocalNetworkAccountsInWallet } from '@aztec/wallets/testing';
@@ -138,38 +138,57 @@ async function deployAccountWithDiagnostics(
138
138
  estimateGas?: boolean,
139
139
  ): Promise<void> {
140
140
  const deployMethod = await account.getDeployMethod();
141
- let txHash;
142
- try {
143
- let gasSettings;
144
- if (estimateGas) {
145
- const sim = await deployMethod.simulate({ from: NO_FROM, fee: { paymentMethod } });
146
- gasSettings = sim.estimatedGas;
147
- logger.info(`${accountLabel} estimated gas: DA=${gasSettings.gasLimits.daGas} L2=${gasSettings.gasLimits.l2Gas}`);
148
- }
149
- const deployResult = await deployMethod.send({
150
- from: NO_FROM,
151
- fee: { paymentMethod, gasSettings },
152
- wait: NO_WAIT,
153
- });
154
- txHash = deployResult.txHash;
155
- await waitForTx(aztecNode, txHash, { timeout: 2400 });
156
- logger.info(`${accountLabel} deployed at ${account.address}`);
157
- } catch (error) {
158
- const blockNumber = await aztecNode.getBlockNumber();
159
- let receipt;
160
- try {
161
- receipt = await aztecNode.getTxReceipt(txHash);
162
- } catch {
163
- receipt = 'unavailable';
164
- }
165
- logger.error(`${accountLabel} deployment failed`, {
166
- txHash: txHash.toString(),
167
- receipt: JSON.stringify(receipt),
168
- currentBlockNumber: blockNumber,
169
- error: String(error),
170
- });
171
- throw error;
141
+ let gasSettings: any;
142
+ if (estimateGas) {
143
+ const sim = await deployMethod.simulate({ from: NO_FROM, fee: { paymentMethod } });
144
+ gasSettings = sim.estimatedGas;
145
+ logger.info(`${accountLabel} estimated gas: DA=${gasSettings.gasLimits.daGas} L2=${gasSettings.gasLimits.l2Gas}`);
172
146
  }
147
+
148
+ // Track the tx hash across retries so we don't re-send when the previous tx is still pending.
149
+ let sentTxHash: { txHash: any } | undefined;
150
+
151
+ await retry(
152
+ async () => {
153
+ // Check if already deployed (handles case where previous attempt succeeded but waitForTx timed out)
154
+ const existing = await aztecNode.getContract(account.address);
155
+ if (existing) {
156
+ logger.info(`${accountLabel} already deployed at ${account.address}, skipping`);
157
+ return;
158
+ }
159
+
160
+ // If we already sent a tx, check if it was dropped before deciding to re-send.
161
+ if (sentTxHash) {
162
+ const prevReceipt = await aztecNode.getTxReceipt(sentTxHash.txHash);
163
+ if (prevReceipt.isDropped()) {
164
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} was dropped, re-sending`);
165
+ sentTxHash = undefined;
166
+ } else {
167
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} still pending, waiting again...`);
168
+ }
169
+ }
170
+
171
+ if (!sentTxHash) {
172
+ const deployResult = await deployMethod.send({
173
+ from: NO_FROM,
174
+ fee: { paymentMethod, gasSettings },
175
+ wait: NO_WAIT,
176
+ });
177
+ sentTxHash = { txHash: deployResult.txHash };
178
+ logger.info(`${accountLabel} tx sent`, { txHash: sentTxHash.txHash.toString() });
179
+ }
180
+
181
+ const receipt = await waitForTx(aztecNode, sentTxHash.txHash, { timeout: 600 });
182
+ if (receipt.isDropped()) {
183
+ sentTxHash = undefined;
184
+ throw new Error(`${accountLabel} tx was dropped, retrying...`);
185
+ }
186
+ logger.info(`${accountLabel} deployed at ${account.address}`);
187
+ },
188
+ `deploy ${accountLabel}`,
189
+ makeBackoff([1, 2, 4, 8, 16]),
190
+ logger,
191
+ );
173
192
  }
174
193
 
175
194
  async function deployAccountsInBatches(
@@ -346,14 +365,18 @@ async function deployTokenAndMint(
346
365
  logger: Logger,
347
366
  ) {
348
367
  logger.verbose(`Deploying TokenContract...`);
349
- const {
350
- receipt: { contract: tokenContract },
351
- } = await TokenContract.deploy(wallet, admin, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS).send({
368
+ const { contract: tokenContract } = await TokenContract.deploy(
369
+ wallet,
370
+ admin,
371
+ TOKEN_NAME,
372
+ TOKEN_SYMBOL,
373
+ TOKEN_DECIMALS,
374
+ ).send({
352
375
  from: admin,
353
376
  fee: {
354
377
  paymentMethod,
355
378
  },
356
- wait: { timeout: 600, returnReceipt: true },
379
+ wait: { timeout: 600 },
357
380
  });
358
381
 
359
382
  const tokenAddress = tokenContract.address;
@@ -25,13 +25,14 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
25
25
  import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
26
26
  import { deriveSigningKey } from '@aztec/stdlib/keys';
27
27
  import type { NoteDao } from '@aztec/stdlib/note';
28
- import type {
29
- BlockHeader,
28
+ import {
29
+ type BlockHeader,
30
+ type ContractOverrides,
30
31
  SimulationOverrides,
31
- TxExecutionRequest,
32
- TxHash,
33
- TxReceipt,
34
- TxSimulationResult,
32
+ type TxExecutionRequest,
33
+ type TxHash,
34
+ type TxReceipt,
35
+ type TxSimulationResult,
35
36
  } from '@aztec/stdlib/tx';
36
37
  import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
37
38
  import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
@@ -110,23 +111,39 @@ export class TestWallet extends BaseWallet {
110
111
  return this.createAccount(accountData);
111
112
  }
112
113
 
113
- async getFakeAccountDataFor(address: AztecAddress) {
114
- const originalAccount = await this.getAccountFromAddress(address);
115
- const originalAddress = originalAccount.getCompleteAddress();
116
- const contractInstance = await this.pxe.getContractInstance(originalAddress.address);
117
- if (!contractInstance) {
118
- throw new Error(`No contract instance found for address: ${originalAddress.address}`);
114
+ /**
115
+ * Builds contract overrides for all provided addresses by replacing their account contracts with stub implementations.
116
+ */
117
+ protected async buildAccountOverrides(addresses: AztecAddress[]): Promise<ContractOverrides> {
118
+ const accounts = await this.getAccounts();
119
+ const contracts: ContractOverrides = {};
120
+
121
+ const filtered = accounts.filter(acc => addresses.some(addr => addr.equals(acc.item)));
122
+
123
+ for (const account of filtered) {
124
+ const address = account.item;
125
+ const originalAccount = await this.getAccountFromAddress(address);
126
+ const completeAddress = originalAccount.getCompleteAddress();
127
+ const contractInstance = await this.pxe.getContractInstance(completeAddress.address);
128
+ if (!contractInstance) {
129
+ throw new Error(
130
+ `No contract instance found for address: ${completeAddress.address} during account override building. This is a bug!`,
131
+ );
132
+ }
133
+
134
+ const stubInstance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {
135
+ salt: Fr.random(),
136
+ });
137
+
138
+ contracts[address.toString()] = {
139
+ instance: stubInstance,
140
+ artifact: StubAccountContractArtifact,
141
+ };
119
142
  }
120
- const stubAccount = createStubAccount(originalAddress);
121
- const instance = await getContractInstanceFromInstantiationParams(StubAccountContractArtifact, {
122
- salt: Fr.random(),
123
- });
124
- return {
125
- account: stubAccount,
126
- instance,
127
- artifact: StubAccountContractArtifact,
128
- };
143
+
144
+ return contracts;
129
145
  }
146
+
130
147
  protected accounts: Map<string, Account> = new Map();
131
148
 
132
149
  /**
@@ -220,6 +237,8 @@ export class TestWallet extends BaseWallet {
220
237
  ): Promise<TxSimulationResult> {
221
238
  const { from, feeOptions, scopes, skipTxValidation, skipFeeEnforcement } = opts;
222
239
  const skipKernels = this.simulationMode !== 'full';
240
+ const useOverride = this.simulationMode === 'kernelless-override';
241
+
223
242
  const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload();
224
243
  const finalExecutionPayload = feeExecutionPayload
225
244
  ? mergeExecutionPayloads([feeExecutionPayload, executionPayload])
@@ -228,18 +247,20 @@ export class TestWallet extends BaseWallet {
228
247
 
229
248
  let overrides: SimulationOverrides | undefined;
230
249
  let txRequest: TxExecutionRequest;
250
+ if (useOverride) {
251
+ const accountOverrides = await this.buildAccountOverrides(this.scopesFrom(from, opts.additionalScopes));
252
+ overrides = new SimulationOverrides(accountOverrides);
253
+ }
254
+
231
255
  if (from === NO_FROM) {
232
256
  const entrypoint = new DefaultEntrypoint();
233
257
  txRequest = await entrypoint.createTxExecutionRequest(finalExecutionPayload, feeOptions.gasSettings, chainInfo);
234
258
  } else {
235
- const useOverride = this.simulationMode === 'kernelless-override';
236
259
  let fromAccount: Account;
237
260
  if (useOverride) {
238
- const { account, instance, artifact } = await this.getFakeAccountDataFor(from);
239
- fromAccount = account;
240
- overrides = {
241
- contracts: { [from.toString()]: { instance, artifact } },
242
- };
261
+ const originalAccount = await this.getAccountFromAddress(from);
262
+ const completeAddress = originalAccount.getCompleteAddress();
263
+ fromAccount = createStubAccount(completeAddress);
243
264
  } else {
244
265
  fromAccount = await this.getAccountFromAddress(from);
245
266
  }
@@ -268,7 +289,11 @@ export class TestWallet extends BaseWallet {
268
289
  }
269
290
 
270
291
  async proveTx(exec: ExecutionPayload, opts: Omit<SendOptions, 'wait'>): Promise<ProvenTx> {
271
- const fee = await this.completeFeeOptions(opts.from, exec.feePayer, opts.fee?.gasSettings);
292
+ const fee = await this.completeFeeOptions({
293
+ from: opts.from,
294
+ feePayer: exec.feePayer,
295
+ gasSettings: opts.fee?.gasSettings,
296
+ });
272
297
  const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(exec, opts.from, fee);
273
298
  const txProvingResult = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes));
274
299
  return new ProvenTx(