Skip to content

Commit 2e8fc2c

Browse files
spalladinoclaude
andcommitted
test(sequencer-client): add roll-forward regression test for fee predictor
Addresses PR #22116 review comment 6: validates that the predictor tracks rollup.getManaMinFeeAt across successive slots over time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7d7975b commit 2e8fc2c

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

yarn-project/sequencer-client/src/global_variable_builder/fee_predictor.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,88 @@ describe('FeePredictor', () => {
223223
},
224224
60_000,
225225
);
226+
227+
it('predictions match L1 across successive slots over time', async () => {
228+
const constantBaseFee = 50_000_000_000n;
229+
230+
// Pin L1 fees to a constant by updating the oracle twice (sets both pre and post).
231+
await rollupCheatCodes.advanceSlots(FEE_ORACLE_LAG + 1);
232+
await cheatCodes.setNextBlockBaseFeePerGas(constantBaseFee);
233+
await rollupCheatCodes.updateL1GasFeeOracle();
234+
await rollupCheatCodes.advanceSlots(FEE_ORACLE_LAG + 1);
235+
await cheatCodes.setNextBlockBaseFeePerGas(constantBaseFee);
236+
await rollupCheatCodes.updateL1GasFeeOracle();
237+
await rollupCheatCodes.advanceSlots(3);
238+
await cheatCodes.mine();
239+
240+
const manaTarget = await rollup.getManaTarget();
241+
const rollupAddress = EthAddress.fromString(rollup.address);
242+
243+
/** Writes a fee header and slot number for the given checkpoint, then bumps the pending tip. */
244+
async function advanceCheckpoint(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader, slotNumber: bigint) {
245+
const feeHeaderSlot = await rollup.getTempCheckpointLogStorageSlot(
246+
checkpointNumber,
247+
TempCheckpointLogField.FeeHeader,
248+
);
249+
await cheatCodes.store(rollupAddress, feeHeaderSlot, RollupContract.compressFeeHeader(feeHeader));
250+
251+
const slotNumberSlot = await rollup.getTempCheckpointLogStorageSlot(
252+
checkpointNumber,
253+
TempCheckpointLogField.SlotNumber,
254+
);
255+
await cheatCodes.store(rollupAddress, slotNumberSlot, slotNumber & ((1n << 32n) - 1n));
256+
257+
const currentTips = await cheatCodes.load(rollupAddress, RollupContract.chainTipsStorageSlot);
258+
const provenCheckpointNumber = currentTips & ((1n << 128n) - 1n);
259+
await cheatCodes.store(
260+
rollupAddress,
261+
RollupContract.chainTipsStorageSlot,
262+
RollupContract.packChainTips(BigInt(checkpointNumber), provenCheckpointNumber),
263+
);
264+
}
265+
266+
const pendingCheckpointNumber = await rollup.getCheckpointNumber();
267+
const currentFeeHeader = (await rollup.getCheckpoint(pendingCheckpointNumber)).feeHeader;
268+
269+
let prevExcessMana = currentFeeHeader.excessMana;
270+
let prevManaUsed = currentFeeHeader.manaUsed;
271+
let nextCheckpointOffset = 1;
272+
273+
// Step through 6 successive slots, creating a fresh predictor each time.
274+
for (let step = 0; step < 6; step++) {
275+
const predictor = new FeePredictor(rollup, slotDuration, l1GenesisTime);
276+
const predicted = await predictor.getPredictedMinFees(publicClient, ManaUsageEstimate.None);
277+
278+
expect(predicted.length).toBe(FEE_ORACLE_LAG + 1);
279+
280+
const startSlot = await getPredictionStartSlot();
281+
for (let i = 0; i < predicted.length; i++) {
282+
const l1Fee = await rollup.getManaMinFeeAt(getTimestamp(startSlot + BigInt(i)), true);
283+
expect(predicted[i].feePerL2Gas).toBe(l1Fee);
284+
}
285+
286+
// Advance: simulate proposing a checkpoint at the current slot with zero mana usage.
287+
const newExcessMana = computeExcessMana(prevExcessMana, prevManaUsed, manaTarget);
288+
const newCheckpointNumber = CheckpointNumber.add(pendingCheckpointNumber, nextCheckpointOffset);
289+
const newFeeHeader: FeeHeader = {
290+
excessMana: newExcessMana,
291+
manaUsed: 0n,
292+
ethPerFeeAsset: currentFeeHeader.ethPerFeeAsset,
293+
congestionCost: 0n,
294+
proverCost: 0n,
295+
};
296+
297+
await advanceCheckpoint(newCheckpointNumber, newFeeHeader, startSlot);
298+
299+
// Warp to the next slot.
300+
const nextTimestamp = getTimestamp(startSlot + 1n);
301+
await cheatCodes.warp(Number(nextTimestamp));
302+
await cheatCodes.setNextBlockBaseFeePerGas(constantBaseFee);
303+
await cheatCodes.mine();
304+
305+
prevExcessMana = newExcessMana;
306+
prevManaUsed = 0n;
307+
nextCheckpointOffset++;
308+
}
309+
}, 60_000);
226310
});

0 commit comments

Comments
 (0)