@aztec/end-to-end 0.0.1-commit.2448fdb → 0.0.1-commit.2606882

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 (172) hide show
  1. package/dest/bench/client_flows/benchmark.d.ts +15 -1
  2. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  3. package/dest/bench/client_flows/benchmark.js +17 -0
  4. package/dest/bench/client_flows/client_flows_benchmark.d.ts +2 -2
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  6. package/dest/bench/client_flows/client_flows_benchmark.js +25 -35
  7. package/dest/bench/utils.d.ts +1 -1
  8. package/dest/bench/utils.d.ts.map +1 -1
  9. package/dest/bench/utils.js +2 -0
  10. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +3 -3
  11. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  12. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +13 -4
  13. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -4
  14. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  15. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +39 -7
  16. package/dest/e2e_deploy_contract/deploy_test.d.ts +3 -3
  17. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  18. package/dest/e2e_deploy_contract/deploy_test.js +2 -1
  19. package/dest/e2e_epochs/epochs_test.d.ts +23 -2
  20. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  21. package/dest/e2e_epochs/epochs_test.js +83 -12
  22. package/dest/e2e_fees/fees_test.d.ts +5 -2
  23. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  24. package/dest/e2e_fees/fees_test.js +31 -5
  25. package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
  26. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  27. package/dest/e2e_nested_contract/nested_contract_test.js +2 -1
  28. package/dest/e2e_p2p/inactivity_slash_test.js +3 -3
  29. package/dest/e2e_p2p/p2p_network.d.ts +11 -10
  30. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  31. package/dest/e2e_p2p/p2p_network.js +47 -28
  32. package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
  33. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  34. package/dest/e2e_p2p/reqresp/utils.js +42 -13
  35. package/dest/e2e_p2p/shared.d.ts +23 -26
  36. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  37. package/dest/e2e_p2p/shared.js +62 -80
  38. package/dest/e2e_token_contract/token_contract_test.d.ts +3 -3
  39. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  40. package/dest/e2e_token_contract/token_contract_test.js +4 -2
  41. package/dest/fixtures/e2e_prover_test.d.ts +6 -5
  42. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  43. package/dest/fixtures/e2e_prover_test.js +9 -13
  44. package/dest/fixtures/elu_monitor.d.ts +21 -0
  45. package/dest/fixtures/elu_monitor.d.ts.map +1 -0
  46. package/dest/fixtures/elu_monitor.js +102 -0
  47. package/dest/fixtures/fixtures.d.ts +70 -1
  48. package/dest/fixtures/fixtures.d.ts.map +1 -1
  49. package/dest/fixtures/fixtures.js +65 -0
  50. package/dest/fixtures/get_bb_config.d.ts +1 -1
  51. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  52. package/dest/fixtures/get_bb_config.js +5 -5
  53. package/dest/fixtures/ha_setup.d.ts +2 -2
  54. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  55. package/dest/fixtures/ha_setup.js +1 -1
  56. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  57. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  58. package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
  59. package/dest/fixtures/setup.d.ts +44 -10
  60. package/dest/fixtures/setup.d.ts.map +1 -1
  61. package/dest/fixtures/setup.js +145 -19
  62. package/dest/fixtures/setup_p2p_test.d.ts +10 -6
  63. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  64. package/dest/fixtures/setup_p2p_test.js +8 -8
  65. package/dest/fixtures/token_utils.d.ts +1 -1
  66. package/dest/fixtures/token_utils.d.ts.map +1 -1
  67. package/dest/fixtures/token_utils.js +2 -5
  68. package/dest/fixtures/utils.d.ts +2 -2
  69. package/dest/fixtures/utils.d.ts.map +1 -1
  70. package/dest/fixtures/utils.js +1 -1
  71. package/dest/install_legacy_contracts.d.cts +1 -1
  72. package/dest/install_legacy_contracts.d.cts.map +1 -1
  73. package/dest/legacy-jest-resolver.d.cts +1 -1
  74. package/dest/legacy-jest-resolver.d.cts.map +1 -1
  75. package/dest/shared/cross_chain_test_harness.d.ts +4 -2
  76. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  77. package/dest/shared/cross_chain_test_harness.js +9 -5
  78. package/dest/shared/gas_portal_test_harness.d.ts +8 -5
  79. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  80. package/dest/shared/gas_portal_test_harness.js +17 -8
  81. package/dest/shared/jest_setup.js +41 -1
  82. package/dest/shared/submit-transactions.d.ts +1 -1
  83. package/dest/shared/submit-transactions.d.ts.map +1 -1
  84. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  85. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  86. package/dest/shared/uniswap_l1_l2.js +50 -32
  87. package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
  88. package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
  89. package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
  90. package/dest/simulators/lending_simulator.d.ts +10 -3
  91. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  92. package/dest/simulators/lending_simulator.js +23 -11
  93. package/dest/simulators/token_simulator.js +1 -1
  94. package/dest/spartan/setup_test_wallets.d.ts +1 -1
  95. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  96. package/dest/spartan/setup_test_wallets.js +67 -43
  97. package/dest/spartan/tx_metrics.d.ts +18 -4
  98. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  99. package/dest/spartan/tx_metrics.js +74 -21
  100. package/dest/spartan/utils/config.d.ts +10 -30
  101. package/dest/spartan/utils/config.d.ts.map +1 -1
  102. package/dest/spartan/utils/config.js +3 -1
  103. package/dest/spartan/utils/index.d.ts +2 -1
  104. package/dest/spartan/utils/index.d.ts.map +1 -1
  105. package/dest/spartan/utils/index.js +2 -0
  106. package/dest/spartan/utils/nodes.d.ts +4 -5
  107. package/dest/spartan/utils/nodes.d.ts.map +1 -1
  108. package/dest/spartan/utils/nodes.js +11 -11
  109. package/dest/spartan/utils/pod_logs.d.ts +25 -0
  110. package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
  111. package/dest/spartan/utils/pod_logs.js +74 -0
  112. package/dest/test-wallet/test_wallet.d.ts +3 -2
  113. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  114. package/dest/test-wallet/test_wallet.js +52 -28
  115. package/dest/test-wallet/utils.d.ts +3 -3
  116. package/dest/test-wallet/utils.d.ts.map +1 -1
  117. package/dest/test-wallet/utils.js +2 -7
  118. package/dest/test-wallet/wallet_worker_script.js +7 -2
  119. package/dest/test-wallet/worker_wallet.d.ts +2 -1
  120. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  121. package/dest/test-wallet/worker_wallet.js +6 -2
  122. package/dest/test-wallet/worker_wallet_schema.d.ts +110 -224
  123. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  124. package/dest/test-wallet/worker_wallet_schema.js +14 -2
  125. package/package.json +48 -46
  126. package/src/bench/client_flows/benchmark.ts +19 -0
  127. package/src/bench/client_flows/client_flows_benchmark.ts +62 -51
  128. package/src/bench/utils.ts +2 -1
  129. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +21 -4
  130. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +49 -11
  131. package/src/e2e_deploy_contract/deploy_test.ts +3 -2
  132. package/src/e2e_epochs/epochs_test.ts +85 -9
  133. package/src/e2e_fees/fees_test.ts +37 -4
  134. package/src/e2e_nested_contract/nested_contract_test.ts +3 -1
  135. package/src/e2e_p2p/inactivity_slash_test.ts +3 -3
  136. package/src/e2e_p2p/p2p_network.ts +58 -40
  137. package/src/e2e_p2p/reqresp/utils.ts +51 -12
  138. package/src/e2e_p2p/shared.ts +60 -113
  139. package/src/e2e_token_contract/token_contract_test.ts +12 -2
  140. package/src/fixtures/dumps/epoch_proof_result.json +1 -1
  141. package/src/fixtures/e2e_prover_test.ts +15 -18
  142. package/src/fixtures/elu_monitor.ts +126 -0
  143. package/src/fixtures/fixtures.ts +83 -0
  144. package/src/fixtures/get_bb_config.ts +7 -6
  145. package/src/fixtures/ha_setup.ts +4 -2
  146. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  147. package/src/fixtures/setup.ts +175 -24
  148. package/src/fixtures/setup_p2p_test.ts +13 -9
  149. package/src/fixtures/token_utils.ts +1 -4
  150. package/src/fixtures/utils.ts +2 -0
  151. package/src/guides/up_quick_start.sh +0 -2
  152. package/src/install_legacy_contracts.cjs +9 -5
  153. package/src/legacy-jest-resolver.cjs +15 -4
  154. package/src/shared/cross_chain_test_harness.ts +14 -4
  155. package/src/shared/gas_portal_test_harness.ts +20 -10
  156. package/src/shared/jest_setup.ts +51 -1
  157. package/src/shared/submit-transactions.ts +1 -4
  158. package/src/shared/uniswap_l1_l2.ts +84 -40
  159. package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
  160. package/src/simulators/lending_simulator.ts +27 -13
  161. package/src/simulators/token_simulator.ts +1 -1
  162. package/src/spartan/setup_test_wallets.ts +65 -40
  163. package/src/spartan/tx_metrics.ts +82 -24
  164. package/src/spartan/utils/config.ts +2 -0
  165. package/src/spartan/utils/index.ts +3 -0
  166. package/src/spartan/utils/nodes.ts +17 -12
  167. package/src/spartan/utils/pod_logs.ts +99 -0
  168. package/src/test-wallet/test_wallet.ts +49 -24
  169. package/src/test-wallet/utils.ts +3 -7
  170. package/src/test-wallet/wallet_worker_script.ts +5 -2
  171. package/src/test-wallet/worker_wallet.ts +7 -3
  172. package/src/test-wallet/worker_wallet_schema.ts +2 -2
@@ -8,7 +8,12 @@ import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
8
8
  import { retryUntil } from '@aztec/foundation/retry';
9
9
  import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice';
10
10
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
11
- import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
11
+ import type { AztecNodeAdmin, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
12
+
13
+ import { waitForL1ToL2MessageSeen } from './wait_for_l1_to_l2_message.js';
14
+
15
+ /** Aztec node that may expose the debug mining API in local e2e setups. */
16
+ type MiningAztecNode = AztecNode & Partial<AztecNodeDebug>;
12
17
 
13
18
  export interface IGasBridgingTestHarness {
14
19
  getL1FeeJuiceBalance(address: EthAddress): Promise<bigint>;
@@ -19,7 +24,7 @@ export interface IGasBridgingTestHarness {
19
24
  }
20
25
 
21
26
  export interface FeeJuicePortalTestingHarnessFactoryConfig {
22
- aztecNode: AztecNode;
27
+ aztecNode: MiningAztecNode;
23
28
  aztecNodeAdmin?: AztecNodeAdmin;
24
29
  l1Client: ExtendedViemWalletClient;
25
30
  wallet: Wallet;
@@ -75,7 +80,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
75
80
 
76
81
  constructor(
77
82
  /** Aztec node */
78
- public aztecNode: AztecNode,
83
+ public aztecNode: MiningAztecNode,
79
84
  /** Aztec node admin interface */
80
85
  public aztecNodeAdmin: AztecNodeAdmin | undefined,
81
86
  /** Wallet. */
@@ -144,8 +149,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
144
149
  await this.mintTokensOnL1();
145
150
  const claim = await this.sendTokensToPortalPublic(bridgeAmount, owner);
146
151
 
147
- const isSynced = async () => await this.aztecNode.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
148
- await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 1);
152
+ await waitForL1ToL2MessageSeen(this.aztecNode, Fr.fromHexString(claim.messageHash), { timeoutSeconds: 24 });
149
153
 
150
154
  // Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
151
155
  await this.advanceL2Block();
@@ -163,6 +167,11 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
163
167
  }
164
168
 
165
169
  private async advanceL2Block() {
170
+ if (this.aztecNode.mineBlock) {
171
+ await this.aztecNode.mineBlock();
172
+ return;
173
+ }
174
+
166
175
  const initialBlockNumber = await this.aztecNode.getBlockNumber();
167
176
 
168
177
  let minTxsPerBlock = undefined;
@@ -171,11 +180,12 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
171
180
  await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); // Set to 0 to ensure we can advance the block
172
181
  }
173
182
 
174
- await retryUntil(async () => (await this.aztecNode.getBlockNumber()) >= initialBlockNumber + 1);
175
-
176
- if (this.aztecNodeAdmin && minTxsPerBlock !== undefined) {
177
- await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
183
+ try {
184
+ await retryUntil(async () => (await this.aztecNode.getBlockNumber()) >= initialBlockNumber + 1);
185
+ } finally {
186
+ if (this.aztecNodeAdmin && minTxsPerBlock !== undefined) {
187
+ await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
188
+ }
178
189
  }
179
190
  }
180
191
  }
181
- // docs:end:cross_chain_test_harness
@@ -1,8 +1,19 @@
1
1
  import { createLogger } from '@aztec/aztec.js/log';
2
2
 
3
- import { beforeEach, expect } from '@jest/globals';
3
+ import { afterAll, afterEach, beforeEach, expect } from '@jest/globals';
4
+ import { readlinkSync } from 'fs';
4
5
  import { basename } from 'path';
5
6
 
7
+ import { EluMonitor } from '../fixtures/elu_monitor.js';
8
+
9
+ const eluMonitor = process.env.ELU_MONITOR_FILE
10
+ ? new EluMonitor(process.env.ELU_MONITOR_FILE, Number(process.env.ELU_MONITOR_INTERVAL_MS) || undefined)
11
+ : undefined;
12
+
13
+ if (eluMonitor) {
14
+ process.on('exit', () => eluMonitor.stop());
15
+ }
16
+
6
17
  beforeEach(() => {
7
18
  const { testPath, currentTestName } = expect.getState();
8
19
  if (!testPath || !currentTestName) {
@@ -10,4 +21,43 @@ beforeEach(() => {
10
21
  }
11
22
  const logger = createLogger(`e2e:${basename(testPath).replace('.test.ts', '')}`);
12
23
  logger.info(`Running test: ${currentTestName}`);
24
+ eluMonitor?.startTest(currentTestName);
25
+ });
26
+
27
+ afterEach(() => {
28
+ eluMonitor?.stopTest();
29
+ });
30
+
31
+ // Log leaked handles after all tests complete. This runs after test-level afterAll hooks,
32
+ // so any handles still alive at this point were not properly cleaned up during teardown.
33
+ // This diagnostic helps identify the source of exit hangs without masking them.
34
+ afterAll(() => {
35
+ const handles = (process as any)._getActiveHandles();
36
+ if (handles.length > 0) {
37
+ const details = handles.map((h: any) => {
38
+ const type = h?.constructor?.name ?? typeof h;
39
+ const fd = h?.fd ?? h?._handle?.fd ?? '?';
40
+ const destroyed = h?.destroyed ?? '?';
41
+ const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : '?';
42
+ const localAddr = h?.localAddress ?? '';
43
+ const remoteAddr = h?.remoteAddress ?? '';
44
+ const localPort = h?.localPort ?? '';
45
+ const remotePort = h?.remotePort ?? '';
46
+ const proto = Object.getPrototypeOf(h)?.constructor?.name ?? '?';
47
+ const keys = Object.keys(h).slice(0, 10).join(',');
48
+ let fdTarget = '';
49
+ if (typeof fd === 'number') {
50
+ try {
51
+ fdTarget = ` -> ${readlinkSync(`/proc/self/fd/${fd}`)}`;
52
+ } catch {
53
+ // ignore
54
+ }
55
+ }
56
+ return ` ${type}(fd=${fd}, destroyed=${destroyed}, hasRef=${hasRef}${fdTarget}) proto=${proto} addr=${localAddr}:${localPort}->${remoteAddr}:${remotePort} keys=[${keys}]`;
57
+ });
58
+ process.stderr.write(
59
+ `\n[jest_setup] WARNING: ${handles.length} handle(s) still active after teardown:\n${details.join('\n')}\n` +
60
+ `These may prevent Jest from exiting. Investigate and fix the leak.\n\n`,
61
+ );
62
+ }
13
63
  });
@@ -19,10 +19,7 @@ export const submitTxsTo = async (
19
19
  times(numTxs, async () => {
20
20
  const accountManager = await wallet.createSchnorrAccount(Fr.random(), Fr.random(), GrumpkinScalar.random());
21
21
  const deployMethod = await accountManager.getDeployMethod();
22
- const { txHash } = await deployMethod.send({
23
- from: submitter,
24
- wait: NO_WAIT,
25
- });
22
+ const { txHash } = await deployMethod.send({ from: submitter, wait: NO_WAIT });
26
23
 
27
24
  logger.info(`Tx sent with hash ${txHash}`);
28
25
  const receipt: TxReceipt = await wallet.getTxReceipt(txHash);
@@ -13,15 +13,19 @@ import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
13
13
  import { extractEvent } from '@aztec/ethereum/utils';
14
14
  import { EpochNumber } from '@aztec/foundation/branded-types';
15
15
  import { sha256ToField } from '@aztec/foundation/crypto/sha256';
16
+ import { retryUntil } from '@aztec/foundation/retry';
16
17
  import { InboxAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts';
17
18
  import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap';
18
19
  import { computeL2ToL1MessageHash } from '@aztec/stdlib/hash';
19
- import { computeL2ToL1MembershipWitness } from '@aztec/stdlib/messaging';
20
20
 
21
21
  import { jest } from '@jest/globals';
22
22
  import { type GetContractReturnType, getContract, parseEther, toFunctionSelector } from 'viem';
23
23
 
24
- import { type EndToEndContext, ensureAccountContractsPublished } from '../fixtures/utils.js';
24
+ import {
25
+ type EndToEndContext,
26
+ ensureAccountContractsPublished,
27
+ ensureAuthRegistryPublished,
28
+ } from '../fixtures/utils.js';
25
29
  import type { TestWallet } from '../test-wallet/test_wallet.js';
26
30
  import { CrossChainTestHarness } from './cross_chain_test_harness.js';
27
31
 
@@ -32,7 +36,7 @@ import { CrossChainTestHarness } from './cross_chain_test_harness.js';
32
36
  // anvil --fork-url https://mainnet.infura.io/v3/9928b52099854248b3a096be07a6b23c --fork-block-number 17514288 --chain-id 31337
33
37
  // For CI, this is configured in `run_tests.sh` and `docker-compose-images.yml`
34
38
 
35
- const TIMEOUT = 360_000;
39
+ const TIMEOUT = 15 * 60 * 1000;
36
40
 
37
41
  export const uniswapL1L2TestSuite = (
38
42
  setup: () => Promise<EndToEndContext>,
@@ -98,6 +102,7 @@ export const uniswapL1L2TestSuite = (
98
102
  ownerEthAddress = EthAddress.fromString((await l1Client.getAddresses())[0]);
99
103
 
100
104
  await ensureAccountContractsPublished(wallet, [ownerAddress, sponsorAddress]);
105
+ await ensureAuthRegistryPublished(wallet, ownerAddress);
101
106
 
102
107
  logger.info('Deploying DAI Portal, initializing and deploying l2 contract...');
103
108
  daiCrossChainHarness = await CrossChainTestHarness.new(
@@ -154,7 +159,6 @@ export const uniswapL1L2TestSuite = (
154
159
  await cleanup();
155
160
  });
156
161
 
157
- // docs:start:uniswap_private
158
162
  it('should uniswap trade on L1 from L2 funds privately (swaps WETH -> DAI)', async () => {
159
163
  const wethL1BeforeBalance = await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress);
160
164
 
@@ -251,14 +255,13 @@ export const uniswapL1L2TestSuite = (
251
255
  // ensure that uniswap contract didn't eat the funds.
252
256
  await wethCrossChainHarness.expectPublicBalanceOnL2(uniswapL2Contract.address, 0n);
253
257
 
254
- // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
255
- const swapResult = (await computeL2ToL1MembershipWitness(
256
- aztecNode,
257
- swapPrivateLeaf,
258
- l2UniswapInteractionReceipt.txHash,
259
- ))!;
260
- const { epochNumber: epoch } = swapResult;
261
- await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
258
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch
259
+ // before we can ask the witness helper for a covering root.
260
+ const minedReceipt = await aztecNode.getTxReceipt(l2UniswapInteractionReceipt.txHash);
261
+ if (minedReceipt.epochNumber === undefined) {
262
+ throw new Error('L2 Uniswap interaction tx is not yet in an epoch');
263
+ }
264
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(minedReceipt.epochNumber + 1));
262
265
  await waitForProven(aztecNode, l2UniswapInteractionReceipt, { provenTimeout: 300 });
263
266
 
264
267
  // 5. Consume L2 to L1 message by calling uniswapPortal.swap_private()
@@ -266,11 +269,19 @@ export const uniswapL1L2TestSuite = (
266
269
  const daiL1BalanceOfPortalBeforeSwap = await daiCrossChainHarness.getL1BalanceOf(
267
270
  daiCrossChainHarness.tokenPortalAddress,
268
271
  );
269
- const withdrawResult = (await computeL2ToL1MembershipWitness(
270
- aztecNode,
271
- withdrawLeaf,
272
- l2UniswapInteractionReceipt.txHash,
273
- ))!;
272
+ const swapResult = await retryUntil(
273
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, swapPrivateLeaf),
274
+ 'swap private l2 to l1 witness',
275
+ 60,
276
+ 1,
277
+ );
278
+ const { epochNumber: epoch, numCheckpointsInEpoch } = swapResult;
279
+ const withdrawResult = await retryUntil(
280
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, withdrawLeaf),
281
+ 'withdraw l2 to l1 witness',
282
+ 60,
283
+ 1,
284
+ );
274
285
 
275
286
  const swapPrivateL2MessageIndex = swapResult.leafIndex;
276
287
  const swapPrivateSiblingPath = swapResult.siblingPath;
@@ -280,6 +291,7 @@ export const uniswapL1L2TestSuite = (
280
291
 
281
292
  const withdrawMessageMetadata = {
282
293
  _epoch: BigInt(epoch),
294
+ _numCheckpointsInEpoch: BigInt(withdrawResult.numCheckpointsInEpoch),
283
295
  _leafIndex: BigInt(withdrawL2MessageIndex),
284
296
  _path: withdrawSiblingPath
285
297
  .toBufferArray()
@@ -288,6 +300,7 @@ export const uniswapL1L2TestSuite = (
288
300
 
289
301
  const swapPrivateMessageMetadata = {
290
302
  _epoch: BigInt(epoch),
303
+ _numCheckpointsInEpoch: BigInt(numCheckpointsInEpoch),
291
304
  _leafIndex: BigInt(swapPrivateL2MessageIndex),
292
305
  _path: swapPrivateSiblingPath
293
306
  .toBufferArray()
@@ -345,10 +358,8 @@ export const uniswapL1L2TestSuite = (
345
358
  logger.info('WETH balance after swap : ', wethL2BalanceAfterSwap.toString());
346
359
  logger.info('DAI balance after swap : ', daiL2BalanceAfterSwap.toString());
347
360
  });
348
- // docs:end:uniswap_private
349
361
 
350
362
  // TODO(#7463): reenable look into this failure https://github.com/AztecProtocol/aztec-packages/actions/runs/9912612912/job/27388320150?pr=7462
351
- // // docs:start:uniswap_public
352
363
  // it('should uniswap trade on L1 from L2 funds publicly (swaps WETH -> DAI)', async () => {
353
364
  // const wethL1BeforeBalance = await wethCrossChainHarness.getL1BalanceOf(ownerEthAddress);
354
365
 
@@ -581,7 +592,6 @@ export const uniswapL1L2TestSuite = (
581
592
  // logger.info('WETH balance after swap : ', wethL2BalanceAfterSwap.toString());
582
593
  // logger.info('DAI balance after swap : ', daiL2BalanceAfterSwap.toString());
583
594
  // });
584
- // // docs:end:uniswap_public
585
595
 
586
596
  // Edge cases for the private flow:
587
597
  // note - tests for uniswapPortal.sol and minting asset on L2 are covered in other tests.
@@ -847,9 +857,31 @@ export const uniswapL1L2TestSuite = (
847
857
  chainId: new Fr(l1Client.chain.id),
848
858
  });
849
859
 
850
- const swapResult = (await computeL2ToL1MembershipWitness(aztecNode, swapPrivateLeaf, withdrawReceipt.txHash))!;
851
- const { epochNumber: epoch } = swapResult;
852
- const withdrawResult = (await computeL2ToL1MembershipWitness(aztecNode, withdrawLeaf, withdrawReceipt.txHash))!;
860
+ // ensure that user's funds were burnt
861
+ await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge);
862
+
863
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch
864
+ // before we can ask the witness helper for a covering root.
865
+ const minedReceipt = await aztecNode.getTxReceipt(withdrawReceipt.txHash);
866
+ if (minedReceipt.epochNumber === undefined) {
867
+ throw new Error('Withdraw tx is not yet in an epoch');
868
+ }
869
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(minedReceipt.epochNumber + 1));
870
+ await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
871
+
872
+ const swapResult = await retryUntil(
873
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, swapPrivateLeaf),
874
+ 'swap private l2 to l1 witness',
875
+ 60,
876
+ 1,
877
+ );
878
+ const { epochNumber: epoch, numCheckpointsInEpoch } = swapResult;
879
+ const withdrawResult = await retryUntil(
880
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, withdrawLeaf),
881
+ 'withdraw l2 to l1 witness',
882
+ 60,
883
+ 1,
884
+ );
853
885
 
854
886
  const swapPrivateL2MessageIndex = swapResult.leafIndex;
855
887
  const swapPrivateSiblingPath = swapResult.siblingPath;
@@ -859,6 +891,7 @@ export const uniswapL1L2TestSuite = (
859
891
 
860
892
  const withdrawMessageMetadata = {
861
893
  _epoch: BigInt(epoch),
894
+ _numCheckpointsInEpoch: BigInt(withdrawResult.numCheckpointsInEpoch),
862
895
  _leafIndex: BigInt(withdrawL2MessageIndex),
863
896
  _path: withdrawSiblingPath
864
897
  .toBufferArray()
@@ -867,19 +900,13 @@ export const uniswapL1L2TestSuite = (
867
900
 
868
901
  const swapPrivateMessageMetadata = {
869
902
  _epoch: BigInt(epoch),
903
+ _numCheckpointsInEpoch: BigInt(numCheckpointsInEpoch),
870
904
  _leafIndex: BigInt(swapPrivateL2MessageIndex),
871
905
  _path: swapPrivateSiblingPath
872
906
  .toBufferArray()
873
907
  .map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
874
908
  };
875
909
 
876
- // ensure that user's funds were burnt
877
- await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge);
878
-
879
- // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
880
- await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
881
- await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
882
-
883
910
  // On L1 call swap_public!
884
911
  logger.info('call swap_public on L1');
885
912
  const swapArgs = [
@@ -979,9 +1006,31 @@ export const uniswapL1L2TestSuite = (
979
1006
  chainId: new Fr(l1Client.chain.id),
980
1007
  });
981
1008
 
982
- const swapResult = (await computeL2ToL1MembershipWitness(aztecNode, swapPublicLeaf, withdrawReceipt.txHash))!;
983
- const { epochNumber: epoch } = swapResult;
984
- const withdrawResult = (await computeL2ToL1MembershipWitness(aztecNode, withdrawLeaf, withdrawReceipt.txHash))!;
1009
+ // check weth balance of owner on L2 (we first bridged `wethAmountToBridge` into L2 and now withdrew it!)
1010
+ await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, 0n);
1011
+
1012
+ // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch
1013
+ // before we can ask the witness helper for a covering root.
1014
+ const minedReceipt = await aztecNode.getTxReceipt(withdrawReceipt.txHash);
1015
+ if (minedReceipt.epochNumber === undefined) {
1016
+ throw new Error('Withdraw tx is not yet in an epoch');
1017
+ }
1018
+ await cheatCodes.rollup.advanceToEpoch(EpochNumber(minedReceipt.epochNumber + 1));
1019
+ await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
1020
+
1021
+ const swapResult = await retryUntil(
1022
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, swapPublicLeaf),
1023
+ 'swap public l2 to l1 witness',
1024
+ 60,
1025
+ 1,
1026
+ );
1027
+ const { epochNumber: epoch, numCheckpointsInEpoch } = swapResult;
1028
+ const withdrawResult = await retryUntil(
1029
+ () => aztecNode.getL2ToL1MembershipWitness(minedReceipt.txHash, withdrawLeaf),
1030
+ 'withdraw l2 to l1 witness',
1031
+ 60,
1032
+ 1,
1033
+ );
985
1034
 
986
1035
  const swapPublicL2MessageIndex = swapResult.leafIndex;
987
1036
  const swapPublicSiblingPath = swapResult.siblingPath;
@@ -991,6 +1040,7 @@ export const uniswapL1L2TestSuite = (
991
1040
 
992
1041
  const withdrawMessageMetadata = {
993
1042
  _epoch: BigInt(epoch),
1043
+ _numCheckpointsInEpoch: BigInt(withdrawResult.numCheckpointsInEpoch),
994
1044
  _leafIndex: BigInt(withdrawL2MessageIndex),
995
1045
  _path: withdrawSiblingPath
996
1046
  .toBufferArray()
@@ -999,19 +1049,13 @@ export const uniswapL1L2TestSuite = (
999
1049
 
1000
1050
  const swapPublicMessageMetadata = {
1001
1051
  _epoch: BigInt(epoch),
1052
+ _numCheckpointsInEpoch: BigInt(numCheckpointsInEpoch),
1002
1053
  _leafIndex: BigInt(swapPublicL2MessageIndex),
1003
1054
  _path: swapPublicSiblingPath
1004
1055
  .toBufferArray()
1005
1056
  .map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[],
1006
1057
  };
1007
1058
 
1008
- // check weth balance of owner on L2 (we first bridged `wethAmountToBridge` into L2 and now withdrew it!)
1009
- await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, 0n);
1010
-
1011
- // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch.
1012
- await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
1013
- await waitForProven(aztecNode, withdrawReceipt, { provenTimeout: 300 });
1014
-
1015
1059
  // Call swap_private on L1
1016
1060
  logger.info('Execute withdraw and swap on the uniswapPortal!');
1017
1061
 
@@ -0,0 +1,23 @@
1
+ import type { Fr } from '@aztec/aztec.js/fields';
2
+ import type { AztecNode } from '@aztec/aztec.js/node';
3
+ import { retryUntil } from '@aztec/foundation/retry';
4
+
5
+ /**
6
+ * Waits until the archiver has seen the L1 to L2 message and indexed it. Unlike
7
+ * {@link waitForL1ToL2MessageReady} from `@aztec/aztec.js/messaging`, this does not
8
+ * require the L2 chain to have advanced to the message's checkpoint — it only confirms
9
+ * the message has been picked up from L1. Use this in tests that explicitly produce L2
10
+ * blocks afterwards to make the message consumable.
11
+ */
12
+ export function waitForL1ToL2MessageSeen(
13
+ node: Pick<AztecNode, 'getL1ToL2MessageCheckpoint'>,
14
+ l1ToL2MessageHash: Fr,
15
+ opts: { timeoutSeconds: number },
16
+ ) {
17
+ return retryUntil(
18
+ async () => (await node.getL1ToL2MessageCheckpoint(l1ToL2MessageHash)) !== undefined,
19
+ `L1 to L2 message ${l1ToL2MessageHash.toString()} seen`,
20
+ opts.timeoutSeconds,
21
+ 1,
22
+ );
23
+ }
@@ -7,6 +7,7 @@ import { SlotNumber } from '@aztec/foundation/branded-types';
7
7
  import { poseidon2Hash } from '@aztec/foundation/crypto/poseidon';
8
8
  import type { TestDateProvider } from '@aztec/foundation/timer';
9
9
  import type { LendingContract } from '@aztec/noir-contracts.js/Lending';
10
+ import type { AztecNode, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
10
11
 
11
12
  import type { TokenSimulator } from './token_simulator.js';
12
13
 
@@ -92,15 +93,25 @@ export class LendingSimulator {
92
93
  public stableCoin: TokenSimulator,
93
94
  ) {}
94
95
 
95
- async prepare() {
96
+ prepare() {
96
97
  this.accumulator = BASE;
97
- const slot = await this.rollup.getSlotAt(
98
- BigInt(await this.cc.eth.lastBlockTimestamp()) + BigInt(this.ethereumSlotDuration),
99
- );
100
- this.time = Number(await this.rollup.getTimestampForSlot(slot));
98
+ this.time = 0;
101
99
  }
102
100
 
103
- async progressSlots(diff: number, dateProvider?: TestDateProvider) {
101
+ /**
102
+ * Advances the simulator's accumulator and clock to match a block timestamp observed on chain.
103
+ * Call this BEFORE applying any accumulator-sensitive mutation (borrow/repay) so the mutation
104
+ * sees the same accumulator as the contract did during execution.
105
+ */
106
+ observeBlockTimestamp(ts: number) {
107
+ const diff = ts - this.time;
108
+ if (diff > 0) {
109
+ this.accumulator = muldivDown(this.accumulator, computeMultiplier(this.rate, BigInt(diff)), BASE);
110
+ }
111
+ this.time = ts;
112
+ }
113
+
114
+ async progressSlots(diff: number, _dateProvider?: TestDateProvider, node?: AztecNode & AztecNodeDebug) {
104
115
  if (diff <= 1) {
105
116
  return;
106
117
  }
@@ -108,16 +119,19 @@ export class LendingSimulator {
108
119
  const slot = await this.rollup.getSlotAt(BigInt(await this.cc.eth.lastBlockTimestamp()));
109
120
  const targetSlot = SlotNumber(slot + diff);
110
121
  const ts = Number(await this.rollup.getTimestampForSlot(targetSlot));
111
- const timeDiff = ts - this.time;
112
- this.time = ts;
113
122
 
114
- // Mine ethereum blocks such that the next block will be in a new slot
115
- await this.cc.eth.warp(this.time - this.ethereumSlotDuration);
116
- if (dateProvider) {
117
- dateProvider.setTime(this.time * 1000);
123
+ // Queue-aware warp under AutomineSequencer: atomic warp + mineBlock that advances L2 time to the
124
+ // target slot. The cheat code routes through the AutomineSequencer queue when one is installed,
125
+ // and otherwise falls back to a manual warp + mineBlock loop.
126
+ if (node) {
127
+ await this.cc.warpL2TimeAtLeastTo(node, ts);
128
+ } else {
129
+ await this.cc.eth.warp(ts - this.ethereumSlotDuration);
118
130
  }
131
+
132
+ // Mark the latest checkpoint as proven so the rollup does not reorg pending checkpoints when
133
+ // time jumps far enough forward to cross an unproven epoch boundary.
119
134
  await this.cc.rollup.markAsProven(await this.rollup.getCheckpointNumber());
120
- this.accumulator = muldivDown(this.accumulator, computeMultiplier(this.rate, BigInt(timeDiff)), BASE);
121
135
  }
122
136
 
123
137
  depositPrivate(from: AztecAddress, onBehalfOf: Fr, amount: bigint) {
@@ -110,7 +110,7 @@ export class TokenSimulator {
110
110
  chunk(calls, 5).map(batch => new BatchCall(this.defaultWallet, batch).simulate({ from: this.defaultAddress })),
111
111
  )
112
112
  )
113
- .flat()
113
+ .flatMap(r => r.result)
114
114
  .map(r => r.result);
115
115
  expect(results[0]).toEqual(this.totalSupply);
116
116
 
@@ -6,14 +6,16 @@ import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
6
6
  import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
7
7
  import { type FeePaymentMethod, SponsoredFeePaymentMethod } from '@aztec/aztec.js/fee';
8
8
  import { Fr } from '@aztec/aztec.js/fields';
9
+ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
9
10
  import { type AztecNode, createAztecNodeClient, waitForTx } from '@aztec/aztec.js/node';
10
11
  import type { Wallet } from '@aztec/aztec.js/wallet';
11
12
  import { createEthereumChain } from '@aztec/ethereum/chain';
12
13
  import { createExtendedL1Client } from '@aztec/ethereum/client';
13
14
  import type { Logger } from '@aztec/foundation/log';
14
- import { retryUntil } from '@aztec/foundation/retry';
15
+ import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
15
16
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
16
17
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
18
+ import { TxStatus } from '@aztec/stdlib/tx';
17
19
  import { registerInitialLocalNetworkAccountsInWallet } from '@aztec/wallets/testing';
18
20
 
19
21
  import { getACVMConfig } from '../fixtures/get_acvm_config.js';
@@ -138,38 +140,57 @@ async function deployAccountWithDiagnostics(
138
140
  estimateGas?: boolean,
139
141
  ): Promise<void> {
140
142
  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;
143
+ let gasSettings: any;
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}`);
172
148
  }
149
+
150
+ // Track the tx hash across retries so we don't re-send when the previous tx is still pending.
151
+ let sentTxHash: { txHash: any } | undefined;
152
+
153
+ await retry(
154
+ async () => {
155
+ // Check if already deployed (handles case where previous attempt succeeded but waitForTx timed out)
156
+ const existing = await aztecNode.getContract(account.address);
157
+ if (existing) {
158
+ logger.info(`${accountLabel} already deployed at ${account.address}, skipping`);
159
+ return;
160
+ }
161
+
162
+ // If we already sent a tx, check if it was dropped before deciding to re-send.
163
+ if (sentTxHash) {
164
+ const prevReceipt = await aztecNode.getTxReceipt(sentTxHash.txHash);
165
+ if (prevReceipt.isDropped()) {
166
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} was dropped, re-sending`);
167
+ sentTxHash = undefined;
168
+ } else {
169
+ logger.info(`${accountLabel} previous tx ${sentTxHash.txHash} still pending, waiting again...`);
170
+ }
171
+ }
172
+
173
+ if (!sentTxHash) {
174
+ const deployResult = await deployMethod.send({
175
+ from: NO_FROM,
176
+ fee: { paymentMethod, gasSettings },
177
+ wait: NO_WAIT,
178
+ });
179
+ sentTxHash = { txHash: deployResult.txHash };
180
+ logger.info(`${accountLabel} tx sent`, { txHash: sentTxHash.txHash.toString() });
181
+ }
182
+
183
+ const receipt = await waitForTx(aztecNode, sentTxHash.txHash, { timeout: 600 });
184
+ if (receipt.isDropped()) {
185
+ sentTxHash = undefined;
186
+ throw new Error(`${accountLabel} tx was dropped, retrying...`);
187
+ }
188
+ logger.info(`${accountLabel} deployed at ${account.address}`);
189
+ },
190
+ `deploy ${accountLabel}`,
191
+ makeBackoff([1, 2, 4, 8, 16]),
192
+ logger,
193
+ );
173
194
  }
174
195
 
175
196
  async function deployAccountsInBatches(
@@ -313,9 +334,7 @@ async function bridgeL1FeeJuice(
313
334
  const portal = await L1FeeJuicePortalManager.new(aztecNode, l1Client, log);
314
335
  const claim = await portal.bridgeTokensPublic(recipient, amount, true /* mint */);
315
336
 
316
- const isSynced = async () =>
317
- (await aztecNode.getL1ToL2MessageCheckpoint(Fr.fromHexString(claim.messageHash))) !== undefined;
318
- await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 0.5);
337
+ await waitForL1ToL2MessageReady(aztecNode, Fr.fromHexString(claim.messageHash), { timeoutSeconds: 24 });
319
338
 
320
339
  log.info(`Created a claim for ${amount} L1 fee juice to ${recipient}.`, claim);
321
340
  return claim;
@@ -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;
@@ -404,7 +427,9 @@ export async function performTransfers({
404
427
 
405
428
  const provenTxs = await Promise.all(txs);
406
429
 
407
- await Promise.all(provenTxs.map(t => t.send({ wait: { timeout: 600 } })));
430
+ // Wait only for the txs to be proposed, not checkpointed. This is enough to keep the chain
431
+ // loaded for the reorg scenario, and avoids each round blocking on the (slower) checkpoint lag.
432
+ await Promise.all(provenTxs.map(t => t.send({ wait: { timeout: 600, waitForStatus: TxStatus.PROPOSED } })));
408
433
 
409
434
  logger.info(`Completed round ${i + 1} / ${rounds}`);
410
435
  }