@aztec/ethereum 3.0.0-nightly.20251003 → 3.0.0-nightly.20251004

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 (37) hide show
  1. package/dest/config.js +1 -1
  2. package/dest/contracts/multicall.d.ts +2 -2
  3. package/dest/contracts/multicall.d.ts.map +1 -1
  4. package/dest/contracts/rollup.d.ts +1 -1
  5. package/dest/contracts/rollup.d.ts.map +1 -1
  6. package/dest/contracts/rollup.js +1 -1
  7. package/dest/deploy_l1_contracts.d.ts +2 -2
  8. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  9. package/dest/l1_tx_utils/config.d.ts +10 -7
  10. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  11. package/dest/l1_tx_utils/config.js +12 -6
  12. package/dest/l1_tx_utils/l1_tx_utils.d.ts +16 -4
  13. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  14. package/dest/l1_tx_utils/l1_tx_utils.js +259 -129
  15. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +2 -2
  16. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  17. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +10 -20
  18. package/dest/l1_tx_utils/types.d.ts +11 -2
  19. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  20. package/dest/l1_tx_utils/types.js +17 -0
  21. package/dest/publisher_manager.d.ts.map +1 -1
  22. package/dest/publisher_manager.js +16 -6
  23. package/dest/test/eth_cheat_codes.d.ts +18 -1
  24. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  25. package/dest/test/eth_cheat_codes.js +101 -22
  26. package/package.json +5 -5
  27. package/src/config.ts +1 -1
  28. package/src/contracts/multicall.ts +3 -6
  29. package/src/contracts/rollup.ts +2 -2
  30. package/src/deploy_l1_contracts.ts +2 -2
  31. package/src/l1_tx_utils/README.md +177 -0
  32. package/src/l1_tx_utils/config.ts +24 -13
  33. package/src/l1_tx_utils/l1_tx_utils.ts +282 -158
  34. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +23 -19
  35. package/src/l1_tx_utils/types.ts +20 -2
  36. package/src/publisher_manager.ts +24 -5
  37. package/src/test/eth_cheat_codes.ts +120 -20
@@ -1,4 +1,4 @@
1
- import { times } from '@aztec/foundation/collection';
1
+ import { getKeys, merge, pick, times } from '@aztec/foundation/collection';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import { makeBackoff, retry } from '@aztec/foundation/retry';
4
4
  import { DateProvider } from '@aztec/foundation/timer';
@@ -34,7 +34,7 @@ import type { GasPrice, L1BlobInputs, L1TxRequest, TransactionStats } from './ty
34
34
  import { getCalldataGasUsage, tryGetCustomErrorNameContractFunction } from './utils.js';
35
35
 
36
36
  export class ReadOnlyL1TxUtils {
37
- public config: L1TxUtilsConfig;
37
+ public config: Required<L1TxUtilsConfig>;
38
38
  protected interrupted = false;
39
39
 
40
40
  constructor(
@@ -44,10 +44,7 @@ export class ReadOnlyL1TxUtils {
44
44
  config?: Partial<L1TxUtilsConfig>,
45
45
  protected debugMaxGasLimit: boolean = false,
46
46
  ) {
47
- this.config = {
48
- ...defaultL1TxUtilsConfig,
49
- ...(config || {}),
50
- };
47
+ this.config = merge(defaultL1TxUtilsConfig, pick(config || {}, ...getKeys(l1TxUtilsConfigMappings)));
51
48
  }
52
49
 
53
50
  public interrupt() {
@@ -70,12 +67,12 @@ export class ReadOnlyL1TxUtils {
70
67
  * Gets the current gas price with bounds checking
71
68
  */
72
69
  public async getGasPrice(
73
- _gasConfig?: L1TxUtilsConfig,
70
+ gasConfigOverrides?: L1TxUtilsConfig,
74
71
  isBlobTx: boolean = false,
75
72
  attempt: number = 0,
76
73
  previousGasPrice?: typeof attempt extends 0 ? never : GasPrice,
77
74
  ): Promise<GasPrice> {
78
- const gasConfig = { ...this.config, ..._gasConfig };
75
+ const gasConfig = merge(this.config, gasConfigOverrides);
79
76
  const block = await this.client.getBlock({ blockTag: 'latest' });
80
77
  const baseFee = block.baseFeePerGas ?? 0n;
81
78
 
@@ -90,7 +87,6 @@ export class ReadOnlyL1TxUtils {
90
87
  this.logger,
91
88
  true,
92
89
  );
93
- this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
94
90
  } catch {
95
91
  this.logger?.warn('Failed to get L1 blob base fee', attempt);
96
92
  }
@@ -174,13 +170,17 @@ export class ReadOnlyL1TxUtils {
174
170
  maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
175
171
  }
176
172
 
177
- this.logger?.debug(`Computed L1 gas price`, {
178
- attempt,
179
- baseFee: formatGwei(baseFee),
180
- maxFeePerGas: formatGwei(maxFeePerGas),
181
- maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
182
- ...(maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(maxFeePerBlobGas) }),
183
- });
173
+ this.logger?.trace(
174
+ `Computed L1 gas price max fee ${formatGwei(maxFeePerGas)} and max priority fee ${formatGwei(maxPriorityFeePerGas)}`,
175
+ {
176
+ attempt,
177
+ baseFee: formatGwei(baseFee),
178
+ maxFeePerGas: formatGwei(maxFeePerGas),
179
+ maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
180
+ blobBaseFee: formatGwei(blobBaseFee),
181
+ maxFeePerBlobGas: formatGwei(maxFeePerBlobGas),
182
+ },
183
+ );
184
184
 
185
185
  return {
186
186
  maxFeePerGas,
@@ -211,10 +211,10 @@ export class ReadOnlyL1TxUtils {
211
211
  gas: LARGE_GAS_LIMIT,
212
212
  });
213
213
 
214
- this.logger?.debug(`L1 gas used in estimateGas by blob tx: ${initialEstimate}`);
214
+ this.logger?.trace(`Estimated gas for blob tx: ${initialEstimate}`);
215
215
  } else {
216
216
  initialEstimate = await this.client.estimateGas({ account, ...request, gas: LARGE_GAS_LIMIT });
217
- this.logger?.debug(`L1 gas used in estimateGas by non-blob tx: ${initialEstimate}`);
217
+ this.logger?.trace(`Estimated gas for non-blob tx: ${initialEstimate}`);
218
218
  }
219
219
 
220
220
  // Add buffer based on either fixed amount or percentage
@@ -362,7 +362,11 @@ export class ReadOnlyL1TxUtils {
362
362
  const bumpedGasLimit = gasLimit + (gasLimit * BigInt((gasConfig?.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
363
363
 
364
364
  const cleanGasConfig = pickBy(gasConfig, (_, key) => key in l1TxUtilsConfigMappings);
365
- this.logger?.debug('Bumping gas limit', { gasLimit, gasConfig: cleanGasConfig, bumpedGasLimit });
365
+ this.logger?.trace(`Bumping gas limit from ${gasLimit} to ${bumpedGasLimit}`, {
366
+ gasLimit,
367
+ gasConfig: cleanGasConfig,
368
+ bumpedGasLimit,
369
+ });
366
370
  return bumpedGasLimit;
367
371
  }
368
372
  }
@@ -13,7 +13,7 @@ export interface L1TxRequest {
13
13
  abi?: Abi;
14
14
  }
15
15
 
16
- export type L1GasConfig = Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date };
16
+ export type L1TxConfig = Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date };
17
17
 
18
18
  export interface L1BlobInputs {
19
19
  blobs: Uint8Array[];
@@ -47,15 +47,19 @@ export enum TxUtilsState {
47
47
  MINED,
48
48
  }
49
49
 
50
+ export const TerminalTxUtilsState = [TxUtilsState.IDLE, TxUtilsState.MINED, TxUtilsState.NOT_MINED];
51
+
50
52
  export type L1TxState = {
51
53
  txHashes: Hex[];
52
54
  cancelTxHashes: Hex[];
53
55
  gasLimit: bigint;
54
56
  gasPrice: GasPrice;
55
- txConfig: L1GasConfig;
57
+ txConfigOverrides: L1TxConfig;
56
58
  request: L1TxRequest;
57
59
  status: TxUtilsState;
58
60
  nonce: number;
61
+ sentAtL1Ts: Date;
62
+ lastSentAtL1Ts: Date;
59
63
  receipt?: TransactionReceipt;
60
64
  blobInputs: L1BlobInputs | undefined;
61
65
  };
@@ -64,3 +68,17 @@ export type SigningCallback = (
64
68
  transaction: TransactionSerializable,
65
69
  signingAddress: EthAddress,
66
70
  ) => Promise<ViemTransactionSignature>;
71
+
72
+ export class UnknownMinedTxError extends Error {
73
+ constructor(nonce: number, account: string) {
74
+ super(`Nonce ${nonce} from account ${account} is MINED but not by one of our expected transactions`);
75
+ this.name = 'UnknownMinedTxError';
76
+ }
77
+ }
78
+
79
+ export class DroppedTransactionError extends Error {
80
+ constructor(nonce: number, account: string) {
81
+ super(`Transaction with nonce ${nonce} from account ${account} was dropped from the mempool`);
82
+ this.name = 'DroppedTransactionError';
83
+ }
84
+ }
@@ -3,8 +3,27 @@ import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  import { L1TxUtils, TxUtilsState } from './l1_tx_utils/index.js';
5
5
 
6
- const sortOrder = [TxUtilsState.IDLE, TxUtilsState.MINED];
7
- const invalidStates = [TxUtilsState.SENT, TxUtilsState.SPEED_UP, TxUtilsState.CANCELLED, TxUtilsState.NOT_MINED]; // Cancelled and not mined are states that can be handled by a later iteration
6
+ // Defines the order in which we prioritise publishers based on their state (first is better)
7
+ const sortOrder = [
8
+ // Always prefer sending from idle publishers
9
+ TxUtilsState.IDLE,
10
+ // Then from publishers that have sent a tx and it got mined
11
+ TxUtilsState.MINED,
12
+ // Then from publishers that have sent a tx but it's in-flight
13
+ TxUtilsState.SPEED_UP,
14
+ TxUtilsState.SENT,
15
+ // We leave cancelled and not-mined states for last, since these represent failures to mines and could be problematic
16
+ TxUtilsState.CANCELLED,
17
+ TxUtilsState.NOT_MINED,
18
+ ];
19
+
20
+ // Which states represent a busy publisher that we should avoid if possible
21
+ const busyStates: TxUtilsState[] = [
22
+ TxUtilsState.SENT,
23
+ TxUtilsState.SPEED_UP,
24
+ TxUtilsState.CANCELLED,
25
+ TxUtilsState.NOT_MINED,
26
+ ];
8
27
 
9
28
  export type PublisherFilter<UtilsType extends L1TxUtils> = (utils: UtilsType) => boolean;
10
29
 
@@ -24,14 +43,14 @@ export class PublisherManager<UtilsType extends L1TxUtils = L1TxUtils> {
24
43
  // Finds and prioritises available publishers based on
25
44
  // 1. Validity as per the provided filter function
26
45
  // 2. Validity based on the state the publisher is in
27
- // 3. Priority based on state as defined bu sortOrder
46
+ // 3. Priority based on state as defined by sortOrder
28
47
  // 4. Then priority based on highest balance
29
48
  // 5. Then priority based on least recently used
30
49
  public async getAvailablePublisher(filter: PublisherFilter<UtilsType> = () => true): Promise<UtilsType> {
31
50
  // Extract the valid publishers
32
- let validPublishers = this.publishers.filter((pub: UtilsType) => !invalidStates.includes(pub.state) && filter(pub));
51
+ let validPublishers = this.publishers.filter((pub: UtilsType) => !busyStates.includes(pub.state) && filter(pub));
33
52
 
34
- // If none found but we allow invalid states, try again including them
53
+ // If none found but we allow invalid (busy) states, try again including them
35
54
  if (validPublishers.length === 0 && this.config.publisherAllowInvalidStates) {
36
55
  this.log.warn(`No valid publishers found. Trying again including invalid states.`);
37
56
  validPublishers = this.publishers.filter(pub => filter(pub));
@@ -3,9 +3,10 @@ import { keccak256 } from '@aztec/foundation/crypto';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
+ import { pluralize } from '@aztec/foundation/string';
6
7
  import type { DateProvider, TestDateProvider } from '@aztec/foundation/timer';
7
8
 
8
- import { type Hex, createPublicClient, fallback, http } from 'viem';
9
+ import { type Hex, type Transaction, createPublicClient, fallback, hexToNumber, http } from 'viem';
9
10
 
10
11
  import type { ViemPublicClient } from '../types.js';
11
12
 
@@ -33,9 +34,12 @@ export class EthCheatCodes {
33
34
  });
34
35
  }
35
36
 
36
- async rpcCall(method: string, params: any[]) {
37
- const paramsString = jsonStringify(params);
38
- this.logger.debug(`Calling ${method} with params: ${paramsString} on ${this.rpcUrls.join(', ')}`);
37
+ public rpcCall(method: string, params: any[]) {
38
+ this.logger.debug(`Calling ${method} with params: ${jsonStringify(params)} on ${this.rpcUrls.join(', ')}`);
39
+ return this.doRpcCall(method, params);
40
+ }
41
+
42
+ private async doRpcCall(method: string, params: any[]) {
39
43
  return (await this.publicClient.transport.request({
40
44
  method,
41
45
  params,
@@ -48,7 +52,7 @@ export class EthCheatCodes {
48
52
  */
49
53
  public async isAutoMining(): Promise<boolean> {
50
54
  try {
51
- const res = await this.rpcCall('anvil_getAutomine', []);
55
+ const res = await this.doRpcCall('anvil_getAutomine', []);
52
56
  return res;
53
57
  } catch (err) {
54
58
  this.logger.error(`Calling "anvil_getAutomine" failed with:`, err);
@@ -61,7 +65,7 @@ export class EthCheatCodes {
61
65
  * @returns The current block number
62
66
  */
63
67
  public async blockNumber(): Promise<number> {
64
- const res = await this.rpcCall('eth_blockNumber', []);
68
+ const res = await this.doRpcCall('eth_blockNumber', []);
65
69
  return parseInt(res, 16);
66
70
  }
67
71
 
@@ -70,7 +74,7 @@ export class EthCheatCodes {
70
74
  * @returns The current chainId
71
75
  */
72
76
  public async chainId(): Promise<number> {
73
- const res = await this.rpcCall('eth_chainId', []);
77
+ const res = await this.doRpcCall('eth_chainId', []);
74
78
  return parseInt(res, 16);
75
79
  }
76
80
 
@@ -79,7 +83,7 @@ export class EthCheatCodes {
79
83
  * @returns The current timestamp
80
84
  */
81
85
  public async timestamp(): Promise<number> {
82
- const res = await this.rpcCall('eth_getBlockByNumber', ['latest', true]);
86
+ const res = await this.doRpcCall('eth_getBlockByNumber', ['latest', true]);
83
87
  return parseInt(res.timestamp, 16);
84
88
  }
85
89
 
@@ -94,7 +98,7 @@ export class EthCheatCodes {
94
98
 
95
99
  private async doMine(numberOfBlocks = 1): Promise<void> {
96
100
  try {
97
- await this.rpcCall('hardhat_mine', [numberOfBlocks]);
101
+ await this.doRpcCall('hardhat_mine', [numberOfBlocks]);
98
102
  } catch (err) {
99
103
  throw new Error(`Error mining: ${err}`);
100
104
  }
@@ -105,7 +109,8 @@ export class EthCheatCodes {
105
109
  */
106
110
  public async evmMine(): Promise<void> {
107
111
  try {
108
- await this.rpcCall('evm_mine', []);
112
+ await this.doRpcCall('evm_mine', []);
113
+ this.logger.warn(`Mined 1 L1 block with evm_mine`);
109
114
  } catch (err) {
110
115
  throw new Error(`Error mining: ${err}`);
111
116
  }
@@ -126,7 +131,7 @@ export class EthCheatCodes {
126
131
  }
127
132
 
128
133
  public async getBalance(account: EthAddress | Hex): Promise<bigint> {
129
- const res = await this.rpcCall('eth_getBalance', [account.toString(), 'latest']);
134
+ const res = await this.doRpcCall('eth_getBalance', [account.toString(), 'latest']);
130
135
  return BigInt(res);
131
136
  }
132
137
 
@@ -162,7 +167,7 @@ export class EthCheatCodes {
162
167
  */
163
168
  public getIntervalMining(): Promise<number | null> {
164
169
  try {
165
- return this.rpcCall('anvil_getIntervalMining', []);
170
+ return this.doRpcCall('anvil_getIntervalMining', []);
166
171
  } catch (err) {
167
172
  throw new Error(`Error getting interval mining: ${err}`);
168
173
  }
@@ -363,8 +368,7 @@ export class EthCheatCodes {
363
368
  * @returns The bytecode for the contract
364
369
  */
365
370
  public async getBytecode(contract: EthAddress): Promise<`0x${string}`> {
366
- const res = await this.rpcCall('eth_getCode', [contract.toString(), 'latest']);
367
- return res;
371
+ return await this.doRpcCall('eth_getCode', [contract.toString(), 'latest']);
368
372
  }
369
373
 
370
374
  /**
@@ -373,8 +377,7 @@ export class EthCheatCodes {
373
377
  * @returns The raw transaction
374
378
  */
375
379
  public async getRawTransaction(txHash: Hex): Promise<`0x${string}`> {
376
- const res = await this.rpcCall('debug_getRawTransaction', [txHash]);
377
- return res;
380
+ return await this.doRpcCall('debug_getRawTransaction', [txHash]);
378
381
  }
379
382
 
380
383
  /**
@@ -383,8 +386,7 @@ export class EthCheatCodes {
383
386
  * @returns The trace
384
387
  */
385
388
  public async debugTraceTransaction(txHash: Hex): Promise<any> {
386
- const res = await this.rpcCall('debug_traceTransaction', [txHash]);
387
- return res;
389
+ return await this.doRpcCall('debug_traceTransaction', [txHash]);
388
390
  }
389
391
 
390
392
  /**
@@ -402,7 +404,6 @@ export class EthCheatCodes {
402
404
  * @param blockNumber - The block number that's going to be the new tip
403
405
  */
404
406
  public reorgTo(blockNumber: number): Promise<void> {
405
- this.logger.info('reorgTo', { blockNumber });
406
407
  if (blockNumber <= 0) {
407
408
  throw new Error(`Can't reorg to block before genesis: ${blockNumber}`);
408
409
  }
@@ -418,6 +419,7 @@ export class EthCheatCodes {
418
419
 
419
420
  const depth = Number(currentTip - BigInt(blockNumber) + 1n);
420
421
  await this.rpcCall('anvil_rollback', [depth]);
422
+ this.logger.warn(`Reorged L1 chain to block number ${blockNumber} (depth ${depth})`);
421
423
  });
422
424
  }
423
425
 
@@ -444,7 +446,70 @@ export class EthCheatCodes {
444
446
  }
445
447
 
446
448
  public traceTransaction(txHash: Hex): Promise<any> {
447
- return this.rpcCall('trace_transaction', [txHash]);
449
+ return this.doRpcCall('trace_transaction', [txHash]);
450
+ }
451
+
452
+ public async getTxPoolStatus(): Promise<{ pending: number; queued: number }> {
453
+ const { pending, queued } = await this.doRpcCall('txpool_status', []);
454
+ return { pending: hexToNumber(pending), queued: hexToNumber(queued) };
455
+ }
456
+
457
+ public async getTxPoolContents(): Promise<TxPoolTransaction[]> {
458
+ const txpoolContent = await this.doRpcCall('txpool_content', []);
459
+ return mapTxPoolContent(txpoolContent);
460
+ }
461
+
462
+ /**
463
+ * Mines an empty block by temporarily removing all pending transactions from the mempool,
464
+ * mining a block, and then re-adding the transactions back to the pool.
465
+ */
466
+ public async mineEmptyBlock(blockCount: number = 1): Promise<void> {
467
+ await this.execWithPausedAnvil(async () => {
468
+ // Get all pending and queued transactions from the pool
469
+ const txs = await this.getTxPoolContents();
470
+
471
+ this.logger.debug(`Found ${txs.length} transactions in pool`);
472
+
473
+ // Get raw transactions before dropping them
474
+ const rawTxs: Hex[] = [];
475
+ for (const tx of txs) {
476
+ try {
477
+ const rawTx = await this.doRpcCall('debug_getRawTransaction', [tx.hash]);
478
+ if (rawTx) {
479
+ rawTxs.push(rawTx);
480
+ this.logger.debug(`Got raw tx for ${tx.hash}`);
481
+ } else {
482
+ this.logger.warn(`No raw tx found for ${tx.hash}`);
483
+ }
484
+ } catch {
485
+ this.logger.warn(`Failed to get raw transaction for ${tx.hash}`);
486
+ }
487
+ }
488
+
489
+ this.logger.debug(`Retrieved ${rawTxs.length} raw transactions`);
490
+
491
+ // Drop all transactions from the mempool
492
+ await this.doRpcCall('anvil_dropAllTransactions', []);
493
+
494
+ // Mine an empty block
495
+ await this.doMine(blockCount);
496
+
497
+ // Re-add the transactions to the pool
498
+ for (const rawTx of rawTxs) {
499
+ try {
500
+ const txHash = await this.doRpcCall('eth_sendRawTransaction', [rawTx]);
501
+ this.logger.debug(`Re-added transaction ${txHash}`);
502
+ } catch (err) {
503
+ this.logger.warn(`Failed to re-add transaction: ${err}`);
504
+ }
505
+ }
506
+
507
+ if (rawTxs.length !== txs.length) {
508
+ this.logger.warn(`Failed to add all txs back: had ${txs.length} but re-added ${rawTxs.length}`);
509
+ }
510
+ });
511
+
512
+ this.logger.warn(`Mined ${blockCount} empty L1 ${pluralize('block', blockCount)}`);
448
513
  }
449
514
 
450
515
  public async execWithPausedAnvil<T>(fn: () => Promise<T>): Promise<T> {
@@ -479,4 +544,39 @@ export class EthCheatCodes {
479
544
  }
480
545
  }
481
546
  }
547
+
548
+ public async syncDateProvider() {
549
+ const timestamp = await this.timestamp();
550
+ if ('setTime' in this.dateProvider) {
551
+ this.dateProvider.setTime(timestamp * 1000);
552
+ }
553
+ }
554
+ }
555
+
556
+ type TxPoolState = 'pending' | 'queued';
557
+
558
+ interface TxPoolContent {
559
+ pending: Record<Hex, Record<string, Transaction>>;
560
+ queued: Record<Hex, Record<string, Transaction>>;
561
+ }
562
+
563
+ export type TxPoolTransaction = Transaction & {
564
+ poolState: TxPoolState;
565
+ };
566
+
567
+ function mapTxPoolContent(content: TxPoolContent): TxPoolTransaction[] {
568
+ const result: TxPoolTransaction[] = [];
569
+
570
+ const processPool = (pool: Record<Hex, Record<string, Transaction>>, poolState: TxPoolState) => {
571
+ for (const txsByNonce of Object.values(pool)) {
572
+ for (const tx of Object.values(txsByNonce)) {
573
+ result.push({ ...tx, poolState });
574
+ }
575
+ }
576
+ };
577
+
578
+ processPool(content.pending, 'pending');
579
+ processPool(content.queued, 'queued');
580
+
581
+ return result;
482
582
  }