@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.
- package/dest/config.js +1 -1
- package/dest/contracts/multicall.d.ts +2 -2
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/rollup.d.ts +1 -1
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +1 -1
- package/dest/deploy_l1_contracts.d.ts +2 -2
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.d.ts +10 -7
- package/dest/l1_tx_utils/config.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +12 -6
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +16 -4
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +259 -129
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +2 -2
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +10 -20
- package/dest/l1_tx_utils/types.d.ts +11 -2
- package/dest/l1_tx_utils/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/types.js +17 -0
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +16 -6
- package/dest/test/eth_cheat_codes.d.ts +18 -1
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +101 -22
- package/package.json +5 -5
- package/src/config.ts +1 -1
- package/src/contracts/multicall.ts +3 -6
- package/src/contracts/rollup.ts +2 -2
- package/src/deploy_l1_contracts.ts +2 -2
- package/src/l1_tx_utils/README.md +177 -0
- package/src/l1_tx_utils/config.ts +24 -13
- package/src/l1_tx_utils/l1_tx_utils.ts +282 -158
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +23 -19
- package/src/l1_tx_utils/types.ts +20 -2
- package/src/publisher_manager.ts +24 -5
- 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
|
-
|
|
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 =
|
|
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?.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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?.
|
|
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?.
|
|
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?.
|
|
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
|
}
|
package/src/l1_tx_utils/types.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface L1TxRequest {
|
|
|
13
13
|
abi?: Abi;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export type
|
|
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
|
-
|
|
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
|
+
}
|
package/src/publisher_manager.ts
CHANGED
|
@@ -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
|
-
|
|
7
|
-
const
|
|
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
|
|
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) => !
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|