Skip to content
BaseHub by wbnns Updated

Jovian L2 Execution Engine

Jovian reworks several execution-layer rules. It adds a configurable minimum base fee, introduces a DA footprint block limit that ties data-availability usage into base fee dynamics, raises the ceiling on the operator fee, and tightens input-size limits on a handful of precompiles.

Jovian adds a configurable minimum base fee whose purpose is to shorten priority-fee auctions on Base.

It is configured in SystemConfig (see System Configuration); the execution engine then enforces it, encoding the value into the block header’s extraData and threading it through the PayloadAttributesV3 parameters of the Engine API.

As with Holocene’s dynamic EIP-1559 parameters, Jovian packs fee parameters into each L2 block header’s extraData. The layout grows by one u64 field holding the minimum base fee in wei.

NameTypeByte Offset
minBaseFeeu64 (big-endian)[9, 17)

Constraints:

  • version MUST be 1 (raised from Holocene’s 0).
  • Nothing may appear past these 17 bytes.

minBaseFee is an absolute floor expressed in wei. When the base fee is computed, a result below minBaseFee MUST be clamped up to it:

if (baseFee < minBaseFee) {
baseFee = minBaseFee
}

Note: extraData is capped at 32 bytes (matching the L1 beacon-chain extraData type), and future upgrades may extend it.

The Engine API PayloadAttributesV3 gains a new minBaseFee field. The existing eip1559Params stays at 8 bytes (the Holocene format).

PayloadAttributesV3: {
timestamp: QUANTITY
prevRandao: DATA (32 bytes)
suggestedFeeRecipient: DATA (20 bytes)
withdrawals: array of WithdrawalV1
parentBeaconBlockRoot: DATA (32 bytes)
transactions: array of DATA
noTxPool: bool
gasLimit: QUANTITY or null
eip1559Params: DATA (8 bytes) or null
minBaseFee: QUANTITY or null
}

minBaseFee MUST be null before the Jovian fork and non-null after it.

As with Holocene’s dynamic EIP-1559 parameters, keeping the minimum base fee in the block header means block sealing never has to reach into state. The function that derives the next block’s base fee from its parent header stays pure while the parameter remains dynamically configurable — handled much like gasLimit, with the derivation pipeline feeding the relevant SystemConfig values to the block builder through PayloadAttributesV3.

Jovian introduces a DA footprint block limit that caps the total estimated compressed transaction data a block may carry. Each transaction now tracks a new resource — its DA footprint — alongside gas usage. The figure is scaled into the gas dimension so its block total can be bounded by the block gas limit, just like total gas usage.

A block’s daFootprint is defined as:

def daFootprint(block: Block) -> int:
daFootprint = 0
for tx in block.transactions:
if tx.type == DEPOSIT_TX_TYPE:
continue
daUsageEstimate = max(
minTransactionSize,
(intercept + fastlzCoef * tx.fastlzSize) // 1e6
)
daFootprint += daUsageEstimate * daFootprintGasScalar
return daFootprint

Here intercept, minTransactionSize, fastlzCoef, and fastlzSize come from the Fjord specs, DEPOSIT_TX_TYPE is 0x7E, and // denotes integer floor division.

From Jovian onward, each block header’s blobGasUsed property is set to that block’s daFootprint. Pre-Jovian (since Ecotone) it was held at 0 because Base has no blob support; the field is now reused to carry the DA footprint.

Block building must guarantee — and header validation must check — that a block’s daFootprint stays under the gasLimit, exactly as it does for gasUsed. This implies a block may hold at most gasLimit/daFootprintGasScalar total estimated DA usage bytes.

In addition, from Jovian the base fee update uses gasMetered := max(gasUsed, blobGasUsed) in place of the previous gasUsed. As a result, blocks with heavy DA usage can push the base fee up in following blocks.

daFootprintGasScalar loads in the same fashion as the operatorFeeScalar and operatorFeeConstant introduced in Isthmus. It can be read two interchangeable ways:

  • from the current L2 block’s deposited L1 attributes (daFootprintGasScalar), decoded per the jovian schema
  • from the L1 Block Info contract (0x4200000000000000000000000000000000000015)
    • through the Solidity getter daFootprintGasScalar
    • by reading storage directly — a big-endian uint16 in slot 8 at offset 12.

Its default value is given in the L1 Attributes section.

Once Jovian is active, transaction receipts gain a new daFootprintGasScalar field carrying its block’s DA footprint gas scalar. The receipt’s blobGasUsed field is also set to the transaction’s DA footprint.

Today’s L1 fee mechanism prices DA usage from an estimate of each transaction’s DA footprint, yet nothing at the protocol level reflects how limited DA throughput on L1 actually is. For example, on Ethereum L1 with Pectra enabled, blob throughput is roughly ~96 kB/s (target ~64 kB/s), yet the calldata floor gas price of 40 for calldata-heavy L2 transactions permits more incompressible data than Ethereum blob space could absorb on most Base chains. This is currently handled at the policy level by batcher-sequencer throttling, which artificially constricts block building — and can drag base fees down, costing chain operators and worsening the user experience (inclusion delays, priority-fee auctions). Imposing a hard cap on a block’s DA footprint that simultaneously feeds into the base fee resolves these shortcomings of the policy-based approach.

Jovian revises the operator fee calculation so larger fees can be charged. From the Jovian activation onward, the operator fee MUST be derived as:

operatorFee = (gas * operatorFeeScalar * 100) + operatorFeeConstant

In effect, the per-gas scalar applied becomes 100 * operatorFeeScalar. Every other data type and the operator-fee semantics from the Isthmus spec carry over unchanged.

Under the new formula the operator fee’s maximum value occupies 103 bits:

operatorFee_max = (uint64_max * uint32_max * 100) + uint64_max ≈ 7.924660923989131 * 10^30

Implementations that use uint256 for intermediate arithmetic need no extra overflow checks.

A few precompiles have revised input-size limits. The new caps are:

  • bn256Pairing: 81,984 bytes (427 pairs)
  • BLS12-381 G1 MSM: 288,960 bytes (1,806 pairs)
  • BLS12-381 G2 MSM: 278,784 bytes (968 pairs)
  • BLS12-381 Pairing: 156,672 bytes (408 pairs)