From 84841682852a6fa9fb7fdd7ba9fd9b165c4863e0 Mon Sep 17 00:00:00 2001 From: spypsy Date: Fri, 3 Jan 2025 13:35:48 +0000 Subject: [PATCH 01/30] fix: bump blob fees on retries --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 71 +++++++++++++++++-- yarn-project/ethereum/src/l1_tx_utils.ts | 32 ++++++++- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 91d4c87e9a7c..7cc32f23efd5 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -1,3 +1,4 @@ +import { Blob } from '@aztec/foundation/blob'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; @@ -106,8 +107,9 @@ describe('GasUtils', () => { await cheatCodes.setAutomine(false); await cheatCodes.setIntervalMining(0); - // Ensure initial base fee is low - await cheatCodes.setNextBlockBaseFeePerGas(initialBaseFee); + // Add blob data + const blobData = new Uint8Array(131072).fill(1); + const kzg = Blob.getViemKzgInstance(); const request = { to: '0x1234567890123456789012345678901234567890' as `0x${string}`, @@ -119,12 +121,16 @@ describe('GasUtils', () => { const originalMaxFeePerGas = WEI_CONST * 10n; const originalMaxPriorityFeePerGas = WEI_CONST; + const originalMaxFeePerBlobGas = WEI_CONST * 10n; const txHash = await walletClient.sendTransaction({ ...request, gas: estimatedGas, maxFeePerGas: originalMaxFeePerGas, maxPriorityFeePerGas: originalMaxPriorityFeePerGas, + blobs: [blobData], + kzg, + maxFeePerBlobGas: originalMaxFeePerBlobGas, }); const rawTx = await cheatCodes.getRawTransaction(txHash); @@ -142,11 +148,12 @@ describe('GasUtils', () => { params: [rawTx], }); - // keeping auto-mining disabled to simulate a stuck transaction - // The monitor should detect the stall and create a replacement tx - // Monitor should detect stall and replace with higher gas price - const monitorFn = gasUtils.monitorTransaction(request, txHash, { gasLimit: estimatedGas }); + const monitorFn = gasUtils.monitorTransaction(request, txHash, { gasLimit: estimatedGas }, undefined, { + blobs: [blobData], + kzg, + maxFeePerBlobGas: WEI_CONST * 20n, + }); await sleep(2000); // re-enable mining @@ -156,11 +163,12 @@ describe('GasUtils', () => { // Verify that a replacement transaction was created expect(receipt.transactionHash).not.toBe(txHash); - // Get details of replacement tx to verify higher gas price + // Get details of replacement tx to verify higher gas prices const replacementTx = await publicClient.getTransaction({ hash: receipt.transactionHash }); expect(replacementTx.maxFeePerGas!).toBeGreaterThan(originalMaxFeePerGas); expect(replacementTx.maxPriorityFeePerGas!).toBeGreaterThan(originalMaxPriorityFeePerGas); + expect(replacementTx.maxFeePerBlobGas!).toBeGreaterThan(originalMaxFeePerBlobGas); }, 20_000); it('respects max gas price limits during spikes', async () => { @@ -299,4 +307,53 @@ describe('GasUtils', () => { const expectedEstimate = baseEstimate + (baseEstimate * 20n) / 100n; expect(bufferedEstimate).toBe(expectedEstimate); }); + + it('correctly handles transactions with blobs', async () => { + // Create a sample blob + const blobData = new Uint8Array(131072).fill(1); // 128KB blob + const kzg = Blob.getViemKzgInstance(); + + const receipt = await gasUtils.sendAndMonitorTransaction( + { + to: '0x1234567890123456789012345678901234567890', + data: '0x', + value: 0n, + }, + undefined, + { + blobs: [blobData], + kzg, + maxFeePerBlobGas: 10000000000n, // 10 gwei + }, + ); + + expect(receipt.status).toBe('success'); + expect(receipt.blobGasUsed).toBeDefined(); + expect(receipt.blobGasPrice).toBeDefined(); + }, 20_000); + + it('estimates gas correctly for blob transactions', async () => { + // Create a sample blob + const blobData = new Uint8Array(131072).fill(1); // 128KB blob + const kzg = Blob.getViemKzgInstance(); + + const request = { + to: '0x1234567890123456789012345678901234567890' as `0x${string}`, + data: '0x' as `0x${string}`, + value: 0n, + }; + + // Estimate gas without blobs first + const baseEstimate = await gasUtils.estimateGas(walletClient.account!, request); + + // Estimate gas with blobs + const blobEstimate = await gasUtils.estimateGas(walletClient.account!, request, undefined, { + blobs: [blobData], + kzg, + maxFeePerBlobGas: 10000000000n, + }); + + // Blob transactions should require more gas + expect(blobEstimate).toBeGreaterThan(baseEstimate); + }, 20_000); }); diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 607f06b95678..dae3b0e4eb00 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -149,6 +149,7 @@ export interface L1BlobInputs { interface GasPrice { maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; + maxFeePerBlobGas?: bigint; } export class L1TxUtils { @@ -301,7 +302,11 @@ export class L1TxUtils { gasConfig, attempts, tx.maxFeePerGas && tx.maxPriorityFeePerGas - ? { maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas } + ? { + maxFeePerGas: tx.maxFeePerGas, + maxPriorityFeePerGas: tx.maxPriorityFeePerGas, + maxFeePerBlobGas: tx.maxFeePerBlobGas, + } : undefined, ); @@ -365,6 +370,15 @@ export class L1TxUtils { const block = await this.publicClient.getBlock({ blockTag: 'latest' }); const baseFee = block.baseFeePerGas ?? 0n; + // Get blob base fee if available + let blobBaseFee = 0n; + try { + const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); + blobBaseFee = BigInt(blobBaseFeeHex); + } catch { + // Ignore if not supported + } + // Get initial priority fee from the network let priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas(); let maxFeePerGas = baseFee; @@ -405,14 +419,28 @@ export class L1TxUtils { // Ensure priority fee doesn't exceed max fee const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee; + if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) { + const bumpPercentage = + gasConfig.priorityFeeRetryBumpPercentage! > MIN_REPLACEMENT_BUMP_PERCENTAGE + ? gasConfig.priorityFeeRetryBumpPercentage! + : MIN_REPLACEMENT_BUMP_PERCENTAGE; + + blobBaseFee = (previousGasPrice.maxFeePerBlobGas * (100n + bumpPercentage)) / 100n; + } + this.logger?.debug(`Computed gas price`, { attempt, baseFee: formatGwei(baseFee), maxFeePerGas: formatGwei(maxFeePerGas), maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas), + ...(blobBaseFee && { maxFeePerBlobGas: formatGwei(blobBaseFee) }), }); - return { maxFeePerGas, maxPriorityFeePerGas }; + return { + maxFeePerGas, + maxPriorityFeePerGas, + ...(blobBaseFee && { maxFeePerBlobGas: blobBaseFee }), + }; } /** From eb311d560d4c7f27f057a3edf00300273e656b9d Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 6 Jan 2025 11:38:58 +0000 Subject: [PATCH 02/30] PR fixes --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 1 - yarn-project/ethereum/src/l1_tx_utils.ts | 24 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 7cc32f23efd5..49e24714e195 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -352,7 +352,6 @@ describe('GasUtils', () => { kzg, maxFeePerBlobGas: 10000000000n, }); - // Blob transactions should require more gas expect(blobEstimate).toBeGreaterThan(baseEstimate); }, 20_000); diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index dae3b0e4eb00..aeb7f7708580 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -32,6 +32,10 @@ const WEI_CONST = 1_000_000_000n; // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298 const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10n; +// setting a minimum bump percentage to 100% due to geth's implementation +// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/blobpool/config.go#L34 +const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100n; + // Avg ethereum block time is ~12s const BLOCK_TIME_MS = 12_000; @@ -376,18 +380,22 @@ export class L1TxUtils { const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); blobBaseFee = BigInt(blobBaseFeeHex); } catch { - // Ignore if not supported + this.logger?.warn('Failed to get blob base fee', { attempt }); } // Get initial priority fee from the network let priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas(); let maxFeePerGas = baseFee; + let maxFeePerBlobGas = blobBaseFee; + // Bump base fee so it's valid for next blocks if it stalls const numBlocks = Math.ceil(gasConfig.stallTimeMs! / BLOCK_TIME_MS); for (let i = 0; i < numBlocks; i++) { // each block can go up 12.5% from previous baseFee maxFeePerGas = (maxFeePerGas * (1_000n + 125n)) / 1_000n; + // same for blob gas fee + maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n)) / 1_000n; } if (attempt > 0) { @@ -421,11 +429,15 @@ export class L1TxUtils { if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) { const bumpPercentage = - gasConfig.priorityFeeRetryBumpPercentage! > MIN_REPLACEMENT_BUMP_PERCENTAGE + gasConfig.priorityFeeRetryBumpPercentage! > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE ? gasConfig.priorityFeeRetryBumpPercentage! - : MIN_REPLACEMENT_BUMP_PERCENTAGE; + : MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE; + + // calculate min blob fee based on previous attempt + const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100n + bumpPercentage)) / 100n; - blobBaseFee = (previousGasPrice.maxFeePerBlobGas * (100n + bumpPercentage)) / 100n; + // use max between current network values and min required values + maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee; } this.logger?.debug(`Computed gas price`, { @@ -433,13 +445,13 @@ export class L1TxUtils { baseFee: formatGwei(baseFee), maxFeePerGas: formatGwei(maxFeePerGas), maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas), - ...(blobBaseFee && { maxFeePerBlobGas: formatGwei(blobBaseFee) }), + ...(maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(maxFeePerBlobGas) }), }); return { maxFeePerGas, maxPriorityFeePerGas, - ...(blobBaseFee && { maxFeePerBlobGas: blobBaseFee }), + ...(maxFeePerBlobGas && { maxFeePerBlobGas: maxFeePerBlobGas }), }; } From ce614fe5404c569ca17e2e5a8cc95763212c8d98 Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 6 Jan 2025 15:11:32 +0000 Subject: [PATCH 03/30] Try info logging to get blob fees --- yarn-project/ethereum/src/l1_tx_utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index aeb7f7708580..8a9141a3baec 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -379,8 +379,11 @@ export class L1TxUtils { try { const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); blobBaseFee = BigInt(blobBaseFeeHex); + this.logger?.info('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) }); + console.log('Blob base fee:', formatGwei(blobBaseFee)); } catch { - this.logger?.warn('Failed to get blob base fee', { attempt }); + this.logger?.info('Failed to get blob base fee', attempt); + console.log('Failed to get blob base fee', attempt); } // Get initial priority fee from the network @@ -440,7 +443,7 @@ export class L1TxUtils { maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee; } - this.logger?.debug(`Computed gas price`, { + this.logger?.info(`Computed gas price`, { attempt, baseFee: formatGwei(baseFee), maxFeePerGas: formatGwei(maxFeePerGas), From e7b3f7106c78fdcd55d1b545c987adc28eb2594d Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 6 Jan 2025 15:13:01 +0000 Subject: [PATCH 04/30] log existing image in bootstrap --- bootstrap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap.sh b/bootstrap.sh index e0e4a070cd21..f77413256ca2 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -203,6 +203,7 @@ case "$cmd" in image=aztecprotocol/aztec:$(git rev-parse HEAD) docker pull $image &>/dev/null || true if docker_has_image $image; then + echo "Image $image already exists and has been downloaded." && exit exit fi github_group "image-aztec" From aa4e4a9a03505f1f9b828c6e0fcc8ad702e20389 Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 6 Jan 2025 15:36:47 +0000 Subject: [PATCH 05/30] actually use calculated maxFeePerBlobGas --- yarn-project/ethereum/src/l1_tx_utils.ts | 37 ++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 8a9141a3baec..0cd2b3e558a7 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -180,7 +180,7 @@ export class L1TxUtils { public async sendTransaction( request: L1TxRequest, _gasConfig?: Partial & { fixedGas?: bigint }, - _blobInputs?: L1BlobInputs, + blobInputs?: L1BlobInputs, ): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> { const gasConfig = { ...this.config, ..._gasConfig }; const account = this.walletClient.account; @@ -194,15 +194,24 @@ export class L1TxUtils { const gasPrice = await this.getGasPrice(gasConfig); - const blobInputs = _blobInputs || {}; - const txHash = await this.walletClient.sendTransaction({ - ...request, - ...blobInputs, - gas: gasLimit, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, - }); - + let txHash: Hex; + if (blobInputs) { + txHash = await this.walletClient.sendTransaction({ + ...request, + ...blobInputs, + gas: gasLimit, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!, + }); + } else { + txHash = await this.walletClient.sendTransaction({ + ...request, + gas: gasLimit, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + }); + } this.logger?.verbose(`Sent L1 transaction ${txHash}`, { gasLimit, maxFeePerGas: formatGwei(gasPrice.maxFeePerGas), @@ -379,11 +388,9 @@ export class L1TxUtils { try { const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); blobBaseFee = BigInt(blobBaseFeeHex); - this.logger?.info('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) }); - console.log('Blob base fee:', formatGwei(blobBaseFee)); + this.logger?.debug('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) }); } catch { - this.logger?.info('Failed to get blob base fee', attempt); - console.log('Failed to get blob base fee', attempt); + this.logger?.warn('Failed to get blob base fee', attempt); } // Get initial priority fee from the network @@ -443,7 +450,7 @@ export class L1TxUtils { maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee; } - this.logger?.info(`Computed gas price`, { + this.logger?.debug(`Computed gas price`, { attempt, baseFee: formatGwei(baseFee), maxFeePerGas: formatGwei(maxFeePerGas), From 258a9047aec5aee61a274fd85aadd9d0c662e794 Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 6 Jan 2025 19:08:34 +0000 Subject: [PATCH 06/30] min bump percentage is 100 for ALL fees --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 4 ++-- yarn-project/ethereum/src/l1_tx_utils.ts | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 49e24714e195..bd9258fddcdc 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -264,7 +264,7 @@ describe('GasUtils', () => { const initialGasPrice = await gasUtils['getGasPrice'](); // Get retry gas price for 2nd attempt - const retryGasPrice = await gasUtils['getGasPrice'](undefined, 1, initialGasPrice); + const retryGasPrice = await gasUtils['getGasPrice'](undefined, false, 1, initialGasPrice); // With default config, retry should bump fees by 50% const expectedPriorityFee = (initialGasPrice.maxPriorityFeePerGas * 150n) / 100n; @@ -283,7 +283,7 @@ describe('GasUtils', () => { const initialGasPrice = await gasUtils['getGasPrice'](); // Get retry gas price with attempt = 1 - const retryGasPrice = await gasUtils['getGasPrice'](undefined, 1, initialGasPrice); + const retryGasPrice = await gasUtils['getGasPrice'](undefined, false, 1, initialGasPrice); // Should use 10% minimum bump even though config specified 5% const expectedPriorityFee = (initialGasPrice.maxPriorityFeePerGas * 110n) / 100n; diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 0cd2b3e558a7..1efc357adc9a 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -192,7 +192,7 @@ export class L1TxUtils { gasLimit = await this.estimateGas(account, request); } - const gasPrice = await this.getGasPrice(gasConfig); + const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs); let txHash: Hex; if (blobInputs) { @@ -313,6 +313,7 @@ export class L1TxUtils { attempts++; const newGasPrice = await this.getGasPrice( gasConfig, + !!blobInputs, attempts, tx.maxFeePerGas && tx.maxPriorityFeePerGas ? { @@ -376,6 +377,7 @@ export class L1TxUtils { */ private async getGasPrice( _gasConfig?: L1TxUtilsConfig, + isBlobTx: boolean = false, attempt: number = 0, previousGasPrice?: typeof attempt extends 0 ? never : GasPrice, ): Promise { @@ -411,8 +413,11 @@ export class L1TxUtils { if (attempt > 0) { const configBump = gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage!; - const bumpPercentage = - configBump > MIN_REPLACEMENT_BUMP_PERCENTAGE ? configBump : MIN_REPLACEMENT_BUMP_PERCENTAGE; + + // if this is a blob tx, we have to use the blob bump percentage + const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE; + + const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage; // Calculate minimum required fees based on previous attempt const minPriorityFee = (previousGasPrice!.maxPriorityFeePerGas * (100n + bumpPercentage)) / 100n; From 36b658292c860e473f8de1b4f6ae3ab07b55a940 Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 16:22:11 +0000 Subject: [PATCH 07/30] max gwei for blob fees --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 13 ++++++++----- yarn-project/ethereum/src/l1_tx_utils.ts | 14 ++++++++++++++ yarn-project/foundation/src/config/env_var.ts | 1 + 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index bd9258fddcdc..b0d094a7dce3 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -181,11 +181,14 @@ describe('GasUtils', () => { // Mine a new block to make the base fee change take effect await cheatCodes.evmMine(); - const receipt = await gasUtils.sendAndMonitorTransaction({ - to: '0x1234567890123456789012345678901234567890', - data: '0x', - value: 0n, - }); + const receipt = await gasUtils.sendAndMonitorTransaction( + { + to: '0x1234567890123456789012345678901234567890', + data: '0x', + value: 0n, + }, + { maxGwei }, + ); expect(receipt.effectiveGasPrice).toBeLessThanOrEqual(maxGwei * WEI_CONST); }, 60_000); diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 1efc357adc9a..8397498e7fde 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -52,6 +52,10 @@ export interface L1TxUtilsConfig { * Minimum gas price in gwei */ minGwei?: bigint; + /** + * Maximum blob fee per gas in gwei + */ + maxBlobGwei?: bigint; /** * Priority fee bump percentage */ @@ -99,6 +103,11 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { env: 'L1_GAS_PRICE_MAX', ...bigintConfigHelper(100n), }, + maxBlobGwei: { + description: 'Maximum blob fee per gas in gwei', + env: 'L1_BLOB_FEE_PER_GAS_MAX', + ...bigintConfigHelper(1_500n), + }, priorityFeeBumpPercentage: { description: 'How much to increase priority fee by each attempt (percentage)', env: 'L1_PRIORITY_FEE_BUMP_PERCENTAGE', @@ -439,6 +448,11 @@ export class L1TxUtils { const maxGweiInWei = gasConfig.maxGwei! * WEI_CONST; maxFeePerGas = maxFeePerGas > maxGweiInWei ? maxGweiInWei : maxFeePerGas; + // Ensure we don't exceed maxBlobGwei + if (maxFeePerBlobGas) { + maxFeePerBlobGas = maxFeePerBlobGas > gasConfig.maxBlobGwei! ? gasConfig.maxBlobGwei! : maxFeePerBlobGas; + } + // Ensure priority fee doesn't exceed max fee const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee; diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index d45d8d36f63d..9b560c21b851 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -186,6 +186,7 @@ export type EnvVar = | 'L1_GAS_LIMIT_BUFFER_FIXED' | 'L1_GAS_PRICE_MIN' | 'L1_GAS_PRICE_MAX' + | 'L1_BLOB_FEE_PER_GAS_MAX' | 'L1_PRIORITY_FEE_BUMP_PERCENTAGE' | 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE' | 'L1_TX_MONITOR_MAX_ATTEMPTS' From 6e9e8ac69a37cffcb960c43c182279a30b876bd9 Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 17:28:14 +0000 Subject: [PATCH 08/30] log maxFeePerBlobGas --- yarn-project/ethereum/src/l1_tx_utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 8397498e7fde..f9acdc270c5b 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -225,6 +225,7 @@ export class L1TxUtils { gasLimit, maxFeePerGas: formatGwei(gasPrice.maxFeePerGas), maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas), + ...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }), }); return { txHash, gasLimit, gasPrice }; From 83c27f998745983cf7b6c811ea26dd07ccf5b4b3 Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 17:29:49 +0000 Subject: [PATCH 09/30] add metadata on speed-up attempts --- yarn-project/ethereum/src/l1_tx_utils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index f9acdc270c5b..98da8455cc8e 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -337,6 +337,11 @@ export class L1TxUtils { this.logger?.debug( `L1 transaction ${currentTxHash} appears stuck. Attempting speed-up ${attempts}/${gasConfig.maxAttempts} ` + `with new priority fee ${formatGwei(newGasPrice.maxPriorityFeePerGas)} gwei`, + { + maxFeePerGas: formatGwei(newGasPrice.maxFeePerGas), + maxPriorityFeePerGas: formatGwei(newGasPrice.maxPriorityFeePerGas), + ...(newGasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(newGasPrice.maxFeePerBlobGas) }), + }, ); currentTxHash = await this.walletClient.sendTransaction({ From c286d826b04fcd64f6ec327186d2d43bbe78f31d Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 17:57:22 +0000 Subject: [PATCH 10/30] fix conversion --- yarn-project/ethereum/src/l1_tx_utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 98da8455cc8e..80ddd69c2d2f 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -456,7 +456,8 @@ export class L1TxUtils { // Ensure we don't exceed maxBlobGwei if (maxFeePerBlobGas) { - maxFeePerBlobGas = maxFeePerBlobGas > gasConfig.maxBlobGwei! ? gasConfig.maxBlobGwei! : maxFeePerBlobGas; + const maxBlobGweiInWei = gasConfig.maxBlobGwei! * WEI_CONST; + maxFeePerBlobGas = maxFeePerBlobGas > maxBlobGweiInWei ? maxBlobGweiInWei : maxFeePerBlobGas; } // Ensure priority fee doesn't exceed max fee From 39de22ef830d6b9ee8499b3adf3f081ce9e435fc Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 09:48:47 +0000 Subject: [PATCH 11/30] add some better logging --- yarn-project/ethereum/src/l1_tx_utils.ts | 4 +- yarn-project/ethereum/src/utils.ts | 94 +++++++++++++++++++ .../src/publisher/l1-publisher.ts | 9 +- .../sequencer-client/src/publisher/utils.ts | 14 --- 4 files changed, 104 insertions(+), 17 deletions(-) delete mode 100644 yarn-project/sequencer-client/src/publisher/utils.ts diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 80ddd69c2d2f..c35123fe33c3 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -22,6 +22,8 @@ import { formatGwei, } from 'viem'; +import { prettyLogViemErrorMsg } from './utils.js'; + // 1_000_000_000 Gwei = 1 ETH // 1_000_000_000 Wei = 1 Gwei // 1_000_000_000_000_000_000 Wei = 1 ETH @@ -358,7 +360,7 @@ export class L1TxUtils { } await sleep(gasConfig.checkIntervalMs!); } catch (err: any) { - this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, err); + this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, prettyLogViemErrorMsg(err)); if (err.message?.includes('reverted')) { throw err; } diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts index fbd8d45d55d5..bd37c0a89b76 100644 --- a/yarn-project/ethereum/src/utils.ts +++ b/yarn-project/ethereum/src/utils.ts @@ -3,7 +3,9 @@ import { type Logger } from '@aztec/foundation/log'; import { type Abi, + BaseError, type ContractEventName, + ContractFunctionRevertedError, type DecodeEventLogReturnType, type Hex, type Log, @@ -64,3 +66,95 @@ function tryExtractEvent< } } } + +export function prettyLogViemErrorMsg(err: any) { + if (err instanceof BaseError) { + const revertError = err.walk(err => err instanceof ContractFunctionRevertedError); + if (revertError instanceof ContractFunctionRevertedError) { + const errorName = revertError.data?.errorName ?? ''; + const args = + revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : ''; + return `${errorName}${args}`; + } + } + return err?.message ?? err; +} + +interface ViemErrorWithCode { + code?: number; + name?: string; + shortMessage?: string; + message: string; + details?: string; + metaMessages?: string[]; +} + +export function formatViemError(error: any): string { + const truncateHex = (hex: string, length = 10) => { + if (!hex || typeof hex !== 'string') { + return hex; + } + if (hex.length <= length * 2) { + return hex; + } + return `${hex.slice(0, length)}...${hex.slice(-length)}`; + }; + + const formatRequestBody = (body: string) => { + try { + const parsed = JSON.parse(body); + if (parsed.params && Array.isArray(parsed.params)) { + parsed.params = parsed.params.map((param: any) => (typeof param === 'string' ? truncateHex(param) : param)); + } + return JSON.stringify(parsed, null, 2); + } catch { + return truncateHex(body); + } + }; + + const errorChain = []; + if (error instanceof BaseError) { + error.walk((err: unknown) => { + const viemErr = err as ViemErrorWithCode; + errorChain.push({ + name: viemErr.name, + message: viemErr.shortMessage || viemErr.message, + details: viemErr.details, + code: viemErr.code, + metaMessages: viemErr.metaMessages?.map(msg => { + if (typeof msg === 'string' && msg.startsWith('Request body:')) { + return `Request body: ${formatRequestBody(msg.slice(13))}`; + } + return msg; + }), + }); + return false; + }); + } else { + errorChain.push({ + message: error?.message || String(error), + details: error?.details, + code: error?.code, + }); + } + + const formatted = { + errorChain, + args: error.message + ?.match(/Request Arguments:\n([\s\S]*?)\n\nDetails/)?.[1] + ?.split('\n') + ?.map((line: string) => line.trim()) + ?.filter(Boolean), + }; + + const clean = (obj: any) => { + Object.keys(obj).forEach(key => { + if (obj[key] === undefined) { + delete obj[key]; + } + }); + return obj; + }; + + return JSON.stringify(clean(formatted), null, 2); +} diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 727e57f51e70..365924fd83da 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -16,7 +16,13 @@ import { type Proof, } from '@aztec/circuits.js'; import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup'; -import { type EthereumChain, type L1ContractsConfig, L1TxUtils, createEthereumChain } from '@aztec/ethereum'; +import { + type EthereumChain, + type L1ContractsConfig, + L1TxUtils, + createEthereumChain, + prettyLogViemErrorMsg, +} from '@aztec/ethereum'; import { makeTuple } from '@aztec/foundation/array'; import { toHex } from '@aztec/foundation/bigint-buffer'; import { Blob } from '@aztec/foundation/blob'; @@ -63,7 +69,6 @@ import { privateKeyToAccount } from 'viem/accounts'; import { type PublisherConfig, type TxSenderConfig } from './config.js'; import { L1PublisherMetrics } from './l1-publisher-metrics.js'; -import { prettyLogViemErrorMsg } from './utils.js'; /** * Stats for a sent transaction. diff --git a/yarn-project/sequencer-client/src/publisher/utils.ts b/yarn-project/sequencer-client/src/publisher/utils.ts deleted file mode 100644 index c8a570896f4d..000000000000 --- a/yarn-project/sequencer-client/src/publisher/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BaseError, ContractFunctionRevertedError } from 'viem'; - -export function prettyLogViemErrorMsg(err: any) { - if (err instanceof BaseError) { - const revertError = err.walk(err => err instanceof ContractFunctionRevertedError); - if (revertError instanceof ContractFunctionRevertedError) { - const errorName = revertError.data?.errorName ?? ''; - const args = - revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : ''; - return `${errorName}${args}`; - } - } - return err?.message ?? err; -} From 7b92aaf1cebe57fb058baecee9e0bcfbeb2de020 Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 10:06:09 +0000 Subject: [PATCH 12/30] fix import --- yarn-project/sequencer-client/src/sequencer/sequencer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index a953e51e1101..6e823a58cb81 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -21,6 +21,7 @@ import { type GlobalVariables, StateReference, } from '@aztec/circuits.js'; +import { prettyLogViemErrorMsg } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { omit } from '@aztec/foundation/collection'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -37,7 +38,6 @@ import { type ValidatorClient } from '@aztec/validator-client'; import { type GlobalVariableBuilder } from '../global_variable_builder/global_builder.js'; import { type L1Publisher, VoteType } from '../publisher/l1-publisher.js'; -import { prettyLogViemErrorMsg } from '../publisher/utils.js'; import { type SlasherClient } from '../slasher/slasher_client.js'; import { createValidatorsForBlockBuilding } from '../tx_validator/tx_validator_factory.js'; import { getDefaultAllowedSetupFunctions } from './allowed.js'; From c4783eb8b070cb9c7468979bdd8f3bd305d28f5e Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 13:45:04 +0000 Subject: [PATCH 13/30] fix logging, add test --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 55 ++++++++++++ yarn-project/ethereum/src/utils.ts | 90 ++++++++++--------- 2 files changed, 103 insertions(+), 42 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index b0d094a7dce3..06ad8b8075a3 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -20,6 +20,9 @@ import { foundry } from 'viem/chains'; import { EthCheatCodes } from './eth_cheat_codes.js'; import { L1TxUtils, defaultL1TxUtilsConfig } from './l1_tx_utils.js'; import { startAnvil } from './test/start_anvil.js'; +import { formatViemError } from './utils.js'; + +const logger = createLogger('ethereum:test:l1_gas_test'); const MNEMONIC = 'test test test test test test test test test test test junk'; const WEI_CONST = 1_000_000_000n; @@ -358,4 +361,56 @@ describe('GasUtils', () => { // Blob transactions should require more gas expect(blobEstimate).toBeGreaterThan(baseEstimate); }, 20_000); + + it('formats anvil errors correctly', async () => { + // Set base fee extremely high to trigger error + const extremelyHighBaseFee = WEI_CONST * 1_000_000n; // 1M gwei + await cheatCodes.setNextBlockBaseFeePerGas(extremelyHighBaseFee); + await cheatCodes.evmMine(); + + try { + await gasUtils.sendAndMonitorTransaction({ + to: '0x1234567890123456789012345678901234567890', + data: '0x', + value: 0n, + }); + fail('Should have thrown'); + } catch (err: any) { + const formattedError = formatViemError(err); + const parsed = JSON.parse(formattedError); + + // Check the error chain structure + expect(parsed.errorChain).toBeDefined(); + expect(Array.isArray(parsed.errorChain)).toBe(true); + expect(parsed.errorChain.length).toBeGreaterThan(0); + + // Check that we have the key error information + const firstError = parsed.errorChain[0]; + expect(firstError.name).toBe('TransactionExecutionError'); + expect(firstError.message).toContain('fee cap'); + expect(firstError.details).toContain('max fee per gas'); + + // Verify args contains expected transaction information + if (firstError.args) { + expect(Array.isArray(firstError.args)).toBe(true); + expect(firstError.args).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^from:\s+0x[a-fA-F0-9]{40}$/), + expect.stringMatching(/^to:\s+0x[a-fA-F0-9]{40}$/), + expect.stringMatching(/^value:\s+0 ETH$/), + expect.stringMatching(/^data:\s+0x$/), + expect.stringMatching(/^gas:\s+\d+$/), + expect.stringMatching(/^maxFeePerGas:\s+\d+(\.\d+)? gwei$/), + expect.stringMatching(/^maxPriorityFeePerGas:\s+\d+(\.\d+)? gwei$/), + ]), + ); + } + + // Check that request body is properly truncated + if (firstError.requestBody) { + expect(firstError.requestBody).toContain('eth_sendRawTransaction'); + expect(firstError.requestBody).toContain('...'); + } + } + }, 10_000); }); diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts index bd37c0a89b76..82ec63ea7d06 100644 --- a/yarn-project/ethereum/src/utils.ts +++ b/yarn-project/ethereum/src/utils.ts @@ -80,15 +80,6 @@ export function prettyLogViemErrorMsg(err: any) { return err?.message ?? err; } -interface ViemErrorWithCode { - code?: number; - name?: string; - shortMessage?: string; - message: string; - details?: string; - metaMessages?: string[]; -} - export function formatViemError(error: any): string { const truncateHex = (hex: string, length = 10) => { if (!hex || typeof hex !== 'string') { @@ -114,23 +105,47 @@ export function formatViemError(error: any): string { const errorChain = []; if (error instanceof BaseError) { - error.walk((err: unknown) => { - const viemErr = err as ViemErrorWithCode; - errorChain.push({ - name: viemErr.name, - message: viemErr.shortMessage || viemErr.message, - details: viemErr.details, - code: viemErr.code, - metaMessages: viemErr.metaMessages?.map(msg => { - if (typeof msg === 'string' && msg.startsWith('Request body:')) { - return `Request body: ${formatRequestBody(msg.slice(13))}`; - } - return msg; - }), - }); + error.walk((err: any) => { + const errorInfo: any = { + name: err.name, + message: err.shortMessage || err.message, + }; + + // Extract request arguments if present + const argsMatch = err.message?.match(/Request Arguments:\n([\s\S]*?)(?:\n\nDetails:|$)/); + if (argsMatch) { + errorInfo.args = argsMatch[1] + .split('\n') + .map((line: string) => line.trim()) + .filter(Boolean); + } + + // Extract details if present + const detailsMatch = err.message?.match(/Details: (.*?)(?:\nVersion:|$)/); + if (detailsMatch) { + errorInfo.details = detailsMatch[1]; + } + + // Process request body if present + if (err.metaMessages?.some((msg: string) => msg.includes('Request body:'))) { + const requestBody = err.metaMessages + .find((msg: string) => msg.includes('Request body:')) + ?.replace('Request body:', '') + .trim(); + if (requestBody) { + errorInfo.requestBody = formatRequestBody(requestBody); + } + } + + if (err.code) { + errorInfo.code = err.code; + } + + errorChain.push(errorInfo); return false; }); } else { + // Handle non-BaseError errorChain.push({ message: error?.message || String(error), details: error?.details, @@ -138,23 +153,14 @@ export function formatViemError(error: any): string { }); } - const formatted = { - errorChain, - args: error.message - ?.match(/Request Arguments:\n([\s\S]*?)\n\nDetails/)?.[1] - ?.split('\n') - ?.map((line: string) => line.trim()) - ?.filter(Boolean), - }; - - const clean = (obj: any) => { - Object.keys(obj).forEach(key => { - if (obj[key] === undefined) { - delete obj[key]; - } - }); - return obj; - }; - - return JSON.stringify(clean(formatted), null, 2); + return JSON.stringify(clean({ errorChain }), null, 2); } + +const clean = (obj: any) => { + Object.keys(obj).forEach(key => { + if (obj[key] === undefined) { + delete obj[key]; + } + }); + return obj; +}; From e553adc12d6911110a7d3837a2ef21e674ca85c1 Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 14:31:26 +0000 Subject: [PATCH 14/30] rm unused var --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 06ad8b8075a3..9464c394cb17 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -22,8 +22,6 @@ import { L1TxUtils, defaultL1TxUtilsConfig } from './l1_tx_utils.js'; import { startAnvil } from './test/start_anvil.js'; import { formatViemError } from './utils.js'; -const logger = createLogger('ethereum:test:l1_gas_test'); - const MNEMONIC = 'test test test test test test test test test test test junk'; const WEI_CONST = 1_000_000_000n; // Simple contract that just returns 42 From db53a668790153a537d43414cd38fbca2f8e72ab Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 14:34:27 +0000 Subject: [PATCH 15/30] disable faucet on sepolia --- spartan/aztec-network/templates/faucet.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spartan/aztec-network/templates/faucet.yaml b/spartan/aztec-network/templates/faucet.yaml index 5053999d0a6a..018c85f96ab2 100644 --- a/spartan/aztec-network/templates/faucet.yaml +++ b/spartan/aztec-network/templates/faucet.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.ethereum.externalHost }} apiVersion: apps/v1 kind: Deployment metadata: @@ -127,3 +128,4 @@ spec: - protocol: TCP port: {{ .Values.faucet.apiServerPort }} targetPort: {{ .Values.faucet.apiServerPort }} +{{ end }} From 5e73f7ee2ac1ba6dbaebec437d7ed5061e8400c8 Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 14:42:26 +0000 Subject: [PATCH 16/30] actually use format fn... --- yarn-project/ethereum/src/l1_tx_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index c35123fe33c3..9c4617eea29f 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -22,7 +22,7 @@ import { formatGwei, } from 'viem'; -import { prettyLogViemErrorMsg } from './utils.js'; +import { formatViemError } from './utils.js'; // 1_000_000_000 Gwei = 1 ETH // 1_000_000_000 Wei = 1 Gwei @@ -360,7 +360,7 @@ export class L1TxUtils { } await sleep(gasConfig.checkIntervalMs!); } catch (err: any) { - this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, prettyLogViemErrorMsg(err)); + this.logger?.warn(`Error monitoring tx ${currentTxHash}:`, formatViemError(err)); if (err.message?.includes('reverted')) { throw err; } From 3bb7df01360a8714a2a3985b0d4a690bcc25178d Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 16:41:35 +0000 Subject: [PATCH 17/30] allow for fixed priority fee --- .../aztec-network/templates/validator.yaml | 2 ++ yarn-project/ethereum/src/l1_tx_utils.ts | 23 ++++++++++++++++--- yarn-project/foundation/src/config/env_var.ts | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/spartan/aztec-network/templates/validator.yaml b/spartan/aztec-network/templates/validator.yaml index 8ff7cf7735f8..77a1cbee5578 100644 --- a/spartan/aztec-network/templates/validator.yaml +++ b/spartan/aztec-network/templates/validator.yaml @@ -200,6 +200,8 @@ spec: value: {{ .Values.validator.viemPollingInterval | quote }} - name: SEQ_VIEM_POLLING_INTERVAL_MS value: {{ .Values.validator.viemPollingInterval | quote }} + - name: L1_FIXED_PRIORITY_FEE_PER_GAS + value: {{ .Values.validator.l1FixedPriorityFeePerGas | quote }} ports: - containerPort: {{ .Values.validator.service.nodePort }} - containerPort: {{ .Values.validator.service.p2pTcpPort }} diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 9c4617eea29f..cb5d6c790623 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -66,6 +66,10 @@ export interface L1TxUtilsConfig { * How much to increase priority fee by each attempt (percentage) */ priorityFeeRetryBumpPercentage?: bigint; + /** + * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage + */ + fixedPriorityFeePerGas?: bigint; /** * Maximum number of speed-up attempts */ @@ -120,6 +124,11 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE', ...bigintConfigHelper(50n), }, + fixedPriorityFeePerGas: { + description: 'Fixed priority fee per gas. Overrides any priority fee bump percentage', + env: 'L1_FIXED_PRIORITY_FEE_PER_GAS', + ...bigintConfigHelper(), + }, maxAttempts: { description: 'Maximum number of speed-up attempts', env: 'L1_TX_MONITOR_MAX_ATTEMPTS', @@ -133,7 +142,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { stallTimeMs: { description: 'How long before considering tx stalled', env: 'L1_TX_MONITOR_STALL_TIME_MS', - ...numberConfigHelper(30_000), + ...numberConfigHelper(45_000), }, txTimeoutMs: { description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.', @@ -412,8 +421,16 @@ export class L1TxUtils { this.logger?.warn('Failed to get blob base fee', attempt); } - // Get initial priority fee from the network - let priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas(); + let priorityFee: bigint; + if (gasConfig.fixedPriorityFeePerGas) { + this.logger?.debug('Using fixed priority fee per gas', { + fixedPriorityFeePerGas: formatGwei(gasConfig.fixedPriorityFeePerGas), + }); + priorityFee = gasConfig.fixedPriorityFeePerGas * WEI_CONST; + } else { + // Get initial priority fee from the network + priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas(); + } let maxFeePerGas = baseFee; let maxFeePerBlobGas = blobBaseFee; diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 9b560c21b851..e8bc3daa3302 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -189,6 +189,7 @@ export type EnvVar = | 'L1_BLOB_FEE_PER_GAS_MAX' | 'L1_PRIORITY_FEE_BUMP_PERCENTAGE' | 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE' + | 'L1_FIXED_PRIORITY_FEE_PER_GAS' | 'L1_TX_MONITOR_MAX_ATTEMPTS' | 'L1_TX_MONITOR_CHECK_INTERVAL_MS' | 'L1_TX_MONITOR_STALL_TIME_MS' From aa0e244e54f607d2c061cc901f5bc3772ee150c3 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 10:00:25 +0000 Subject: [PATCH 18/30] truncate more log fields --- yarn-project/ethereum/src/utils.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts index 82ec63ea7d06..e2688a3dd014 100644 --- a/yarn-project/ethereum/src/utils.ts +++ b/yarn-project/ethereum/src/utils.ts @@ -94,12 +94,30 @@ export function formatViemError(error: any): string { const formatRequestBody = (body: string) => { try { const parsed = JSON.parse(body); + + // Handle specific fields that need truncation if (parsed.params && Array.isArray(parsed.params)) { - parsed.params = parsed.params.map((param: any) => (typeof param === 'string' ? truncateHex(param) : param)); + parsed.params = parsed.params.map((param: any) => { + if (typeof param === 'object') { + const truncated = { ...param }; + // Only truncate known large fields + if (truncated.blobs) { + truncated.blobs = truncated.blobs.map((blob: string) => truncateHex(blob)); + } + if (truncated.data) { + truncated.data = truncateHex(truncated.data); + } + // Keep other fields as is (from, blobVersionedHashes, etc.) + return truncated; + } + return param; + }); } + return JSON.stringify(parsed, null, 2); } catch { - return truncateHex(body); + // If we can't parse it as JSON, only truncate if it's a very long string + return body.length > 1000 ? truncateHex(body) : body; } }; From 573762852a617044666e5d1c3ed2205d7c4666f4 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 11:21:01 +0000 Subject: [PATCH 19/30] don't use fixed blob fees --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 10 ++-- yarn-project/ethereum/src/l1_tx_utils.ts | 22 ++++++--- .../src/publisher/l1-publisher.ts | 46 +++++++++++-------- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 9464c394cb17..a47daa5fb8c0 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -94,7 +94,7 @@ describe('GasUtils', () => { }, 5_000); it('sends and monitors a simple transaction', async () => { - const receipt = await gasUtils.sendAndMonitorTransaction({ + const { receipt } = await gasUtils.sendAndMonitorTransaction({ to: '0x1234567890123456789012345678901234567890', data: '0x', value: 0n, @@ -182,7 +182,7 @@ describe('GasUtils', () => { // Mine a new block to make the base fee change take effect await cheatCodes.evmMine(); - const receipt = await gasUtils.sendAndMonitorTransaction( + const { receipt } = await gasUtils.sendAndMonitorTransaction( { to: '0x1234567890123456789012345678901234567890', data: '0x', @@ -209,7 +209,7 @@ describe('GasUtils', () => { stallTimeMs: 1000, }); - const baselineTx = await baselineGasUtils.sendAndMonitorTransaction({ + const { receipt: baselineTx } = await baselineGasUtils.sendAndMonitorTransaction({ to: EthAddress.ZERO.toString(), data: SIMPLE_CONTRACT_BYTECODE, }); @@ -229,7 +229,7 @@ describe('GasUtils', () => { stallTimeMs: 1000, }); - const bufferedTx = await bufferedGasUtils.sendAndMonitorTransaction({ + const { receipt: bufferedTx } = await bufferedGasUtils.sendAndMonitorTransaction({ to: EthAddress.ZERO.toString(), data: SIMPLE_CONTRACT_BYTECODE, }); @@ -317,7 +317,7 @@ describe('GasUtils', () => { const blobData = new Uint8Array(131072).fill(1); // 128KB blob const kzg = Blob.getViemKzgInstance(); - const receipt = await gasUtils.sendAndMonitorTransaction( + const { receipt } = await gasUtils.sendAndMonitorTransaction( { to: '0x1234567890123456789012345678901234567890', data: '0x', diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index cb5d6c790623..5a55a45e2670 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -167,10 +167,10 @@ export interface L1TxRequest { export interface L1BlobInputs { blobs: Uint8Array[]; kzg: any; - maxFeePerBlobGas: bigint; + maxFeePerBlobGas?: bigint; } -interface GasPrice { +export interface GasPrice { maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; maxFeePerBlobGas?: bigint; @@ -393,9 +393,10 @@ export class L1TxUtils { request: L1TxRequest, gasConfig?: Partial & { fixedGas?: bigint }, blobInputs?: L1BlobInputs, - ): Promise { - const { txHash, gasLimit } = await this.sendTransaction(request, gasConfig, blobInputs); - return this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs); + ): Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }> { + const { txHash, gasLimit, gasPrice } = await this.sendTransaction(request, gasConfig, blobInputs); + const receipt = await this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs); + return { receipt, gasPrice }; } /** @@ -525,8 +526,15 @@ export class L1TxUtils { // Strangely, the only way to get gas and send blobs is prepareTransactionRequest(). // See: https://github.com/wevm/viem/issues/2075 if (_blobInputs) { - initialEstimate = (await this.walletClient.prepareTransactionRequest({ account, ...request, ..._blobInputs })) - .gas; + const gasPrice = await this.getGasPrice(gasConfig, true, 0); + initialEstimate = ( + await this.walletClient.prepareTransactionRequest({ + account, + ...request, + ..._blobInputs, + maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!, + }) + )?.gas; } else { initialEstimate = await this.publicClient.estimateGas({ account, ...request }); } diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 365924fd83da..9c2331a7d7a6 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -18,6 +18,7 @@ import { import { type FeeRecipient, type RootRollupPublicInputs } from '@aztec/circuits.js/rollup'; import { type EthereumChain, + type GasPrice, type L1ContractsConfig, L1TxUtils, createEthereumChain, @@ -122,6 +123,14 @@ type L1ProcessArgs = { attestations?: Signature[]; }; +type L1ProcessReturnType = { + receipt: TransactionReceipt | undefined; + args: any; + functionName: string; + data: Hex; + gasPrice: GasPrice; +}; + /** Arguments to the submitEpochProof method of the rollup contract */ export type L1SubmitEpochProofArgs = { epochSize: number; @@ -643,7 +652,7 @@ export class L1Publisher { return false; } - const { receipt, args, functionName, data } = result; + const { receipt, args, functionName, data, gasPrice } = result; // Tx was mined successfully if (receipt.status === 'success') { @@ -682,7 +691,7 @@ export class L1Publisher { { blobs: proposeTxArgs.blobs.map(b => b.dataWithZeros), kzg, - maxFeePerBlobGas: 10000000000n, + maxFeePerBlobGas: gasPrice.maxFeePerBlobGas, }, ); this.log.error(`Rollup process tx reverted. ${errorMsg}`, undefined, { @@ -696,11 +705,10 @@ export class L1Publisher { /** Calls claimEpochProofRight in the Rollup contract to submit a chosen prover quote for the previous epoch. */ public async claimEpochProofRight(proofQuote: EpochProofQuote) { const timer = new Timer(); - - let receipt; + let result; try { this.log.debug(`Submitting claimEpochProofRight transaction`); - receipt = await this.l1TxUtils.sendAndMonitorTransaction({ + result = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data: encodeFunctionData({ abi: RollupAbi, @@ -715,6 +723,8 @@ export class L1Publisher { return false; } + const { receipt } = result; + if (receipt.status === 'success') { const tx = await this.getTransactionStats(receipt.transactionHash); const stats: L1PublishStats = { @@ -955,7 +965,7 @@ export class L1Publisher { this.log.info(`SubmitEpochProof proofSize=${args.proof.withoutPublicInputs().length} bytes`); - const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ + const { receipt } = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.rollupContract.address, data: encodeFunctionData({ abi: this.rollupContract.abi, @@ -964,7 +974,7 @@ export class L1Publisher { }), }); - return txReceipt.transactionHash; + return receipt.transactionHash; } catch (err) { this.log.error(`Rollup submit epoch proof failed`, err); return undefined; @@ -987,7 +997,7 @@ export class L1Publisher { { blobs: encodedData.blobs.map(b => b.dataWithZeros), kzg, - maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS + // maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS }, ); @@ -1052,9 +1062,7 @@ export class L1Publisher { ] as const; } - private async sendProposeTx( - encodedData: L1ProcessArgs, - ): Promise<{ receipt: TransactionReceipt | undefined; args: any; functionName: string; data: Hex } | undefined> { + private async sendProposeTx(encodedData: L1ProcessArgs): Promise { if (this.interrupted) { return undefined; } @@ -1066,7 +1074,7 @@ export class L1Publisher { functionName: 'propose', args, }); - const receipt = await this.l1TxUtils.sendAndMonitorTransaction( + const result = await this.l1TxUtils.sendAndMonitorTransaction( { to: this.rollupContract.address, data, @@ -1077,11 +1085,12 @@ export class L1Publisher { { blobs: encodedData.blobs.map(b => b.dataWithZeros), kzg, - maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS + // maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS }, ); return { - receipt, + receipt: result.receipt, + gasPrice: result.gasPrice, args, functionName: 'propose', data, @@ -1095,7 +1104,7 @@ export class L1Publisher { private async sendProposeAndClaimTx( encodedData: L1ProcessArgs, quote: EpochProofQuote, - ): Promise<{ receipt: TransactionReceipt | undefined; args: any; functionName: string; data: Hex } | undefined> { + ): Promise { if (this.interrupted) { return undefined; } @@ -1107,7 +1116,7 @@ export class L1Publisher { functionName: 'proposeAndClaim', args: [...args, quote.toViemArgs()], }); - const receipt = await this.l1TxUtils.sendAndMonitorTransaction( + const result = await this.l1TxUtils.sendAndMonitorTransaction( { to: this.rollupContract.address, data, @@ -1116,12 +1125,13 @@ export class L1Publisher { { blobs: encodedData.blobs.map(b => b.dataWithZeros), kzg, - maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS + // maxFeePerBlobGas: 10000000000n, //This is 10 gwei, taken from DEFAULT_MAX_FEE_PER_GAS }, ); return { - receipt, + receipt: result.receipt, + gasPrice: result.gasPrice, args: [...args, quote.toViemArgs()], functionName: 'proposeAndClaim', data, From 9a4fef2be7dcc867e9d72e5662e135b9e0202298 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 11:21:39 +0000 Subject: [PATCH 20/30] fix maybe undefined --- yarn-project/sequencer-client/src/publisher/l1-publisher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 9c2331a7d7a6..d82aaba80bf7 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -691,7 +691,7 @@ export class L1Publisher { { blobs: proposeTxArgs.blobs.map(b => b.dataWithZeros), kzg, - maxFeePerBlobGas: gasPrice.maxFeePerBlobGas, + maxFeePerBlobGas: gasPrice.maxFeePerBlobGas ?? 10000000000n, }, ); this.log.error(`Rollup process tx reverted. ${errorMsg}`, undefined, { From 1da65237024daaf527f45024147dcf43623fddc3 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 11:22:34 +0000 Subject: [PATCH 21/30] values updates --- spartan/aztec-network/values.yaml | 1 + .../values/sepolia-48-validators-with-metrics.yaml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spartan/aztec-network/values.yaml b/spartan/aztec-network/values.yaml index efdac924caac..c5f4946e0c2f 100644 --- a/spartan/aztec-network/values.yaml +++ b/spartan/aztec-network/values.yaml @@ -121,6 +121,7 @@ validator: viemPollingInterval: 1000 storageSize: "1Gi" dataDir: "/data" + l1FixedPriorityFeePerGas: "" proverNode: proverPublisherPrivateKey: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" diff --git a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml index 9e944e701fb6..0490d09448cb 100644 --- a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml @@ -12,6 +12,7 @@ ethereum: deployL1ContractsPrivateKey: validator: + l1FixedPriorityFeePerGas: 1 replicas: 48 validatorKeys: validatorAddresses: @@ -73,4 +74,4 @@ proverNode: proverPublisherPrivateKey: bot: - txIntervalSeconds: 20 \ No newline at end of file + txIntervalSeconds: 5 \ No newline at end of file From 11e448c96eff9dab716cca99c0465f265a1a29b8 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 11:40:22 +0000 Subject: [PATCH 22/30] update typing --- .../end-to-end/src/e2e_p2p/p2p_network.ts | 2 +- .../ethereum/src/deploy_l1_contracts.ts | 2 +- .../src/publisher/l1-publisher.test.ts | 38 +++++++++++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index e7a8f8fd56ba..906fea233912 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -148,7 +148,7 @@ export class P2PNetworkTest { this.logger.info('Syncing mock system time'); const { dateProvider, deployL1ContractsValues } = this.ctx!; // Send a tx and only update the time after the tx is mined, as eth time is not continuous - const receipt = await this.gasUtils!.sendAndMonitorTransaction({ + const { receipt } = await this.gasUtils!.sendAndMonitorTransaction({ to: this.baseAccount.address, data: '0x', value: 1n, diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 657c9ff71457..dec16a962bd9 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -762,7 +762,7 @@ export async function deployL1Contract( } else { // Regular deployment path const deployData = encodeDeployData({ abi, bytecode, args }); - const receipt = await l1TxUtils.sendAndMonitorTransaction({ + const { receipt } = await l1TxUtils.sendAndMonitorTransaction({ to: null, data: deployData, }); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index 689c03c71d16..b830db9b7239 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -1,6 +1,7 @@ import { L2Block } from '@aztec/circuit-types'; import { EthAddress } from '@aztec/circuits.js'; import { + type GasPrice, type L1ContractsConfig, type L1TxRequest, type L1TxUtilsConfig, @@ -40,7 +41,7 @@ interface MockL1TxUtils { sendAndMonitorTransaction: ( request: L1TxRequest, _gasConfig?: Partial, - ) => Promise; + ) => Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }>; } interface MockRollupContractWrite { @@ -146,7 +147,10 @@ describe('L1Publisher', () => { rollupContractRead.getCurrentSlot.mockResolvedValue(l2Block.header.globalVariables.slotNumber.toBigInt()); publicClient.getBlock.mockResolvedValue({ timestamp: 12n }); publicClient.estimateGas.mockResolvedValue(GAS_GUESS); - l1TxUtils.sendAndMonitorTransaction.mockResolvedValue(proposeTxReceipt); + l1TxUtils.sendAndMonitorTransaction.mockResolvedValue({ + receipt: proposeTxReceipt, + gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n }, + }); (l1TxUtils as any).estimateGas.mockResolvedValue(GAS_GUESS); }); @@ -244,7 +248,9 @@ describe('L1Publisher', () => { it('does not retry if sending a propose tx fails', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); - l1TxUtils.sendAndMonitorTransaction.mockRejectedValueOnce(new Error()).mockResolvedValueOnce(proposeTxReceipt); + l1TxUtils.sendAndMonitorTransaction + .mockRejectedValueOnce(new Error()) + .mockResolvedValueOnce({ receipt: proposeTxReceipt, gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n } }); const result = await publisher.proposeL2Block(l2Block); @@ -262,7 +268,9 @@ describe('L1Publisher', () => { it('does not retry if sending a publish and propose tx fails', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); - l1TxUtils.sendAndMonitorTransaction.mockRejectedValueOnce(new Error()).mockResolvedValueOnce(proposeTxReceipt); + l1TxUtils.sendAndMonitorTransaction + .mockRejectedValueOnce(new Error()) + .mockResolvedValueOnce({ receipt: proposeTxReceipt, gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n } }); const result = await publisher.proposeL2Block(l2Block); @@ -271,7 +279,10 @@ describe('L1Publisher', () => { it('returns false if publish and propose tx reverts', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); - l1TxUtils.sendAndMonitorTransaction.mockResolvedValueOnce({ ...proposeTxReceipt, status: 'reverted' }); + l1TxUtils.sendAndMonitorTransaction.mockResolvedValueOnce({ + receipt: { ...proposeTxReceipt, status: 'reverted' }, + gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n }, + }); const result = await publisher.proposeL2Block(l2Block); @@ -281,7 +292,10 @@ describe('L1Publisher', () => { it('returns false if propose tx reverts', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); - l1TxUtils.sendAndMonitorTransaction.mockResolvedValueOnce({ ...proposeTxReceipt, status: 'reverted' }); + l1TxUtils.sendAndMonitorTransaction.mockResolvedValueOnce({ + receipt: { ...proposeTxReceipt, status: 'reverted' }, + gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n }, + }); const result = await publisher.proposeL2Block(l2Block); @@ -291,7 +305,11 @@ describe('L1Publisher', () => { it('returns false if sending publish and progress tx is interrupted', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); l1TxUtils.sendAndMonitorTransaction.mockImplementationOnce( - () => sleep(10, proposeTxReceipt) as Promise, + () => + sleep(10, { receipt: proposeTxReceipt, gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n } }) as Promise<{ + receipt: TransactionReceipt; + gasPrice: GasPrice; + }>, ); const resultPromise = publisher.proposeL2Block(l2Block); publisher.interrupt(); @@ -304,7 +322,11 @@ describe('L1Publisher', () => { it('returns false if sending propose tx is interrupted', async () => { rollupContractRead.archive.mockResolvedValue(l2Block.header.lastArchive.root.toString() as `0x${string}`); l1TxUtils.sendAndMonitorTransaction.mockImplementationOnce( - () => sleep(10, proposeTxReceipt) as Promise, + () => + sleep(10, { receipt: proposeTxReceipt, gasPrice: { maxFeePerGas: 1n, maxPriorityFeePerGas: 1n } }) as Promise<{ + receipt: TransactionReceipt; + gasPrice: GasPrice; + }>, ); const resultPromise = publisher.proposeL2Block(l2Block); From b04b1b16da6a7c8cbb6aab17f39f02201de4469d Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 11:57:45 +0000 Subject: [PATCH 23/30] fix test --- .../sequencer-client/src/publisher/l1-publisher.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index b830db9b7239..1a4fd097f8ca 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -236,7 +236,7 @@ describe('L1Publisher', () => { data: encodeFunctionData({ abi: rollupContract.abi, functionName: 'propose', args }), }, { fixedGas: GAS_GUESS + L1Publisher.PROPOSE_GAS_GUESS }, - { blobs: expectedBlobs.map(b => b.dataWithZeros), kzg, maxFeePerBlobGas: 10000000000n }, + { blobs: expectedBlobs.map(b => b.dataWithZeros), kzg }, ); expect(sendToBlobSinkSpy).toHaveBeenCalledTimes(1); From 379e286b3bcb4d35a74c8657e94110ba2b0b47c0 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 12:59:06 +0000 Subject: [PATCH 24/30] try fixing logging again --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 35 ++++++++----------- yarn-project/ethereum/src/utils.ts | 22 ++++++------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index a47daa5fb8c0..8a5ecc2aced0 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -386,28 +386,23 @@ describe('GasUtils', () => { const firstError = parsed.errorChain[0]; expect(firstError.name).toBe('TransactionExecutionError'); expect(firstError.message).toContain('fee cap'); - expect(firstError.details).toContain('max fee per gas'); - - // Verify args contains expected transaction information - if (firstError.args) { - expect(Array.isArray(firstError.args)).toBe(true); - expect(firstError.args).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^from:\s+0x[a-fA-F0-9]{40}$/), - expect.stringMatching(/^to:\s+0x[a-fA-F0-9]{40}$/), - expect.stringMatching(/^value:\s+0 ETH$/), - expect.stringMatching(/^data:\s+0x$/), - expect.stringMatching(/^gas:\s+\d+$/), - expect.stringMatching(/^maxFeePerGas:\s+\d+(\.\d+)? gwei$/), - expect.stringMatching(/^maxPriorityFeePerGas:\s+\d+(\.\d+)? gwei$/), - ]), - ); - } - // Check that request body is properly truncated + // Verify no duplicate messages in error chain + const messages = parsed.errorChain.map((e: any) => e.message); + expect(new Set(messages).size).toBe(messages.length - 1); // contains duplicate message + + // Check request body formatting if present if (firstError.requestBody) { - expect(firstError.requestBody).toContain('eth_sendRawTransaction'); - expect(firstError.requestBody).toContain('...'); + const body = JSON.parse(firstError.requestBody); + expect(body.method).toBe('eth_sendRawTransaction'); + expect(body.params).toBeDefined(); + expect(Array.isArray(body.params)).toBe(true); + // Check params are truncated if too long + body.params.forEach((param: string) => { + if (param.length > 50) { + expect(param).toContain('...'); + } + }); } } }, 10_000); diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts index e2688a3dd014..8af45f17a9f7 100644 --- a/yarn-project/ethereum/src/utils.ts +++ b/yarn-project/ethereum/src/utils.ts @@ -110,6 +110,9 @@ export function formatViemError(error: any): string { // Keep other fields as is (from, blobVersionedHashes, etc.) return truncated; } + if (typeof param === 'string' && param.length > 1000) { + return truncateHex(param); + } return param; }); } @@ -122,8 +125,16 @@ export function formatViemError(error: any): string { }; const errorChain = []; + const seenMessages = new Set(); // To avoid duplicate error messages + if (error instanceof BaseError) { error.walk((err: any) => { + // Skip if we've seen this exact message before + if (seenMessages.has(err.message)) { + return false; + } + seenMessages.add(err.message); + const errorInfo: any = { name: err.name, message: err.shortMessage || err.message, @@ -171,14 +182,5 @@ export function formatViemError(error: any): string { }); } - return JSON.stringify(clean({ errorChain }), null, 2); + return JSON.stringify({ errorChain }, null, 2); } - -const clean = (obj: any) => { - Object.keys(obj).forEach(key => { - if (obj[key] === undefined) { - delete obj[key]; - } - }); - return obj; -}; From 6e95160123eeed60d90d6c1da456d11fd2d3b27f Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 13:03:20 +0000 Subject: [PATCH 25/30] use formatter in sendTransaction --- yarn-project/ethereum/src/l1_tx_utils.ts | 73 +++++++++++++----------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 5a55a45e2670..7ce8d450c723 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -202,44 +202,49 @@ export class L1TxUtils { _gasConfig?: Partial & { fixedGas?: bigint }, blobInputs?: L1BlobInputs, ): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> { - const gasConfig = { ...this.config, ..._gasConfig }; - const account = this.walletClient.account; - let gasLimit: bigint; + try { + const gasConfig = { ...this.config, ..._gasConfig }; + const account = this.walletClient.account; + let gasLimit: bigint; + + if (gasConfig.fixedGas) { + gasLimit = gasConfig.fixedGas; + } else { + gasLimit = await this.estimateGas(account, request); + } - if (gasConfig.fixedGas) { - gasLimit = gasConfig.fixedGas; - } else { - gasLimit = await this.estimateGas(account, request); - } + const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs); - const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs); - - let txHash: Hex; - if (blobInputs) { - txHash = await this.walletClient.sendTransaction({ - ...request, - ...blobInputs, - gas: gasLimit, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, - maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!, - }); - } else { - txHash = await this.walletClient.sendTransaction({ - ...request, - gas: gasLimit, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + let txHash: Hex; + if (blobInputs) { + txHash = await this.walletClient.sendTransaction({ + ...request, + ...blobInputs, + gas: gasLimit, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!, + }); + } else { + txHash = await this.walletClient.sendTransaction({ + ...request, + gas: gasLimit, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + }); + } + this.logger?.verbose(`Sent L1 transaction ${txHash}`, { + gasLimit, + maxFeePerGas: formatGwei(gasPrice.maxFeePerGas), + maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas), + ...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }), }); - } - this.logger?.verbose(`Sent L1 transaction ${txHash}`, { - gasLimit, - maxFeePerGas: formatGwei(gasPrice.maxFeePerGas), - maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas), - ...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }), - }); - return { txHash, gasLimit, gasPrice }; + return { txHash, gasLimit, gasPrice }; + } catch (err: any) { + this.logger?.error(`Failed to send transaction: ${formatViemError(err)}`); + throw err; + } } /** From 9fd9ec2ff666dc99249470206937340dbbd76e56 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 15:25:17 +0000 Subject: [PATCH 26/30] fix for <1 gwei prio fee --- yarn-project/ethereum/src/l1_tx_utils.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 7ce8d450c723..f9e513b800b6 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -69,7 +69,7 @@ export interface L1TxUtilsConfig { /** * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage */ - fixedPriorityFeePerGas?: bigint; + fixedPriorityFeePerGas?: number; /** * Maximum number of speed-up attempts */ @@ -125,9 +125,9 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { ...bigintConfigHelper(50n), }, fixedPriorityFeePerGas: { - description: 'Fixed priority fee per gas. Overrides any priority fee bump percentage', + description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage', env: 'L1_FIXED_PRIORITY_FEE_PER_GAS', - ...bigintConfigHelper(), + ...numberConfigHelper(0), }, maxAttempts: { description: 'Maximum number of speed-up attempts', @@ -430,9 +430,10 @@ export class L1TxUtils { let priorityFee: bigint; if (gasConfig.fixedPriorityFeePerGas) { this.logger?.debug('Using fixed priority fee per gas', { - fixedPriorityFeePerGas: formatGwei(gasConfig.fixedPriorityFeePerGas), + fixedPriorityFeePerGas: gasConfig, }); - priorityFee = gasConfig.fixedPriorityFeePerGas * WEI_CONST; + // try to maintain precision up to 1000000 wei + priorityFee = BigInt(gasConfig.fixedPriorityFeePerGas * 1_000_000) * (WEI_CONST / 1_000_000n); } else { // Get initial priority fee from the network priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas(); @@ -470,8 +471,10 @@ export class L1TxUtils { priorityFee = priorityFee > minPriorityFee ? priorityFee : minPriorityFee; maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee; } else { - // first attempt, just bump priority fee - priorityFee = (priorityFee * (100n + (gasConfig.priorityFeeBumpPercentage || 0n))) / 100n; + // first attempt, just bump priority fee, unless it's a fixed config + if (!gasConfig.fixedPriorityFeePerGas) { + priorityFee = (priorityFee * (100n + (gasConfig.priorityFeeBumpPercentage || 0n))) / 100n; + } maxFeePerGas += priorityFee; } From cfbe1d0a2db3f70d35d4c4ee98713734c6c91644 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 16:08:08 +0000 Subject: [PATCH 27/30] just format everywhere --- yarn-project/ethereum/src/utils.ts | 25 +++++++++++++++---- .../src/publisher/l1-publisher.ts | 10 ++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/yarn-project/ethereum/src/utils.ts b/yarn-project/ethereum/src/utils.ts index 8af45f17a9f7..df72c3c9b907 100644 --- a/yarn-project/ethereum/src/utils.ts +++ b/yarn-project/ethereum/src/utils.ts @@ -124,8 +124,21 @@ export function formatViemError(error: any): string { } }; + const formatArgs = (args: string[]) => { + return args.map(arg => { + // If it's a data field with a long hex string, truncate it + if (arg.startsWith('data:')) { + const [prefix, hexData] = arg.split(/:\s+/); + if (hexData && hexData.startsWith('0x') && hexData.length > 50) { + return `${prefix}: ${truncateHex(hexData)}`; + } + } + return arg; + }); + }; + const errorChain = []; - const seenMessages = new Set(); // To avoid duplicate error messages + const seenMessages = new Set(); if (error instanceof BaseError) { error.walk((err: any) => { @@ -143,10 +156,12 @@ export function formatViemError(error: any): string { // Extract request arguments if present const argsMatch = err.message?.match(/Request Arguments:\n([\s\S]*?)(?:\n\nDetails:|$)/); if (argsMatch) { - errorInfo.args = argsMatch[1] - .split('\n') - .map((line: string) => line.trim()) - .filter(Boolean); + errorInfo.args = formatArgs( + argsMatch[1] + .split('\n') + .map((line: string) => line.trim()) + .filter(Boolean), + ); } // Extract details if present diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index d82aaba80bf7..c7158b1c6aa6 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -22,7 +22,7 @@ import { type L1ContractsConfig, L1TxUtils, createEthereumChain, - prettyLogViemErrorMsg, + formatViemError, } from '@aztec/ethereum'; import { makeTuple } from '@aztec/foundation/array'; import { toHex } from '@aztec/foundation/bigint-buffer'; @@ -574,7 +574,7 @@ export class L1Publisher { account: this.account, }); } catch (err) { - const msg = prettyLogViemErrorMsg(err); + const msg = formatViemError(err); logger.error(`Failed to vote`, msg); this.myLastVotes[voteType] = cachedMyLastVote; return false; @@ -717,7 +717,7 @@ export class L1Publisher { }), }); } catch (err) { - this.log.error(`Failed to claim epoch proof right: ${prettyLogViemErrorMsg(err)}`, err, { + this.log.error(`Failed to claim epoch proof right: ${formatViemError(err)}`, err, { proofQuote: proofQuote.toInspect(), }); return false; @@ -1096,7 +1096,7 @@ export class L1Publisher { data, }; } catch (err) { - this.log.error(`Rollup publish failed: ${prettyLogViemErrorMsg(err)}`, err); + this.log.error(`Rollup publish failed: ${formatViemError(err)}`, err); return undefined; } } @@ -1137,7 +1137,7 @@ export class L1Publisher { data, }; } catch (err) { - this.log.error(`Rollup publish failed: ${prettyLogViemErrorMsg(err)}`, err); + this.log.error(`Rollup publish failed: ${formatViemError(err)}`, err); return undefined; } } From 9d1e93db4c43e5ef03b44791581bc96ffdc9e239 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 9 Jan 2025 17:25:50 +0000 Subject: [PATCH 28/30] allow 2 decimals in percentages, set lower gas estimate buffer --- .../aztec-network/templates/validator.yaml | 2 ++ spartan/aztec-network/values.yaml | 1 + .../sepolia-48-validators-with-metrics.yaml | 1 + yarn-project/ethereum/src/l1_tx_utils.ts | 32 +++++++++++-------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/spartan/aztec-network/templates/validator.yaml b/spartan/aztec-network/templates/validator.yaml index f1437617081b..bf21e4a0fff4 100644 --- a/spartan/aztec-network/templates/validator.yaml +++ b/spartan/aztec-network/templates/validator.yaml @@ -210,6 +210,8 @@ spec: value: {{ .Values.validator.viemPollingInterval | quote }} - name: L1_FIXED_PRIORITY_FEE_PER_GAS value: {{ .Values.validator.l1FixedPriorityFeePerGas | quote }} + - name: L1_GAS_LIMIT_BUFFER_PERCENTAGE + value: {{ .Values.validator.l1GasLimitBufferPercentage | quote }} - name: DATA_DIRECTORY value: "{{ .Values.validator.dataDir }}" - name: DATA_STORE_MAP_SIZE_KB diff --git a/spartan/aztec-network/values.yaml b/spartan/aztec-network/values.yaml index f7831c5760f5..efd30dbbd66c 100644 --- a/spartan/aztec-network/values.yaml +++ b/spartan/aztec-network/values.yaml @@ -128,6 +128,7 @@ validator: storageSize: "1Gi" dataDir: "/data" l1FixedPriorityFeePerGas: "" + l1GasLimitBufferPercentage: "" proverNode: proverPublisherPrivateKey: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" diff --git a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml index 0490d09448cb..25781c330f22 100644 --- a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml @@ -13,6 +13,7 @@ ethereum: validator: l1FixedPriorityFeePerGas: 1 + l1GasLimitBufferPercentage: 5 replicas: 48 validatorKeys: validatorAddresses: diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 9cc0e3ff7ce3..45336efa94df 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -32,11 +32,11 @@ const WEI_CONST = 1_000_000_000n; // setting a minimum bump percentage to 10% due to geth's implementation // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298 -const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10n; +const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10; // setting a minimum bump percentage to 100% due to geth's implementation // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/blobpool/config.go#L34 -const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100n; +const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100; // Avg ethereum block time is ~12s const BLOCK_TIME_MS = 12_000; @@ -45,7 +45,7 @@ export interface L1TxUtilsConfig { /** * How much to increase calculated gas limit. */ - gasLimitBufferPercentage?: bigint; + gasLimitBufferPercentage?: number; /** * Maximum gas price in gwei */ @@ -61,13 +61,13 @@ export interface L1TxUtilsConfig { /** * Priority fee bump percentage */ - priorityFeeBumpPercentage?: bigint; + priorityFeeBumpPercentage?: number; /** * How much to increase priority fee by each attempt (percentage) */ - priorityFeeRetryBumpPercentage?: bigint; + priorityFeeRetryBumpPercentage?: number; /** - * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage + * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage config */ fixedPriorityFeePerGas?: number; /** @@ -97,7 +97,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { gasLimitBufferPercentage: { description: 'How much to increase gas price by each attempt (percentage)', env: 'L1_GAS_LIMIT_BUFFER_PERCENTAGE', - ...bigintConfigHelper(20n), + ...numberConfigHelper(10), }, minGwei: { description: 'Minimum gas price in gwei', @@ -117,12 +117,12 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType = { priorityFeeBumpPercentage: { description: 'How much to increase priority fee by each attempt (percentage)', env: 'L1_PRIORITY_FEE_BUMP_PERCENTAGE', - ...bigintConfigHelper(20n), + ...numberConfigHelper(20), }, priorityFeeRetryBumpPercentage: { description: 'How much to increase priority fee by each retry attempt (percentage)', env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE', - ...bigintConfigHelper(50n), + ...numberConfigHelper(50), }, fixedPriorityFeePerGas: { description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage', @@ -466,8 +466,10 @@ export class L1TxUtils { const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage; // Calculate minimum required fees based on previous attempt - const minPriorityFee = (previousGasPrice!.maxPriorityFeePerGas * (100n + bumpPercentage)) / 100n; - const minMaxFee = (previousGasPrice!.maxFeePerGas * (100n + bumpPercentage)) / 100n; + // multiply by 100 & divide by 100 to maintain some precision + const minPriorityFee = + (previousGasPrice!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n; + const minMaxFee = (previousGasPrice!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n; // Add priority fee to maxFeePerGas maxFeePerGas += priorityFee; @@ -477,8 +479,9 @@ export class L1TxUtils { maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee; } else { // first attempt, just bump priority fee, unless it's a fixed config + // multiply by 100 & divide by 100 to maintain some precision if (!gasConfig.fixedPriorityFeePerGas) { - priorityFee = (priorityFee * (100n + (gasConfig.priorityFeeBumpPercentage || 0n))) / 100n; + priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n; } maxFeePerGas += priorityFee; } @@ -503,7 +506,7 @@ export class L1TxUtils { : MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE; // calculate min blob fee based on previous attempt - const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100n + bumpPercentage)) / 100n; + const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n; // use max between current network values and min required values maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee; @@ -553,7 +556,8 @@ export class L1TxUtils { } // Add buffer based on either fixed amount or percentage - const withBuffer = initialEstimate + (initialEstimate * (gasConfig.gasLimitBufferPercentage ?? 0n)) / 100n; + const withBuffer = + initialEstimate + (initialEstimate * BigInt((gasConfig.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n; return withBuffer; } From 7140c1ee525f25dd615a3b520024d1da852e6f95 Mon Sep 17 00:00:00 2001 From: spypsy Date: Fri, 10 Jan 2025 00:18:14 +0000 Subject: [PATCH 29/30] update test --- yarn-project/ethereum/src/l1_tx_utils.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 3f588d9a41e8..61a4de07fb6a 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -73,7 +73,7 @@ describe('GasUtils', () => { await cheatCodes.evmMine(); gasUtils = new L1TxUtils(publicClient, walletClient, logger, { - gasLimitBufferPercentage: 20n, + gasLimitBufferPercentage: 20, maxGwei: 500n, minGwei: 1n, maxAttempts: 3, @@ -201,7 +201,7 @@ describe('GasUtils', () => { // First deploy without any buffer const baselineGasUtils = new L1TxUtils(publicClient, walletClient, logger, { - gasLimitBufferPercentage: 0n, + gasLimitBufferPercentage: 0, maxGwei: 500n, minGwei: 10n, // Increased minimum gas price maxAttempts: 5, @@ -221,7 +221,7 @@ describe('GasUtils', () => { // Now deploy with 20% buffer const bufferedGasUtils = new L1TxUtils(publicClient, walletClient, logger, { - gasLimitBufferPercentage: 20n, + gasLimitBufferPercentage: 20, maxGwei: 500n, minGwei: 1n, maxAttempts: 3, @@ -281,7 +281,7 @@ describe('GasUtils', () => { it('respects minimum gas price bump for replacements', async () => { const gasUtils = new L1TxUtils(publicClient, walletClient, logger, { ...defaultL1TxUtilsConfig, - priorityFeeRetryBumpPercentage: 5n, // Set lower than minimum 10% + priorityFeeRetryBumpPercentage: 5, // Set lower than minimum 10% }); const initialGasPrice = await gasUtils['getGasPrice'](); From da68f1a72d2c20fe783b0f85eb987b6dd3161bac Mon Sep 17 00:00:00 2001 From: spypsy Date: Fri, 10 Jan 2025 00:44:00 +0000 Subject: [PATCH 30/30] another --- yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 906fea233912..ba092589a221 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -300,7 +300,7 @@ export class P2PNetworkTest { this.ctx.deployL1ContractsValues.walletClient, this.logger, { - gasLimitBufferPercentage: 20n, + gasLimitBufferPercentage: 20, maxGwei: 500n, minGwei: 1n, maxAttempts: 3,