Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions yarn-project/ethereum/src/l1_tx_utils/l1_tx_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,50 @@ describe('L1TxUtils', () => {
}
});

it('bumps gas fees correctly at very low wei values (ceiling division)', async () => {
await cheatCodes.setNextBlockBaseFeePerGas(1n);
await cheatCodes.evmMine();

const originalGetBlobBaseFee = l1Client.getBlobBaseFee;
l1Client.getBlobBaseFee = () => Promise.resolve(1n);

const originalEstimate = l1Client.estimateMaxPriorityFeePerGas;
l1Client.estimateMaxPriorityFeePerGas = () => Promise.resolve(0n);

try {
gasUtils.updateConfig({
...defaultL1TxUtilsConfig,
stallTimeMs: 12_000,
priorityFeeBumpPercentage: 0,
minimumPriorityFeePerGas: 0,
});

const gasPrice = await gasUtils['getGasPrice'](undefined, true);

// With ceiling division: (1n * 1125n + 999n) / 1000n = 2n
expect(gasPrice.maxFeePerGas).toBe(2n);
expect(gasPrice.maxFeePerBlobGas).toBe(2n);

// Verify compounding works across multiple iterations
gasUtils.updateConfig({
...defaultL1TxUtilsConfig,
stallTimeMs: 24_000,
priorityFeeBumpPercentage: 0,
minimumPriorityFeePerGas: 0,
});

const gasPrice2 = await gasUtils['getGasPrice'](undefined, true);

// Iteration 1: ceil(1 * 1125 / 1000) = 2
// Iteration 2: ceil(2 * 1125 / 1000) = ceil(2.25) = 3
expect(gasPrice2.maxFeePerGas).toBe(3n);
expect(gasPrice2.maxFeePerBlobGas).toBe(3n);
} finally {
l1Client.getBlobBaseFee = originalGetBlobBaseFee;
l1Client.estimateMaxPriorityFeePerGas = originalEstimate;
}
});

it('calculates correct gas prices for retry attempts', async () => {
await cheatCodes.setNextBlockBaseFeePerGas(WEI_CONST);
await cheatCodes.evmMine();
Expand Down
12 changes: 8 additions & 4 deletions yarn-project/ethereum/src/l1_tx_utils/readonly_l1_tx_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ export class ReadOnlyL1TxUtils {
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;
// ceil, (a+b-1)/b, to avoid truncation at small values (e.g. 1 wei blob base fee)
maxFeePerGas = (maxFeePerGas * (1_000n + 125n) + 999n) / 1_000n;
// same for blob gas fee
maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n)) / 1_000n;
maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n) + 999n) / 1_000n;
}

if (attempt > 0) {
Expand Down Expand Up @@ -242,13 +243,16 @@ export class ReadOnlyL1TxUtils {
const gasConfig = { ...this.config, ..._gasConfig };
let initialEstimate = 0n;
if (_blobInputs) {
// @note requests with blobs also require maxFeePerBlobGas to be set
// @note requests with blobs also require maxFeePerBlobGas to be set.
// Use 2x buffer for maxFeePerBlobGas to avoid stale fees and to pass EIP-4844 validation (even if it is a gas estimation call).
// 1. maxFeePerBlobGas >= blobBaseFee
// 2. account balance >= gas * maxFeePerGas + maxFeePerBlobGas * blobCount + value
const gasPrice = await this.getGasPrice(gasConfig, true, 0);
initialEstimate = await this.client.estimateGas({
account,
...request,
..._blobInputs,
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas! * 2n,
gas: MAX_L1_TX_LIMIT,
blockTag: 'latest',
});
Expand Down
Loading