OP Stack receipt l1Fee is not l1GasPrice times l1GasUsed — post-Ecotone GasPriceOracle scales blob base fee separately
Symptom: on Base/Optimism (or any OP Stack chain) eth_getTransactionReceipt returns l1Fee, l1GasPrice and l1GasUsed, and the widely repeated formula l1Fee = l1GasPrice * l1GasUsed overshoots badly — e.g. 1600 * 17701 = 28,321,600 against a reported l1Fee of 31,248.
Cause: that formula describes the pre-Ecotone (Bedrock) fee market. Since the Ecotone upgrade, GasPriceOracle.getL1Fee() dispatches to _getL1FeeEcotone(), which prices L1 data using two independent scalars — one on the L1 base fee and one on the blob base fee — because rollup batches are posted as EIP-4844 blobs. In shape:
l1GasUsed = calldata cost of the tx (zero bytes 4, non-zero 16, plus overhead)
scaledFee = 16 * baseFeeScalar * l1BaseFee + blobBaseFeeScalar * l1BlobBaseFee
l1Fee = l1GasUsed * scaledFee / (16 * 1e6)The scalars are chain-set values readable from the oracle (baseFeeScalar(), blobBaseFeeScalar()), and the 1e6 denominator is why multiplying the raw receipt fields is off by orders of magnitude. The receipt's l1GasPrice reports only the L1 base fee, so it cannot reconstruct the fee on its own; blob pricing usually dominates.
How to get it right: call GasPriceOracle (predeploy 0x420000000000000000000000000000000000000F) — getL1Fee(bytes) for an unsigned tx, or read the scalars and compute — rather than deriving it from receipt fields. Fjord changed the calculation again (FastLZ-based compressed-size estimation with a 100-byte floor), so branch on the active fork and re-check after each upgrade; the figures here are Ecotone-era (2024).
Source: adapted from a Stack Exchange answer by Zak Ayesh (question by johnhckuo), https://ethereum.stackexchange.com/questions/164337/optimism-l1-fee-calculation — CC BY-SA 4.0