@aztec/ethereum 0.69.0-devnet → 0.69.1-devnet

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/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { decodeEventLog, } from 'viem';
1
+ import { BaseError, ContractFunctionRevertedError, decodeEventLog, } from 'viem';
2
2
  export function extractEvent(logs, address, abi, eventName, filter, logger) {
3
3
  const event = tryExtractEvent(logs, address, abi, eventName, filter, logger);
4
4
  if (!event) {
@@ -24,4 +24,97 @@ function tryExtractEvent(logs, address, abi, eventName, filter, logger) {
24
24
  }
25
25
  }
26
26
  }
27
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQU1MLGNBQWMsR0FDZixNQUFNLE1BQU0sQ0FBQztBQVNkLE1BQU0sVUFBVSxZQUFZLENBSzFCLElBQVcsRUFDWCxPQUFZLEVBQ1osR0FBUyxFQUNULFNBQXFCLEVBQ3JCLE1BQXFDLEVBQ3JDLE1BQWU7SUFFZixNQUFNLEtBQUssR0FBRyxlQUFlLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM3RSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxTQUFTLGlCQUFpQixPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQ3hGLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRCxTQUFTLGVBQWUsQ0FLdEIsSUFBVyxFQUNYLE9BQVksRUFDWixHQUFTLEVBQ1QsU0FBcUIsRUFDckIsTUFBcUMsRUFDckMsTUFBZTtJQUVmLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7UUFDdkIsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxLQUFLLE9BQU8sQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDO1lBQ3hELElBQUksQ0FBQztnQkFDSCxNQUFNLFlBQVksR0FBRyxjQUFjLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2dCQUNyRCxJQUFJLFlBQVksQ0FBQyxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7b0JBQ3pDLE1BQU0sYUFBYSxHQUFHLFlBQTBCLENBQUM7b0JBQ2pELElBQUksQ0FBQyxNQUFNLElBQUksTUFBTSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7d0JBQ3JDLE9BQU8sYUFBYSxDQUFDO29CQUN2QixDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDYixNQUFNLEVBQUUsSUFBSSxDQUFDLDJDQUEyQyxPQUFPLEtBQUssR0FBRyxFQUFFLENBQUMsQ0FBQztZQUM3RSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDIn0=
27
+ export function prettyLogViemErrorMsg(err) {
28
+ if (err instanceof BaseError) {
29
+ const revertError = err.walk(err => err instanceof ContractFunctionRevertedError);
30
+ if (revertError instanceof ContractFunctionRevertedError) {
31
+ const errorName = revertError.data?.errorName ?? '';
32
+ const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
33
+ return `${errorName}${args}`;
34
+ }
35
+ }
36
+ return err?.message ?? err;
37
+ }
38
+ export function formatViemError(error) {
39
+ const truncateHex = (hex, length = 100) => {
40
+ if (!hex || typeof hex !== 'string') {
41
+ return hex;
42
+ }
43
+ if (!hex.startsWith('0x')) {
44
+ return hex;
45
+ }
46
+ if (hex.length <= length * 2) {
47
+ return hex;
48
+ }
49
+ return `${hex.slice(0, length)}...${hex.slice(-length)}`;
50
+ };
51
+ const formatRequestBody = (body) => {
52
+ try {
53
+ const parsed = JSON.parse(body);
54
+ // Recursively process all parameters that might contain hex strings
55
+ const processParams = (obj) => {
56
+ if (Array.isArray(obj)) {
57
+ return obj.map(item => processParams(item));
58
+ }
59
+ if (typeof obj === 'object' && obj !== null) {
60
+ const result = {};
61
+ for (const [key, value] of Object.entries(obj)) {
62
+ result[key] = processParams(value);
63
+ }
64
+ return result;
65
+ }
66
+ if (typeof obj === 'string') {
67
+ if (obj.startsWith('0x')) {
68
+ return truncateHex(obj);
69
+ }
70
+ }
71
+ return obj;
72
+ };
73
+ // Process the entire request body
74
+ const processed = processParams(parsed);
75
+ return JSON.stringify(processed, null, 2);
76
+ }
77
+ catch {
78
+ return body;
79
+ }
80
+ };
81
+ const truncateHexStringsInText = (text) => {
82
+ const hexRegex = /\b0x[a-fA-F0-9]{10,}/g;
83
+ return text.replace(hexRegex, hex => truncateHex(hex));
84
+ };
85
+ const extractAndFormatRequestBody = (message) => {
86
+ // First handle Request body JSON
87
+ const requestBodyRegex = /Request body: ({[\s\S]*?})\n/g;
88
+ let result = message.replace(requestBodyRegex, (match, body) => {
89
+ return `Request body: ${formatRequestBody(body)}\n`;
90
+ });
91
+ // Then handle Arguments section
92
+ const argsRegex = /((?:Request |Estimate Gas )?Arguments:[\s\S]*?(?=\n\n|$))/g;
93
+ result = result.replace(argsRegex, section => {
94
+ const lines = section.split('\n');
95
+ const processedLines = lines.map(line => {
96
+ // Check if line contains a colon followed by content
97
+ const colonIndex = line.indexOf(':');
98
+ if (colonIndex !== -1) {
99
+ const [prefix, content] = [line.slice(0, colonIndex + 1), line.slice(colonIndex + 1).trim()];
100
+ // If content contains a hex string, truncate it
101
+ if (content.includes('0x')) {
102
+ const hexMatches = content.match(/0x[a-fA-F0-9]+/g) || [];
103
+ let processedContent = content;
104
+ hexMatches.forEach(hex => {
105
+ processedContent = processedContent.replace(hex, truncateHex(hex));
106
+ });
107
+ return `${prefix} ${processedContent}`;
108
+ }
109
+ }
110
+ return line;
111
+ });
112
+ return processedLines.join('\n');
113
+ });
114
+ // Finally, catch any remaining hex strings in the message
115
+ result = truncateHexStringsInText(result);
116
+ return result;
117
+ };
118
+ return JSON.stringify({ error: extractAndFormatRequestBody(error?.message || String(error)) }, null, 2).replace(/\\n/g, '\n');
119
+ }
120
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQUVMLFNBQVMsRUFFVCw2QkFBNkIsRUFJN0IsY0FBYyxHQUNmLE1BQU0sTUFBTSxDQUFDO0FBU2QsTUFBTSxVQUFVLFlBQVksQ0FLMUIsSUFBVyxFQUNYLE9BQVksRUFDWixHQUFTLEVBQ1QsU0FBcUIsRUFDckIsTUFBcUMsRUFDckMsTUFBZTtJQUVmLE1BQU0sS0FBSyxHQUFHLGVBQWUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzdFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNYLE1BQU0sSUFBSSxLQUFLLENBQUMsaUNBQWlDLFNBQVMsaUJBQWlCLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQztBQUVELFNBQVMsZUFBZSxDQUt0QixJQUFXLEVBQ1gsT0FBWSxFQUNaLEdBQVMsRUFDVCxTQUFxQixFQUNyQixNQUFxQyxFQUNyQyxNQUFlO0lBRWYsS0FBSyxNQUFNLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUN2QixJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLEtBQUssT0FBTyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7WUFDeEQsSUFBSSxDQUFDO2dCQUNILE1BQU0sWUFBWSxHQUFHLGNBQWMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLENBQUM7Z0JBQ3JELElBQUksWUFBWSxDQUFDLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDekMsTUFBTSxhQUFhLEdBQUcsWUFBMEIsQ0FBQztvQkFDakQsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQzt3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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/ethereum",
3
- "version": "0.69.0-devnet",
3
+ "version": "0.69.1-devnet",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -29,11 +29,10 @@
29
29
  "../package.common.json"
30
30
  ],
31
31
  "dependencies": {
32
- "@aztec/foundation": "0.69.0-devnet",
33
- "@aztec/l1-artifacts": "0.69.0-devnet",
32
+ "@aztec/foundation": "0.69.1-devnet",
33
+ "@aztec/l1-artifacts": "0.69.1-devnet",
34
34
  "@viem/anvil": "^0.0.10",
35
35
  "dotenv": "^16.0.3",
36
- "get-port": "^7.1.0",
37
36
  "tslib": "^2.4.0",
38
37
  "viem": "^2.7.15",
39
38
  "zod": "^3.23.8"
@@ -404,7 +404,7 @@ export const deployL1Contracts = async (
404
404
  logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
405
405
 
406
406
  const slashFactoryAddress = await deployer.deploy(l1Artifacts.slashFactory, [rollupAddress.toString()]);
407
- logger.info(`Deployed SlashFactory at ${slashFactoryAddress}`);
407
+ logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
408
408
 
409
409
  await deployer.waitForDeployments();
410
410
  logger.verbose(`All core contracts have been deployed`);
@@ -762,7 +762,7 @@ export async function deployL1Contract(
762
762
  } else {
763
763
  // Regular deployment path
764
764
  const deployData = encodeDeployData({ abi, bytecode, args });
765
- const receipt = await l1TxUtils.sendAndMonitorTransaction({
765
+ const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
766
766
  to: null,
767
767
  data: deployData,
768
768
  });
@@ -22,6 +22,8 @@ import {
22
22
  formatGwei,
23
23
  } from 'viem';
24
24
 
25
+ import { formatViemError } from './utils.js';
26
+
25
27
  // 1_000_000_000 Gwei = 1 ETH
26
28
  // 1_000_000_000 Wei = 1 Gwei
27
29
  // 1_000_000_000_000_000_000 Wei = 1 ETH
@@ -30,7 +32,11 @@ const WEI_CONST = 1_000_000_000n;
30
32
 
31
33
  // setting a minimum bump percentage to 10% due to geth's implementation
32
34
  // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
33
- const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10n;
35
+ const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10;
36
+
37
+ // setting a minimum bump percentage to 100% due to geth's implementation
38
+ // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/blobpool/config.go#L34
39
+ const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100;
34
40
 
35
41
  // Avg ethereum block time is ~12s
36
42
  const BLOCK_TIME_MS = 12_000;
@@ -39,7 +45,7 @@ export interface L1TxUtilsConfig {
39
45
  /**
40
46
  * How much to increase calculated gas limit.
41
47
  */
42
- gasLimitBufferPercentage?: bigint;
48
+ gasLimitBufferPercentage?: number;
43
49
  /**
44
50
  * Maximum gas price in gwei
45
51
  */
@@ -48,14 +54,22 @@ export interface L1TxUtilsConfig {
48
54
  * Minimum gas price in gwei
49
55
  */
50
56
  minGwei?: bigint;
57
+ /**
58
+ * Maximum blob fee per gas in gwei
59
+ */
60
+ maxBlobGwei?: bigint;
51
61
  /**
52
62
  * Priority fee bump percentage
53
63
  */
54
- priorityFeeBumpPercentage?: bigint;
64
+ priorityFeeBumpPercentage?: number;
55
65
  /**
56
66
  * How much to increase priority fee by each attempt (percentage)
57
67
  */
58
- priorityFeeRetryBumpPercentage?: bigint;
68
+ priorityFeeRetryBumpPercentage?: number;
69
+ /**
70
+ * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage config
71
+ */
72
+ fixedPriorityFeePerGas?: number;
59
73
  /**
60
74
  * Maximum number of speed-up attempts
61
75
  */
@@ -83,7 +97,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
83
97
  gasLimitBufferPercentage: {
84
98
  description: 'How much to increase gas price by each attempt (percentage)',
85
99
  env: 'L1_GAS_LIMIT_BUFFER_PERCENTAGE',
86
- ...bigintConfigHelper(20n),
100
+ ...numberConfigHelper(10),
87
101
  },
88
102
  minGwei: {
89
103
  description: 'Minimum gas price in gwei',
@@ -95,15 +109,25 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
95
109
  env: 'L1_GAS_PRICE_MAX',
96
110
  ...bigintConfigHelper(100n),
97
111
  },
112
+ maxBlobGwei: {
113
+ description: 'Maximum blob fee per gas in gwei',
114
+ env: 'L1_BLOB_FEE_PER_GAS_MAX',
115
+ ...bigintConfigHelper(1_500n),
116
+ },
98
117
  priorityFeeBumpPercentage: {
99
118
  description: 'How much to increase priority fee by each attempt (percentage)',
100
119
  env: 'L1_PRIORITY_FEE_BUMP_PERCENTAGE',
101
- ...bigintConfigHelper(20n),
120
+ ...numberConfigHelper(20),
102
121
  },
103
122
  priorityFeeRetryBumpPercentage: {
104
123
  description: 'How much to increase priority fee by each retry attempt (percentage)',
105
124
  env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE',
106
- ...bigintConfigHelper(50n),
125
+ ...numberConfigHelper(50),
126
+ },
127
+ fixedPriorityFeePerGas: {
128
+ description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage',
129
+ env: 'L1_FIXED_PRIORITY_FEE_PER_GAS',
130
+ ...numberConfigHelper(0),
107
131
  },
108
132
  maxAttempts: {
109
133
  description: 'Maximum number of speed-up attempts',
@@ -118,7 +142,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
118
142
  stallTimeMs: {
119
143
  description: 'How long before considering tx stalled',
120
144
  env: 'L1_TX_MONITOR_STALL_TIME_MS',
121
- ...numberConfigHelper(30_000),
145
+ ...numberConfigHelper(45_000),
122
146
  },
123
147
  txTimeoutMs: {
124
148
  description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.',
@@ -143,12 +167,13 @@ export interface L1TxRequest {
143
167
  export interface L1BlobInputs {
144
168
  blobs: Uint8Array[];
145
169
  kzg: any;
146
- maxFeePerBlobGas: bigint;
170
+ maxFeePerBlobGas?: bigint;
147
171
  }
148
172
 
149
- interface GasPrice {
173
+ export interface GasPrice {
150
174
  maxFeePerGas: bigint;
151
175
  maxPriorityFeePerGas: bigint;
176
+ maxFeePerBlobGas?: bigint;
152
177
  }
153
178
 
154
179
  export class L1TxUtils {
@@ -174,37 +199,57 @@ export class L1TxUtils {
174
199
  */
175
200
  public async sendTransaction(
176
201
  request: L1TxRequest,
177
- _gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint },
178
- _blobInputs?: L1BlobInputs,
202
+ _gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint; txTimeoutAt?: Date },
203
+ blobInputs?: L1BlobInputs,
179
204
  ): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> {
180
- const gasConfig = { ...this.config, ..._gasConfig };
181
- const account = this.walletClient.account;
182
- let gasLimit: bigint;
183
-
184
- if (gasConfig.fixedGas) {
185
- gasLimit = gasConfig.fixedGas;
186
- } else {
187
- gasLimit = await this.estimateGas(account, request);
188
- }
205
+ try {
206
+ const gasConfig = { ...this.config, ..._gasConfig };
207
+ const account = this.walletClient.account;
208
+ let gasLimit: bigint;
209
+
210
+ if (gasConfig.fixedGas) {
211
+ gasLimit = gasConfig.fixedGas;
212
+ } else {
213
+ gasLimit = await this.estimateGas(account, request);
214
+ }
189
215
 
190
- const gasPrice = await this.getGasPrice(gasConfig);
216
+ const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs);
191
217
 
192
- const blobInputs = _blobInputs || {};
193
- const txHash = await this.walletClient.sendTransaction({
194
- ...request,
195
- ...blobInputs,
196
- gas: gasLimit,
197
- maxFeePerGas: gasPrice.maxFeePerGas,
198
- maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
199
- });
200
-
201
- this.logger?.verbose(`Sent L1 transaction ${txHash}`, {
202
- gasLimit,
203
- maxFeePerGas: formatGwei(gasPrice.maxFeePerGas),
204
- maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas),
205
- });
218
+ if (gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) {
219
+ throw new Error('Transaction timed out before sending');
220
+ }
206
221
 
207
- return { txHash, gasLimit, gasPrice };
222
+ let txHash: Hex;
223
+ if (blobInputs) {
224
+ txHash = await this.walletClient.sendTransaction({
225
+ ...request,
226
+ ...blobInputs,
227
+ gas: gasLimit,
228
+ maxFeePerGas: gasPrice.maxFeePerGas,
229
+ maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
230
+ maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
231
+ });
232
+ } else {
233
+ txHash = await this.walletClient.sendTransaction({
234
+ ...request,
235
+ gas: gasLimit,
236
+ maxFeePerGas: gasPrice.maxFeePerGas,
237
+ maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
238
+ });
239
+ }
240
+ this.logger?.verbose(`Sent L1 transaction ${txHash}`, {
241
+ gasLimit,
242
+ maxFeePerGas: formatGwei(gasPrice.maxFeePerGas),
243
+ maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas),
244
+ ...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }),
245
+ });
246
+
247
+ return { txHash, gasLimit, gasPrice };
248
+ } catch (err: any) {
249
+ const formattedErr = formatViemError(err);
250
+ this.logger?.error(`Failed to send transaction`, formattedErr);
251
+ throw formattedErr;
252
+ }
208
253
  }
209
254
 
210
255
  /**
@@ -218,7 +263,7 @@ export class L1TxUtils {
218
263
  request: L1TxRequest,
219
264
  initialTxHash: Hex,
220
265
  params: { gasLimit: bigint },
221
- _gasConfig?: Partial<L1TxUtilsConfig>,
266
+ _gasConfig?: Partial<L1TxUtilsConfig> & { txTimeoutAt?: Date },
222
267
  _blobInputs?: L1BlobInputs,
223
268
  ): Promise<TransactionReceipt> {
224
269
  const gasConfig = { ...this.config, ..._gasConfig };
@@ -246,7 +291,12 @@ export class L1TxUtils {
246
291
  let attempts = 0;
247
292
  let lastAttemptSent = Date.now();
248
293
  const initialTxTime = lastAttemptSent;
294
+
249
295
  let txTimedOut = false;
296
+ const isTimedOut = () =>
297
+ (gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) ||
298
+ (gasConfig.txTimeoutMs !== undefined && Date.now() - initialTxTime > gasConfig.txTimeoutMs) ||
299
+ false;
250
300
 
251
301
  while (!txTimedOut) {
252
302
  try {
@@ -284,11 +334,9 @@ export class L1TxUtils {
284
334
  this.logger?.debug(`L1 transaction ${currentTxHash} pending. Time passed: ${timePassed}ms.`);
285
335
 
286
336
  // Check timeout before continuing
287
- if (gasConfig.txTimeoutMs) {
288
- txTimedOut = Date.now() - initialTxTime > gasConfig.txTimeoutMs;
289
- if (txTimedOut) {
290
- break;
291
- }
337
+ txTimedOut = isTimedOut();
338
+ if (txTimedOut) {
339
+ break;
292
340
  }
293
341
 
294
342
  await sleep(gasConfig.checkIntervalMs!);
@@ -299,15 +347,25 @@ export class L1TxUtils {
299
347
  attempts++;
300
348
  const newGasPrice = await this.getGasPrice(
301
349
  gasConfig,
350
+ !!blobInputs,
302
351
  attempts,
303
352
  tx.maxFeePerGas && tx.maxPriorityFeePerGas
304
- ? { maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas }
353
+ ? {
354
+ maxFeePerGas: tx.maxFeePerGas,
355
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
356
+ maxFeePerBlobGas: tx.maxFeePerBlobGas,
357
+ }
305
358
  : undefined,
306
359
  );
307
360
 
308
361
  this.logger?.debug(
309
362
  `L1 transaction ${currentTxHash} appears stuck. Attempting speed-up ${attempts}/${gasConfig.maxAttempts} ` +
310
363
  `with new priority fee ${formatGwei(newGasPrice.maxPriorityFeePerGas)} gwei`,
364
+ {
365
+ maxFeePerGas: formatGwei(newGasPrice.maxFeePerGas),
366
+ maxPriorityFeePerGas: formatGwei(newGasPrice.maxPriorityFeePerGas),
367
+ ...(newGasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(newGasPrice.maxFeePerBlobGas) }),
368
+ },
311
369
  );
312
370
 
313
371
  currentTxHash = await this.walletClient.sendTransaction({
@@ -324,16 +382,15 @@ export class L1TxUtils {
324
382
  }
325
383
  await sleep(gasConfig.checkIntervalMs!);
326
384
  } catch (err: any) {
327
- this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, err);
385
+ const formattedErr = formatViemError(err);
386
+ this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, formattedErr);
328
387
  if (err.message?.includes('reverted')) {
329
- throw err;
388
+ throw formattedErr;
330
389
  }
331
390
  await sleep(gasConfig.checkIntervalMs!);
332
391
  }
333
392
  // Check if tx has timed out.
334
- if (gasConfig.txTimeoutMs) {
335
- txTimedOut = Date.now() - initialTxTime > gasConfig.txTimeoutMs!;
336
- }
393
+ txTimedOut = isTimedOut();
337
394
  }
338
395
  throw new Error(`L1 transaction ${currentTxHash} timed out`);
339
396
  }
@@ -346,11 +403,12 @@ export class L1TxUtils {
346
403
  */
347
404
  public async sendAndMonitorTransaction(
348
405
  request: L1TxRequest,
349
- gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint },
406
+ gasConfig?: Partial<L1TxUtilsConfig> & { fixedGas?: bigint; txTimeoutAt?: Date },
350
407
  blobInputs?: L1BlobInputs,
351
- ): Promise<TransactionReceipt> {
352
- const { txHash, gasLimit } = await this.sendTransaction(request, gasConfig, blobInputs);
353
- return this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs);
408
+ ): Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }> {
409
+ const { txHash, gasLimit, gasPrice } = await this.sendTransaction(request, gasConfig, blobInputs);
410
+ const receipt = await this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs);
411
+ return { receipt, gasPrice };
354
412
  }
355
413
 
356
414
  /**
@@ -358,6 +416,7 @@ export class L1TxUtils {
358
416
  */
359
417
  private async getGasPrice(
360
418
  _gasConfig?: L1TxUtilsConfig,
419
+ isBlobTx: boolean = false,
361
420
  attempt: number = 0,
362
421
  previousGasPrice?: typeof attempt extends 0 ? never : GasPrice,
363
422
  ): Promise<GasPrice> {
@@ -365,26 +424,54 @@ export class L1TxUtils {
365
424
  const block = await this.publicClient.getBlock({ blockTag: 'latest' });
366
425
  const baseFee = block.baseFeePerGas ?? 0n;
367
426
 
368
- // Get initial priority fee from the network
369
- let priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas();
427
+ // Get blob base fee if available
428
+ let blobBaseFee = 0n;
429
+ try {
430
+ const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' });
431
+ blobBaseFee = BigInt(blobBaseFeeHex);
432
+ this.logger?.debug('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
433
+ } catch {
434
+ this.logger?.warn('Failed to get blob base fee', attempt);
435
+ }
436
+
437
+ let priorityFee: bigint;
438
+ if (gasConfig.fixedPriorityFeePerGas) {
439
+ this.logger?.debug('Using fixed priority fee per gas', {
440
+ fixedPriorityFeePerGas: gasConfig.fixedPriorityFeePerGas,
441
+ });
442
+ // try to maintain precision up to 1000000 wei
443
+ priorityFee = BigInt(gasConfig.fixedPriorityFeePerGas * 1_000_000) * (WEI_CONST / 1_000_000n);
444
+ } else {
445
+ // Get initial priority fee from the network
446
+ priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas();
447
+ }
370
448
  let maxFeePerGas = baseFee;
371
449
 
450
+ let maxFeePerBlobGas = blobBaseFee;
451
+
372
452
  // Bump base fee so it's valid for next blocks if it stalls
373
453
  const numBlocks = Math.ceil(gasConfig.stallTimeMs! / BLOCK_TIME_MS);
374
454
  for (let i = 0; i < numBlocks; i++) {
375
455
  // each block can go up 12.5% from previous baseFee
376
456
  maxFeePerGas = (maxFeePerGas * (1_000n + 125n)) / 1_000n;
457
+ // same for blob gas fee
458
+ maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n)) / 1_000n;
377
459
  }
378
460
 
379
461
  if (attempt > 0) {
380
462
  const configBump =
381
463
  gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage!;
382
- const bumpPercentage =
383
- configBump > MIN_REPLACEMENT_BUMP_PERCENTAGE ? configBump : MIN_REPLACEMENT_BUMP_PERCENTAGE;
464
+
465
+ // if this is a blob tx, we have to use the blob bump percentage
466
+ const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE;
467
+
468
+ const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage;
384
469
 
385
470
  // Calculate minimum required fees based on previous attempt
386
- const minPriorityFee = (previousGasPrice!.maxPriorityFeePerGas * (100n + bumpPercentage)) / 100n;
387
- const minMaxFee = (previousGasPrice!.maxFeePerGas * (100n + bumpPercentage)) / 100n;
471
+ // multiply by 100 & divide by 100 to maintain some precision
472
+ const minPriorityFee =
473
+ (previousGasPrice!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
474
+ const minMaxFee = (previousGasPrice!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
388
475
 
389
476
  // Add priority fee to maxFeePerGas
390
477
  maxFeePerGas += priorityFee;
@@ -393,8 +480,11 @@ export class L1TxUtils {
393
480
  priorityFee = priorityFee > minPriorityFee ? priorityFee : minPriorityFee;
394
481
  maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
395
482
  } else {
396
- // first attempt, just bump priority fee
397
- priorityFee = (priorityFee * (100n + (gasConfig.priorityFeeBumpPercentage || 0n))) / 100n;
483
+ // first attempt, just bump priority fee, unless it's a fixed config
484
+ // multiply by 100 & divide by 100 to maintain some precision
485
+ if (!gasConfig.fixedPriorityFeePerGas) {
486
+ priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
487
+ }
398
488
  maxFeePerGas += priorityFee;
399
489
  }
400
490
 
@@ -402,17 +492,41 @@ export class L1TxUtils {
402
492
  const maxGweiInWei = gasConfig.maxGwei! * WEI_CONST;
403
493
  maxFeePerGas = maxFeePerGas > maxGweiInWei ? maxGweiInWei : maxFeePerGas;
404
494
 
495
+ // Ensure we don't exceed maxBlobGwei
496
+ if (maxFeePerBlobGas) {
497
+ const maxBlobGweiInWei = gasConfig.maxBlobGwei! * WEI_CONST;
498
+ maxFeePerBlobGas = maxFeePerBlobGas > maxBlobGweiInWei ? maxBlobGweiInWei : maxFeePerBlobGas;
499
+ }
500
+
405
501
  // Ensure priority fee doesn't exceed max fee
406
502
  const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee;
407
503
 
504
+ if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) {
505
+ const bumpPercentage =
506
+ gasConfig.priorityFeeRetryBumpPercentage! > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE
507
+ ? gasConfig.priorityFeeRetryBumpPercentage!
508
+ : MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE;
509
+
510
+ // calculate min blob fee based on previous attempt
511
+ const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
512
+
513
+ // use max between current network values and min required values
514
+ maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
515
+ }
516
+
408
517
  this.logger?.debug(`Computed gas price`, {
409
518
  attempt,
410
519
  baseFee: formatGwei(baseFee),
411
520
  maxFeePerGas: formatGwei(maxFeePerGas),
412
521
  maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
522
+ ...(maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(maxFeePerBlobGas) }),
413
523
  });
414
524
 
415
- return { maxFeePerGas, maxPriorityFeePerGas };
525
+ return {
526
+ maxFeePerGas,
527
+ maxPriorityFeePerGas,
528
+ ...(maxFeePerBlobGas && { maxFeePerBlobGas: maxFeePerBlobGas }),
529
+ };
416
530
  }
417
531
 
418
532
  /**
@@ -430,14 +544,22 @@ export class L1TxUtils {
430
544
  // Strangely, the only way to get gas and send blobs is prepareTransactionRequest().
431
545
  // See: https://github.com/wevm/viem/issues/2075
432
546
  if (_blobInputs) {
433
- initialEstimate = (await this.walletClient.prepareTransactionRequest({ account, ...request, ..._blobInputs }))
434
- .gas;
547
+ const gasPrice = await this.getGasPrice(gasConfig, true, 0);
548
+ initialEstimate = (
549
+ await this.walletClient.prepareTransactionRequest({
550
+ account,
551
+ ...request,
552
+ ..._blobInputs,
553
+ maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
554
+ })
555
+ )?.gas;
435
556
  } else {
436
557
  initialEstimate = await this.publicClient.estimateGas({ account, ...request });
437
558
  }
438
559
 
439
560
  // Add buffer based on either fixed amount or percentage
440
- const withBuffer = initialEstimate + (initialEstimate * (gasConfig.gasLimitBufferPercentage ?? 0n)) / 100n;
561
+ const withBuffer =
562
+ initialEstimate + (initialEstimate * BigInt((gasConfig.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
441
563
 
442
564
  return withBuffer;
443
565
  }
@@ -2,38 +2,47 @@ import { makeBackoff, retry } from '@aztec/foundation/retry';
2
2
  import { fileURLToPath } from '@aztec/foundation/url';
3
3
 
4
4
  import { type Anvil, createAnvil } from '@viem/anvil';
5
- import getPort from 'get-port';
6
5
  import { dirname, resolve } from 'path';
7
6
 
8
7
  /**
9
8
  * Ensures there's a running Anvil instance and returns the RPC URL.
10
9
  */
11
10
  export async function startAnvil(l1BlockTime?: number): Promise<{ anvil: Anvil; rpcUrl: string }> {
12
- let ethereumHostPort: number | undefined;
13
-
14
11
  const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
15
12
 
13
+ let port: number | undefined;
14
+
16
15
  // Start anvil.
17
16
  // We go via a wrapper script to ensure if the parent dies, anvil dies.
18
17
  const anvil = await retry(
19
18
  async () => {
20
- ethereumHostPort = await getPort();
21
19
  const anvil = createAnvil({
22
20
  anvilBinary,
23
- port: ethereumHostPort,
21
+ port: 0,
24
22
  blockTime: l1BlockTime,
23
+ stopTimeout: 1000,
24
+ });
25
+
26
+ // Listen to the anvil output to get the port.
27
+ const removeHandler = anvil.on('message', (message: string) => {
28
+ if (port === undefined && message.includes('Listening on')) {
29
+ port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)![2]);
30
+ }
25
31
  });
26
32
  await anvil.start();
33
+ removeHandler();
34
+
27
35
  return anvil;
28
36
  },
29
37
  'Start anvil',
30
38
  makeBackoff([5, 5, 5]),
31
39
  );
32
40
 
33
- if (!ethereumHostPort) {
41
+ if (!port) {
34
42
  throw new Error('Failed to start anvil');
35
43
  }
36
44
 
37
- const rpcUrl = `http://127.0.0.1:${ethereumHostPort}`;
38
- return { anvil, rpcUrl };
45
+ // Monkeypatch the anvil instance to include the actually assigned port
46
+ Object.defineProperty(anvil, 'port', { value: port, writable: false });
47
+ return { anvil, rpcUrl: `http://127.0.0.1:${port}` };
39
48
  }