> For the complete documentation index, see [llms.txt](https://docs.lighter.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lighter.xyz/evm/differences-from-l1-evm/l1-data-fee.md).

# 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`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/GasPriceOracle.sol) 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:

1. **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.
2. **Pre-execution.** A fee-paying transaction prepays its maximum possible data fee.
3. **Execution.** State-diff growth and EVM-to-Core responses consume the transaction's pubdata budget.
4. **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 = 70
```

Pubdata 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 * authorizationListLength
```

The 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](#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 * pricePerPubdataByte
```

Increasing `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 * pricePerPubdataByte
```

Fee-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](#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`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/SequencerFeeVault.sol) (`0x660000000000000000000000000000000000000a`)      |
| L1 data fee   | [`PubdataFeeVault`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/SequencerFeeVault.sol) (`0x660000000000000000000000000000000000000b`)   |
| Priority tip  | [`SequencerFeeVault`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/SequencerFeeVault.sol) (`0x6600000000000000000000000000000000000002`) |

`COINBASE` returns the [`SequencerFeeVault`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/SequencerFeeVault.sol) address. Unlike L1 Ethereum, Lighter EVM does not burn base fees; it sends them to the [`BaseFeeVault`](https://github.com/elliottech/lighter-contracts/blob/lighter-evm-devnet/contracts/SequencerFeeVault.sol).

## 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](/evm/json-rpc-api.md#l1-data-fee-fields) 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](/evm/chain-info.md#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](#bundling-policy), 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](#price-per-byte).

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 `gasLimit` when pubdata is the binding resource. This increases the pubdata budget as well as the execution-gas limit.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.lighter.xyz/evm/differences-from-l1-evm/l1-data-fee.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
