Skip to content

Commit 7829d8d

Browse files
authored
fix: speed-up attempt blob fee (#16931)
- use newly calculated `maxFeePerBlobGas` in new TX - retry fetching blob fees. Blob txs are specifically very important so applying retry mechanism just for those Fixes https://linear.app/aztec-labs/issue/TMNT-312/details-transaction-gas-price-below-minimum-blob-fee-cap-0-minimum
2 parents ee91a6d + a9ccd01 commit 7829d8d

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

yarn-project/ethereum/src/l1_tx_utils.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,78 @@ describe('L1TxUtils', () => {
9494
});
9595
});
9696

97+
it('regression: speed-up of blob tx via L1TxUtils sets non-zero maxFeePerBlobGas', async () => {
98+
await cheatCodes.setAutomine(false);
99+
await cheatCodes.setIntervalMining(0);
100+
101+
const baseUtils = createL1TxUtilsFromViemWallet(l1Client, logger, dateProvider, {
102+
gasLimitBufferPercentage: 20,
103+
maxGwei: 500n,
104+
maxAttempts: 1,
105+
checkIntervalMs: 50,
106+
stallTimeMs: 300,
107+
});
108+
109+
const blobData = new Uint8Array(131072).fill(1);
110+
const kzg = Blob.getViemKzgInstance();
111+
112+
const request = {
113+
to: '0x1234567890123456789012345678901234567890' as `0x${string}`,
114+
data: '0x' as `0x${string}`,
115+
value: 0n,
116+
} as const;
117+
118+
const estimatedGas = await l1Client.estimateGas(request);
119+
120+
// Send initial blob tx with a valid maxFeePerBlobGas
121+
const { txHash } = await baseUtils.sendTransaction(request, undefined, {
122+
blobs: [blobData],
123+
kzg,
124+
maxFeePerBlobGas: 10n * WEI_CONST,
125+
});
126+
127+
// Capture the replacement tx when it is being signed
128+
const originalSign = l1Client.signTransaction;
129+
const signedTxs: TransactionSerializable[] = [];
130+
using _spy = jest.spyOn(l1Client, 'signTransaction').mockImplementation((arg: any) => {
131+
signedTxs.push(arg);
132+
return originalSign(arg);
133+
});
134+
135+
// Trigger monitor with blob inputs but WITHOUT maxFeePerBlobGas so the bug manifests
136+
const monitorPromise = baseUtils.monitorTransaction(
137+
request,
138+
txHash,
139+
new Set(),
140+
{ gasLimit: estimatedGas },
141+
undefined,
142+
{
143+
blobs: [blobData],
144+
kzg,
145+
},
146+
);
147+
148+
// Wait until a speed-up is attempted
149+
await retryUntil(
150+
() => baseUtils['state'] === TxUtilsState.SPEED_UP || signedTxs.length > 0,
151+
'waiting for speed-up',
152+
40,
153+
0.05,
154+
);
155+
156+
// Interrupt to stop the monitor loop and avoid hanging the test
157+
baseUtils.interrupt();
158+
await expect(monitorPromise).rejects.toThrow();
159+
160+
// Ensure we captured a replacement tx being signed
161+
expect(signedTxs.length).toBeGreaterThan(0);
162+
const replacement = signedTxs[signedTxs.length - 1] as any;
163+
164+
// Assert fix: maxFeePerBlobGas is populated and non-zero on replacement
165+
expect(replacement.maxFeePerBlobGas).toBeDefined();
166+
expect(replacement.maxFeePerBlobGas!).toBeGreaterThan(0n);
167+
}, 20_000);
168+
97169
it('sends and monitors a simple transaction', async () => {
98170
const { receipt } = await gasUtils.sendAndMonitorTransaction({
99171
to: '0x1234567890123456789012345678901234567890',

yarn-project/ethereum/src/l1_tx_utils.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,13 @@ export class ReadOnlyL1TxUtils {
279279
let blobBaseFee = 0n;
280280
if (isBlobTx) {
281281
try {
282-
blobBaseFee = await this.client.getBlobBaseFee();
282+
blobBaseFee = await retry<bigint>(
283+
() => this.client.getBlobBaseFee(),
284+
'Getting L1 blob base fee',
285+
makeBackoff(times(2, () => 1)),
286+
this.logger,
287+
true,
288+
);
283289
this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
284290
} catch {
285291
this.logger?.warn('Failed to get L1 blob base fee', attempt);
@@ -864,14 +870,17 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
864870
},
865871
);
866872

867-
const txData = {
873+
const txData: PrepareTransactionRequestRequest = {
868874
...request,
869875
...blobInputs,
870876
nonce,
871877
gas: params.gasLimit,
872878
maxFeePerGas: newGasPrice.maxFeePerGas,
873879
maxPriorityFeePerGas: newGasPrice.maxPriorityFeePerGas,
874880
};
881+
if (isBlobTx && newGasPrice.maxFeePerBlobGas) {
882+
(txData as any).maxFeePerBlobGas = newGasPrice.maxFeePerBlobGas;
883+
}
875884
const signedRequest = await this.prepareSignedTransaction(txData);
876885
const newHash = await this.client.sendRawTransaction({ serializedTransaction: signedRequest });
877886
if (!isCancelTx) {

0 commit comments

Comments
 (0)