EIP-8279 Block access list byte floor

Hegota Considered for Inclusion · Fees & gas · EIP status Draft · created 2026-05-23

Counts the bytes added to the block access list (BAL) during execution for each transaction and adds them to the EIP-7623 floor price at 64 gas per byte. The worst-case size of a 60M gas block drops from about 1.55MiB to about 0.89MiB.

Why it is needed

A node must be able to process not the average block but the largest block an attacker can construct. Ethereum has kept the worst-case block size down with EIP-7623, which charges at least 64 gas per byte of calldata, and EIP-7981 and EIP-8131 extended that floor to everything inside a transaction, such as access lists, delegation signatures, and blob hashes. However, the Block Access List (BAL) of EIP-7928 is a new source of bytes that, unlike transaction contents, is filled by instructions during execution, so no such fixed floor reaches it. The cheapest path is the first storage slot read within a transaction (cold SLOAD), which adds 32 bytes of BAL for 2,100 gas. If an attacker fills calldata entirely with non-zero values to raise intrinsic gas above the floor, the calldata is bought at 16 gas per byte and the rest of the block can be filled with SLOAD keys. In a 60M gas block, a pure calldata attack reaches only 0.894MiB, but with EIP-8131 alone the delegation signature bypass grows to 1.067MiB, and under EIP-7928 the cold SLOAD bypass grows to 1.548MiB. SSTORE, address access, CREATE, and successful EIP-7702 delegations are the same hole.

What changes

Today

After EIP-8279

Who is affected

In depth

Constants and per-transaction metering

The rates and units are constants. FLOOR_GAS_PER_BYTE = 64 (EIP-7976), TX_MAX_GAS_LIMIT = 16_777_216 (EIP-7825), BAL_BYTES_PER_ADDRESS = 20, BAL_BYTES_PER_STORAGE_KEY = 32, BAL_BYTES_PER_STORAGE_VALUE = 32, BAL_BYTES_PER_BALANCE = 32, BAL_BYTES_PER_NONCE = 8, DELEGATION_CODE_BYTES = 23. Until the transaction ends, the client keeps 2 counters and 3 sets. bal_data_bytes is the number of bytes execution has added to the BAL. floor_gas_used is the floor accumulator: the fixed seed plus the metered bytes. metered_addresses, metered_storage_keys and metered_storage_values remember the addresses and (storage_address, key) pairs that have already been metered, which prevents double metering. None of them goes into the signed transaction, none is RLP-encoded, propagated or stored, and there is no new transaction field or type. meter_bal_data computes new_floor = static_floor + new_bal_data_bytes × FLOOR_GAS_PER_BYTE and raises OutOfGasError if it exceeds execution_budget = min(tx.gas, TX_MAX_GAS_LIMIT). It does not deduct or subtract gas from the execution gas budget. Metering must happen before the corresponding BAL insertion or state change, and after the pre-state execution gas check required by EIP-7928 has passed. An access that fails the pre-check meters nothing, and an OutOfGasError in the metering step halts before any BAL bytes are created for a value that was never produced. The metered value is a deliberate upper bound on the actual BAL contribution, so overcounting only raises the floor and never lowers it.

Fixed floor seed and static handling of delegation signatures

The floor for the transaction content share, tx_floor, is defined by EIP-8131. This EIP statically adds the BAL share that arises when delegation signatures are processed. auth_bal_bytes = 20 (authority address) + 23 (delegation indicator code) + 8 (authority nonce) = 51, and static_floor = tx_floor + FLOOR_GAS_PER_BYTE × auth_bal_bytes × num_authorizations. At the start of execution, floor_gas_used is set to this value and bal_data_bytes to 0. The handling is static because set_delegation runs outside the EVM's exception handler. If meter_bal_data raised OutOfGasError there, it would propagate uncaught and halt block processing. If the 159 bytes, the 108-byte tuple charged by EIP-8131 plus 51 bytes, are put into the floor up front, set_delegation can change state freely, and a transaction whose budget is too small is already rejected at the validation stage. Some BAL entries are also not placed by any instruction: the sender, coinbase, top-level target address, sender and coinbase balances, sender nonce, the recipient balance of a top-level value transfer, the nonce of a top-level creation target, and the optional top-level EIP-7702 delegation target. Together they come to at most 184 bytes, and the TX_BASE / FLOOR_GAS_PER_BYTE = 328 bytes of slack already present in TX_BASE covers all of them. Transaction validation still requires execution_budget >= max(intrinsic, static_floor).

Metering triggers during execution

Each trigger meters a fixed number of bytes before the corresponding BAL entry is added. An account touched for the first time in a transaction by BALANCE, EXT*, CALL*, a SELFDESTRUCT beneficiary, or a CREATE/CREATE2 deployment address costs 20 bytes, and the first touch of a storage key by SLOAD or SSTORE costs 32 bytes. An SSTORE with current == original and new != original adds +32 bytes only if that (storage_address, key) pair has not yet been metered. A CALL that sends value to a different account costs 64 bytes for the caller and recipient balances, and a SELFDESTRUCT with a nonzero balance that goes to a different beneficiary also costs 64 bytes. CREATE/CREATE2 meters 8 bytes before the creator nonce increment, 8 bytes for the new contract nonce after the collision check, and +64 bytes if there is an endowment, and if the deployment succeeds it meters len(deployed_code) just before set_code. CALLCODE, DELEGATECALL and STATICCALL do not send value out of the executing account, so they have no balance bytes. The first-touch sets are independent of the EVM's warm state. An address or key pre-warmed by an EIP-2930 access list is still metered again, because EIP-7928 puts it in the BAL when execution first touches it. The 32 bytes of storage value are metered at most once per slot per transaction, metered_storage_values is never rewound, and restoring the original value later does not refund anything. storage_address is the account whose storage the current execution context modifies, so under CALLCODE and DELEGATECALL it is not the account whose code is running.

Final charge, budget ceiling, and block size bound

tx.gasUsed = max(execution_gas_used, tx_env.floor_gas_used). The mechanism during execution is a counter, not a reservation. It does not subtract gas from the execution budget or set it aside in advance. It only increases bal_data_bytes and floor_gas_used and checks that the floor is at or below execution_budget. The floor accumulator only decides how much of the prepaid tx.gas × gas_price is kept and how much is refunded. The ceiling is min(tx.gas, TX_MAX_GAS_LIMIT) rather than tx.gas because of EIP-8037. Under it, tx.gas can exceed the EIP-7825 cap, and the excess goes into a state gas reservoir that is used only for state charges. If the floor were allowed up to tx.gas, a deployment transaction with a large reservoir could push the floor past TX_MAX_GAS_LIMIT through code deposit bytes, exceeding the per-transaction execution cap that validation enforces statically and the execution capacity the block has secured. Without EIP-8037, tx.gas does not exceed TX_MAX_GAS_LIMIT, so the budget equals tx.gas. System contract calls (EIP-2935, EIP-4788, EIP-7002, EIP-7251) and withdrawals do not use the per-transaction floor, and EIP-7928's block-level buffer bal_items <= block_gas_limit / ITEM_COST (about 1,428 items at 60M) absorbs them. So per transaction block_bytes_per_tx × FLOOR_GAS_PER_BYTE ≤ tx_env.floor_gas_used ≤ tx.gasUsed holds, and summing over all user transactions gives sum(block_bytes) ≤ sum(tx.gasUsed) / 64 ≤ block_gas_limit / 64.

Why charge on the floor side and use a single rate

Raising execution fees would hurt even users who do not use the workaround. The floor only takes effect when execution is too cheap to cover it, and that is exactly the workaround situation. Adding X per SLOAD to intrinsic gas does not bound a block by gas_limit / 64. The optimal workaround still yields B = gas_limit / 64 + 24 · gas_limit / (2100 + X) bytes, and the residual term vanishes only when X becomes very large. A floor-side charge gives a closed upper bound directly. If every BAL byte is priced at FLOOR_GAS_PER_BYTE, the same as nonzero calldata, then whichever of calldata, delegation signatures, storage keys, addresses, code or balances the attacker picks, the floor gives 1/64 byte per gas, so the attacker's choice does not matter and the optimum converges to B = gas_limit / 64. Runtime extensions can make the floor bind even when execution gas does not, so they are checked against the execution gas budget. The first touch of an account warmed by EIP-2930 is a floor of 1,280 versus execution of 100, a warmed SLOAD is 2,048 versus 100, and a SELFDESTRUCT to a warmed but unmetered beneficiary is 84 × 64 = 5,376 versus 5,000. Deployed code is 64 per byte versus 200, and under EIP-8037 the code deposit execution charge is 6 gas per word, under 0.2 gas per byte, which makes it the trigger with the widest margin over the floor. Even so, the main bulk case is the calldata + SLOAD workaround, where the fixed seed of EIP-8131 is already close to the execution budget.

Meter first, never refund: reverts and repeated writes

EIP-7928 keeps accessed addresses and storage keys in the BAL even if the call frame is reverted. Metering before insertion means every BAL byte is counted without gaps, and an OOG in the metering step halts before insertion. Balance, nonce, code and storage value changes in a reverted frame are discarded, but bal_data_bytes, floor_gas_used and the three sets are not rewound, and the storage value charge is not refunded either. Refunding when a slot returns to its original value would require journaling together with call frame state. This is because if a later write is reverted, the earlier value change reappears in the BAL while the bytes stay refunded. The monotonically increasing metered_storage_values set charges once per slot and keeps the bound without any rollback mechanism. Without the set, each cycle of leaving the original value O and returning would add another 2,048 gas, but EIP-7928 records at most one post-value per slot. As a test case, a round trip that changes O to X and back to O meters key 32 + value 32 = 64 bytes, overcounting the BAL by 32 bytes. Changing O to A, B, C in succession, and going to A, returning to O, then going to B, both meter 64 bytes, matching the single post-value the BAL records. A CALL with value that reverts is overcounted by 64 bytes. A cold SSTORE (zero to nonzero) is 64 bytes, or 4,096 gas, but the execution charge of 22,100 is larger, so execution dominates. There is overcounting but no undercounting, so the upper bound holds.

Compatibility, mainnet sample, and gas estimation

This is a hard fork change. The combined floor per delegation signature is (108 + 51) × 64 = 10,176 gas, still lower than the intrinsic gas of PER_EMPTY_ACCOUNT_COST = 25,000 per signature. Static transaction validation differs only when this combined fixed floor dominates. During execution, BAL extensions can raise gasUsed, or raise OutOfGasError if the new floor exceeds the execution budget, and that includes first touches of pre-warmed entries. A simple ETH transfer stays at 21,000. In a May 2026 mainnet sample (1,500 random blocks, 441,271 transactions, 20,000 of them with BAL byte tracing), EIP-8131 alone made 3.24% of transactions pay more, with gas up +3.29%. EIP-8279 adds 0.54 percentage points and +0.77 percentage points to that, for a total of 3.79% / +4.06%. In the sample, the BAL share was never the sole binding constraint. After the TX_BASE absorption, BAL bytes per transaction average 120 to 200 bytes, under 13k in floor gas, which is far below typical execution gas. The main role of this EIP is to pair with EIP-8131 to block the calldata + SLOAD workaround, not to add a widely binding standalone floor. Wallets and eth_estimateGas compute the new fixed floor including the per-signature term, and when tracing execution they must count the BAL bytes of first touches, value-transfer calls and deployments together with intrinsic gas. If they miss this, the estimate for a transaction caught by the floor comes out low, and it can pass static validation and still halt with OutOfGasError during execution.

Status and core developer call decisions

The source status is Draft. The constants, metering triggers and sample figures can all still change. In the core developer calls, at ACDE #246 on 09.24, this BAL byte floor EIP was raised to Considered for Inclusion (CFI) in Hegotá together with EIP-8131. It was a session where the highest-rated candidates all moved to the next stage at once. It was also discussed at ACDT #100 on 10.12, but no decision was made. The source also says the two EIPs need each other. EIP-8131 does not price BAL bytes, and EIP-8279 reuses the floor of EIP-8131, so neither alone closes the workaround.

Original EIP · ← EIPs