L1 Data Fee
Lighter EVM publishes pubdata — the data required to reconstruct public protocol state — to L1 Ethereum blobs. Pubdata currently consists of state diffs and EVM-to-Core write responses. In addition to the ordinary execution fee, every fee-paying transaction pays an L1 data fee (also called the pubdata fee) for its metered pubdata contribution.
The data fee has two independent inputs:
- The transaction's pubdata contribution, metered deterministically during execution.
- The price per pubdata byte, published by the sequencer once per block.
These inputs estimate the transaction's share of the eventual L1 data-availability cost.
Note that state-diff contributions to pubdata are metered per block, even though the state-diff submitted to L1 is aggregated across many blocks. This is done so the sequencer can issue pre-confirmations without knowing the eventual batch boundary.
Since this necessarily over-estimates the actual byte-contribution, the sequencer accounts for this expected aggregation when selecting the trusted blobBaseFee, amortizing the expected batch blob cost over the block-metered bytes.
Pubdata contribution
A transaction's metered pubdata contribution has two parts: state diffs and EVM-to-Core write responses.
State diffs
When a transaction changes an account, the protocol meters differences in four account-state components: nonce, balance, code, and storage. Each component materialized in the evolving block diff contributes its encoded size:
| Account field | Bytes |
|---|---|
| Nonce | 32 |
| Balance | 32 |
| Storage slot | 64 (32-byte key + 32-byte value) |
| Code | Length of the runtime code |
Two structural overheads are charged alongside the fields: a 25-byte header for each account that appears in the diff, and a one-time 4-byte length prefix for each account with storage entries.
Metering uses the state at the beginning of the block as its baseline:
- Divergence materializes a field. A field is added to the evolving diff when it first differs from its pre-block value.
- The first writer pays while the field remains materialized. Later transactions can overwrite an already-materialized field without adding pubdata bytes.
- Restoration removes a field. Returning a field to its pre-block value removes its bytes from the evolving diff. The resulting refund is applied at settlement and does not replenish the transaction's in-flight budget.
- Clearing storage is not inherently free. Setting an old non-zero slot to zero still differs from its pre-block value and is therefore a charged write. Only restoration to the pre-block value removes the entry.
- Self-destruct removes only same-transaction creations. Under current EVM semantics, self-destruct deletes an account only when it was created in the same transaction. Deletion removes the bytes materialized for the account's nonce, code, and storage. Note that if the address held funds before the block, account-clearing implies the state of the account's balance goes from non-zero to zero, which is charged for.
EVM-to-Core write responses
Each EVM-to-Core write is charged for a response record consisting of a 1-byte status header plus the greater of the success and error payload widths for its write type. The resulting charge is 1–9 bytes.
Price per byte
The sequencer publishes blobBaseFee once per block; the GasPriceOracle predeploy at 0x660000000000000000000000000000000000000d exposes the current value. The protocol derives the price per pubdata byte as:
pricePerPubdataByte = ceil(blobBaseFee * GAS_PER_BLOB / BYTES_PER_BLOB)
= ceil(blobBaseFee * 131072 / 126976)131072 is the EIP-4844 gas per blob and 126976 is the usable pubdata capacity of a blob. blobBaseFee is a trusted sequencer input: the protocol uses the published value and does not verify it against L1.
Transaction lifecycle
The pubdata-charging lifecycle has four phases:
- Validation. The protocol computes a deterministic intrinsic byte reservation and rejects the transaction if the reservation does not fit its pubdata budget or the block's remaining pubdata capacity.
- Pre-execution. A fee-paying transaction prepays its maximum possible data fee.
- Execution. State-diff growth and EVM-to-Core responses consume the transaction's pubdata budget.
- Settlement. The final fee is credited to the pubdata fee vault and the unused reserve is returned to the sender.
Validation
Each transaction has a hard pubdata byte limit derived from its gas limit:
pubdataByteLimit = floor(gasLimit / PUBDATA_GAS_PER_BYTE)
PUBDATA_GAS_PER_BYTE = 70Pubdata accounting is independent of execution gas: the GAS opcode is unaffected, but gasLimit controls both the execution-gas limit and the pubdata byte limit.
For an ordinary fee-paying transaction, the intrinsic reservation is:
intrinsicPubdataBytes = 260 + 80 * authorizationListLengthThe fixed 260 bytes cover the sender's account header, balance, and nonce together with the account headers and balances of the three fee vaults. Each EIP-7702 authorization tuple adds a conservative 80 bytes for an account header, nonce, and delegation designation. Fee-exempt exogenous transactions cannot carry an authorization list and reserve 89 intrinsic bytes for the sender's account header, balance, and nonce.
The transaction is rejected before execution if intrinsicPubdataBytes exceeds pubdataByteLimit, or if it does not fit into the block's remaining pubdata capacity (see Block limit). The reservation itself is deducted from the pubdata budget when execution starts.
Pre-execution
Before an ordinary transaction executes, the sender prepays its maximum possible data fee:
maxPubdataFee = pubdataByteLimit * pricePerPubdataByteIncreasing gasLimit therefore requires a larger up-front balance, but unused execution gas and unused pubdata reserve are returned at settlement.
Execution
The execution budget begins at pubdataByteLimit - intrinsicPubdataBytes. State-diff growth and EVM-to-Core response records consume this budget. State-diff removals reduce the final charge but do not restore budget during execution.
If a message call or contract creation exceeds the remaining pubdata budget, that frame reverts to its checkpoint, and the pubdata consumed within the frame is returned to the execution budget. This is not an exceptional halt, so unused gas is also refunded. The transaction reverts only if the failure propagates to the top level.
Settlement
At settlement, the metered contribution is:
stateDiffBytes = max(0, intrinsicPubdataBytes + addedStateDiffBytes - removedStateDiffBytes)
meteredPubdataBytes = stateDiffBytes + interopResponseBytes
l1Fee = meteredPubdataBytes * pricePerPubdataByteFee-exempt exogenous transactions follow the same byte metering and budget rules but do not prepay or settle an L1 data fee inside the EVM.
Block limit
The cumulative metered contribution of a block may not exceed MAX_EVM_PUBDATA_BYTES_PER_BLOCK = 253,952 bytes, the usable capacity of two blobs. A transaction is rejected at validation if its intrinsic reservation alone does not fit. Runtime consumption that would cross the limit triggers the same frame-revert behavior as exhausting the transaction's own budget.
Fee vaults
Transaction fees are routed to three predeploy vaults:
| Fee component | Destination |
|---|---|
| Base fee | BaseFeeVault (0x660000000000000000000000000000000000000a) |
| L1 data fee | PubdataFeeVault (0x660000000000000000000000000000000000000b) |
| Priority tip | SequencerFeeVault (0x6600000000000000000000000000000000000002) |
COINBASE returns the SequencerFeeVault address. Unlike L1 Ethereum, Lighter EVM does not burn base fees; it sends them to the BaseFeeVault.
Receipt fields
eth_getTransactionReceipt exposes the metered data fee through three fields:
| Receipt field | Meaning |
|---|---|
l1GasUsed | Pubdata bytes metered for the transaction |
l1GasPrice | Price per pubdata byte, in wei |
l1Fee | l1GasUsed × l1GasPrice, in wei |
Fee-exempt exogenous transactions still report their metered bytes in l1GasUsed, but report l1GasPrice = 0 and l1Fee = 0. See the JSON-RPC API for details. The standard gasUsed and effectiveGasPrice fields continue to report execution-gas accounting separately.
Impacts on account abstraction (ERC-4337)
Lighter EVM provides ERC-4337 EntryPoint versions 0.6 through 0.9 as preinstalls.
The L1 data fee is charged to the sender of the outer L2 transaction. For an ERC-4337 bundle, that sender is the bundler's EOA. The EntryPoint meters and reimburses execution gas, but it does not measure Lighter EVM pubdata or reimburse the bundler for the resulting data fee.
Bundlers can recover this cost by adding a data-fee margin to each UserOperation's preVerificationGas, the ERC-4337 field for per-operation overhead that is not measured directly by EntryPoint execution.
Pricing a UserOperation
For the safest one-operation-per-transaction policy described below, price the full pubdata budget of the outer L2 transaction:
pubdataByteLimit = floor(transactionGasLimit / PUBDATA_GAS_PER_BYTE)
dataFeeWei = pubdataByteLimit * pricePerPubdataByte
estimatedUserOpGasPrice =
min(maxFeePerGas, maxPriorityFeePerGas + estimatedBlockBaseFee)
dataFeeGas = ceil(dataFeeWei / estimatedUserOpGasPrice)transactionGasLimit is the outer L2 transaction's gas limit, not an individual UserOperation gas-limit field. pricePerPubdataByte is calculated from the current blobBaseFee as described above.
Add dataFeeGas to preVerificationGas. Because blobBaseFee and the block base fee may change before inclusion, bundlers should add padding and round up. Apply any safety margin on estimatedUserOpGasPrice downward: a lower denominator produces a larger, more conservative dataFeeGas.
This calculation deliberately uses the transaction's worst-case pubdata budget. It will overcharge operations that materialize less pubdata, but it prevents the bundler from under-recovering the transaction-level fee.
Bundling policy
The protocol enforces one pubdata budget for the outer L2 transaction, not a separate budget for each UserOperation. A multi-operation bundle therefore has no enforceable per-operation allocation. A successful operation can consume more of the shared budget than the bundler expected, leaving later operations without enough pubdata budget and potentially causing them to fail.
Sending one UserOperation per L2 transaction aligns the priced operation with the protocol-enforced budget. This trades bundling efficiency for predictable fee recovery and removes cross-operation pubdata interference.
Smart contract guidance
- Pack values into fewer storage slots and reuse already-materialized slots where practical.
- Do not treat clearing a storage slot as a pubdata optimization. It saves pubdata only when it restores the slot's pre-block value.
- Increase
gasLimitwhen pubdata is the binding resource. This increases the pubdata budget as well as the execution-gas limit.
Updated about 7 hours ago
