This EIP precisely caps state growth while at the same time facilitating between 50%-300% more non-state operations per block compared to when solely raising the state gas cost. The cap is achieved by tracking state creation and having a dedicated EIP-4844 style fee market mechanism set the cost, to target 250 MiB per day. To preserve the existing transaction format and EVM execution patterns, state is still priced in gas during transaction processing. The state creation gas cost is updated every block, adjusting slowly so that users can set reasonable gas limits. The byte-level harmonization of state creation operations provided by EIP-8037 is then still applied. To facilitate scaling, state creation and regular gas have separate limits, but a normalized aggregate gas is used when calculating the base fee.
Ethereum is currently focused on scaling the layer 1, with a rapid expansion of the block gas limit foreseen in the near-term from compute and memory optimization, as well as via headliner proposals EIP-7732 and EIP-7928. Unfortunately, if the gas cost for state creation is kept fixed, state would likely expand in line with an expanding gas limit. As client database sizes increase, degradation in performance is expected. EIP-8037 addresses state growth by harmonizing current state creation costs according to how many bytes they add to the state. It also increases the gas cost of state creation significantly, by between 3x and 9.5x per operation.
It can be assumed that increased gas costs will somewhat keep state from expanding in line with the increased gas limit. However, since the price-elasticity of demand for state creation is unknown, it is impossible to say exactly what the effect would be. Users may not be particularly sensitive to the increased state gas costs and still continue to purchase state relative to other resources similar to present usage patterns, given that the overall base fee may also fall from the increased gas limit. This would have two effects:
To confidently target some specific state growth, it is necessary to dynamically vary the price of state creation according to its usage. The EIP-4844 mechanism is then particularly suitable. This proposal tracks state_bytes_created and state_bytes_cleared each block and keep a running counter of excess_state_bytes. When state_bytes_created - state_bytes_cleared exceeds the target, excess_state_bytes increases, resulting in an increase to the gas charged per state byte. Users should not need to attach excessive margins to the transaction gas limit without risking transactions failure, and the gas charged per state byte is thus set to vary very moderately between blocks, at 0.1% at the very most.
To not impede scaling when a higher gas is charged per state byte, it is necessary to exempt state gas from the block gas limit. This is achieved by tracking the regular_gas and state_gas separately, having a separate limit for each. To ensure that the base fee still prices usage across all resources, the base fee update still operates on a normalized aggregate of both. Thus, the smooth shift in the gas charged per state byte can be understood as a separate control mechanism for pricing state relative to all other operations, facilitating good load-balancing over time. It is however not a mechanism that prices state creation in isolation—the base fee still applies to all resources.
The full specification is available here.
Four constants have a similar role as in EIP-4844.
| Constant | Value |
|---|---|
STATE_GAS_UPDATE_FRACTION |
36_418_000 |
TARGET_STATE_BYTES_PER_BLOCK |
36_400 |
MAX_STATE_BYTES_PER_BLOCK |
2 * TARGET_STATE_BYTES_PER_BLOCK |
MIN_STATE_GAS_PER_BYTE |
380 |
The STATE_GAS_UPDATE_FRACTION is chosen so that a block that uses MAX_STATE_BYTES_PER_BLOCK can increase state_gas_per_byte by at most 0.1%. It was computed as (MAX_STATE_BYTES_PER_BLOCK - TARGET_STATE_BYTES_PER_BLOCK) / ln(1.001), rounded to the nearest thousand.
The number of bytes assigned to each state operation follows EIP-8037.
| Constant | Value |
|---|---|
CREATE_BYTES |
112 |
CODE_DEPOSIT_BYTES |
1 |
NEW_ACCOUNT_BYTES |
112 |
STORAGE_SET_BYTES |
32 |
PER_EMPTY_ACCOUNT_BYTES |
112 |
PER_AUTH_BASE_BYTES |
23 |
The current header encoding is extended with three new 64-bit unsigned integer fields.
state_bytes_created is the number of state bytes that were created in the current block.state_bytes_cleared is the number of state bytes that were cleared in the current block.excess_state_bytes is a running total of state bytes created in excess of the target, prior to the block. Blocks with above-target state bytes creation increase this value, blocks with below-target state bytes creation decrease it (bounded at 0).The header sequence of the new fields is [..., TBD, state_bytes_created, state_bytes_cleared, excess_state_bytes].
We also rename the gas_used field to regular_gas_used:
regular_gas_used is the regular gas used in the block, ignoring gas spent on state creation.During processing, the protocol tracks regular_gas and state_gas separately, and returns the aggregate gas_used accounting for refunds, similar to today. Furthermore, regular_gas_used (without refunds to satisfy EIP-7778) is returned to count against the block limit. Finally, the protocol also tracks state_bytes_created and state_bytes_cleared, allowing for a more accurate tracking of the excess_state_bytes. These are returned in the list state_bytes(created, cleared).
The protocol keeps track of excess_state_bytes, and computes the state_gas_per_byte from that variable (referred to as cost_per_state_byte in EIP-8037). The same update mechanism as in EIP-4844 is applied.
def get_state_gas_per_byte(header: Header) -> int:
return fake_exponential(
MIN_STATE_GAS_PER_BYTE,
header.excess_state_bytes,
STATE_GAS_UPDATE_FRACTION
)
The STATE_GAS_UPDATE_FRACTION is set so that the maximum increase in state_gas_per_byte is 0.1%, when a block consumes MAX_STATE_BYTES_PER_BLOCK. The state_gas_per_byte has a floor of MIN_STATE_GAS_PER_BYTE, corresponding to EIP-8037 when scaled down to a 60M block gas limit. The excess_state_bytes are updated as in EIP-4844, but accounting for both created and cleared state bytes:
def calc_excess_state_bytes(parent: Header) -> int:
excess = parent.excess_state_bytes + parent.state_bytes_created
deficit = TARGET_STATE_BYTES_PER_BLOCK + parent.state_bytes_cleared
if excess < deficit:
return 0
else:
return excess - deficit
The block level gas cost for each state operation is computed as GAS_STATE_OPERATION = STATE_OPERATION_BYTES * state_gas_per_byte. For completeness, the function below specifies this for all operations:
def update_state_op_costs(state_gas_per_byte: int) -> None:
GAS_CREATE = state_gas_per_byte * CREATE_BYTES
GAS_CODE_DEPOSIT = state_gas_per_byte * CODE_DEPOSIT_BYTES
GAS_NEW_ACCOUNT = state_gas_per_byte * NEW_ACCOUNT_BYTES
GAS_STORAGE_SET = state_gas_per_byte * STORAGE_SET_BYTES
PER_EMPTY_ACCOUNT_COST = state_gas_per_byte * PER_EMPTY_ACCOUNT_BYTES
PER_AUTH_BASE_COST = state_gas_per_byte * PER_AUTH_BASE_BYTES
The base fee is updated by aggregating the regular gas and "normalized state gas", where a net change of MAX_STATE_BYTES_PER_BLOCK corresponds to the block's gas limit, thus giving state gas the same range as regular gas. The change is still restricted to the range ±12.5%. When not conditioning against negative values, the gas_used_delta used in the base fee update can for the EIP-1559 mechanism thus be computed as:
normalized_state_gas_used = parent.gas_limit * (parent.state_bytes_created - parent.state_bytes_cleared) // MAX_STATE_BYTES_PER_BLOCK
gas_used_delta = (parent.regular_gas_used + normalized_state_gas_used - parent.gas_limit) // 2
The transaction's gas_used is computed as the sum of regular_gas and state_gas and includes refunds, similar to how gas_used is applied today. The regular_gas_used is returned without refunds, which the EIP-1559 cumulative gas at the block level accounts for. We also keep track of the cumulative state_bytes_created and state_bytes_cleared in the vector state_bytes.
def validate_block(block: Block) -> None:
...
# Check that the excess state bytes was updated correctly
assert block.header.excess_state_bytes == calc_excess_state_bytes(block.parent.header)
# Compute the per-block state_gas_per_byte and update state-op gas costs
state_gas_per_byte = get_state_gas_per_byte(block.header)
update_state_op_costs(state_gas_per_byte)
state_bytes_created = 0
state_bytes_cleared = 0
for tx in block.transactions:
...
# Transaction execution returns a gas_used vector and refund_vector
gas_used, regular_gas_used, state_bytes = self.execute_transaction(transaction, effective_gas_price)
# The gas refund stays the same, the transaction gas counter only counts regular gas without refunds
gas_refund = transaction.gas_limit - gas_used
cumulative_transaction_gas_used += regular_gas_used
# Update the total state bytes created and cleared in the block
state_bytes_created += state_bytes[0]
state_bytes_cleared += state_bytes[1]
...
# Ensure the net state bytes created are within the per-block limit
assert state_bytes_created <= MAX_STATE_BYTES_PER_BLOCK + state_bytes_cleared
# Ensure state_bytes_created and state_bytes_cleared matches header
assert state_bytes_created == block.header.state_bytes_created
assert state_bytes_cleared == block.header.state_bytes_cleared
...
EIP-8037 sets a fixed price of 1900 gas per byte, which can lead to a range of outcomes, depending on, e.g., the price-elasticity of demand for state creation. A specific concern is that since EIP-8037 counts state gas against the regular block gas limit, it may impede scaling, when users consume relatively more state gas at the higher gas per byte charged after the change. EIP-8037 assumes that users will spend 30% of all gas on state gas, just as today, even if the state gas per state byte is increased close to tenfold. This assumption may not hold.
Ideally, an increase in the gas cost for state creation will lead users and developers to reduce usage of these operations, relative to other operations. As an example, some may opt to use an existing ETH address instead of creating a new one for each CEX withdrawal. However, given that state creation is a rather fundamental part of interacting with a blockchain, it is reasonable to suspect that many other usage patterns remain as today. Furthermore, an increase in the gas limit as Ethereum scales is associated with a general drop in the base fee. Thus, even if state creation becomes relatively more expensive than other operations, the reduced base fee may mean that users do not take notice to the extent that is desired.
Assume that 30% of all consumed gas is state gas at 60M gas limit, producing 102 GiB in state growth per year (286 MiB/day). Then scale the L1 by $5x$ up to a 300M gas limit and increase the gas cost for state creation by $8.5x$ (using account creation in EIP-8037 as a reference point), while usage patterns do not change. The equilibrium outcome is then that $100 \times 0.3 \times 8.5 / (0.3 \times 8.5 + 0.7) = 78.5\%$ of all gas is spent on state. Even though Ethereum scaled the gas limit by $5x$, the real achieved scaling is only $5 \times (1 - 0.785) / 0.7 = 1.54x$, and state growth is 157 GiB per year (440 MiB/day).
The first example assumes no change in usage behavior and illustrates a general effect that might take place, but somewhat less pronounced. Assume instead that usage patterns indeed change, and that users create half as many state bytes for every gas they spend on other operations, in comparison to how they interact with Ethereum today. In this case, the equilibrium outcome is that $100 \times (0.3 \times 8.5 \times 0.5) / (0.3 \times 8.5 \times 0.5 + 0.7) = 64.7\%$ of all gas is spent on state. When scaling the gas limit by $5x$, the real achieved scaling is $5 \times (1 - 0.647) / 0.7 = 2.52x$, and state growth is 129 GiB per year (363 MiB/day).
Exempting state from the gas limit applied to regular gas—as proposed in this EIP—would produce a $7.14x$ in scaling (for non-state operations). We would thus achieve close to three times as much throughput as achieved in the example where users halve their state purchases relative to other operations (because they would still spend 64.7% of all available gas on state creation).
Alternative solutions would ideally adhere to the principles outlined in this EIP, specifically:
One variant is to expand the transaction format, using a separate regular_gas_limit and state_byte_limit while removing the current gas_limit. At the beginning of processing a transaction, the protocol computes:
gas_limit = regular_gas_limit + state_gas_per_byte * state_byte_limit
All further processing is applied as if the transaction had specified the computed gas_limit in the first place. This ensures that users do not need to risk having a transaction fail if the state_gas_per_byte drifts significantly between submission and execution, if they set a tight gas_limit. They simply specify how much regular gas they will use and how many state-bytes they will add, which then implies a certain gas_limit at execution time. The idea has certain similarities with the "aggregated gas" concept explored in EIP-7999, but the separate fee here influences how much gas that a resource consumes, hence the name "Multidimensional subfee market". A benefit of the approach is that it offers perfect control over state growth while there is no fee-drift that can make a transaction fail. A downside is that the transaction format must be updated. It would be possible to move to a multidimensional subfee market at a later stage, after first implementing this proposal.
It is possible to try to achieve the stated goals with multidimensional gas metering of EIP-8011. The difference between the proposal and the EIP-8011 mechanism is that the latter would not have a separate price for state. It uses the max of the gas consumption among resources for the base fee update. The base fee update may thus for example be set either from the regular_gas or the state_gas, depending on which that is used the most in the block. This means that there is less control over state growth due to the interaction between resources. When another resource consumes the most gas, it will also set the price for state. When state is the most consumed resource, it might raise the price for other resources to a level where scaling is impeded. These types of interactions can be optimized by careful tuning of limits, targets and gas costs, although whatever setting that is decided on may never be fully satisfactory. The benefit is that the gas cost set for state can be fixed, while state still is "exempted" from the regular gas limit, since it only influences the base fee in parallel via the max operation. Thus scaling is mostly preserved.
Finally, it is possible to use the max of the gas consumption among resources for the base fee update, while still retaining the separate EIP-4844 mechanism for the state_gas_per_byte. This just represents another way to update the base fee, instead of the aggregation proposed here.
The multidimensional fee market with aggregate gas specified in EIP-7999 could be applied without changing the transaction format. The gas cost for state creation is fixed and users set a single gas_limit. The fee for the gas for state creation is however completely separate in fee_per_state_gas, instead of being folded into the gas cost through conversion. Users set a max_fee_per_gas as today and the total fee they are willing to pay is computed as max_fee = max_fee_per_gas * gas_limit. This fee is compared with the fee determined during execution: fee_per_state_gas * state_gas + base_fee_per_gas * regular_gas. The benefit is that the gas cost cannot drift between transaction submission and execution, while the transaction format still stays the same. The downside is that if a users sets a tight max_fee_per_gas, then the builder cannot be sure before execution whether the fee covers the cost of the transaction. The user would need to set a more permissive max_fee_per_gas that covers the higher of base_fee_per_gas and fee_per_state_gas to provide full guarantees.
This EIP works well with a broader adoption of full multidimensional gas metering, as proposed in EIP-8011, if we at the same time also adopt the EIP-8011 mechanism for updating the base fee, as suggested previously. We would then expand the gas_used_per_resource vector to track also the other resources in separation. State would still be treated differently in that it has its own state_gas_per_byte cost derived from the excess_state_bytes, whereas other resources are priced directly via the base fee. The metering approach of EIP-8011 is then applied to set the base fee. Given that state is increasingly becoming the major resource contraint (and thus pricing constraint) as we try to limit its usage, it is particularly beneficial that this EIP would allow the EIP-8011 metering to only be applied to the remaining resources. This is likely to benefit scaling further. Note further that the special treatment of the code_deposit_gas proposed in EIP-8037 would not be necessary with this proposal, because the constraint on state bytes per block is more moderate.
The gas consumed by state creation operations will slowly change so that state growth is maintained at a desirable level. The speed of that drift is tuned by adjusting the STATE_GAS_UPDATE_FRACTION. This constant must be set to a level where UX concerns are mitigated. Specifically, the main concern is that if a transaction sets a very tight gas_limit while consuming state bytes, then that gas_limit may be exceeded, if the state_gas_per_byte cost derived from the excess_state_bytes shifts between submission and execution. The transaction would thus fail. Wallets must first fetch the excess_state_bytes and compute state_gas_per_byte, just as for the blob_base_fee (and base_fee) today. They know how many state_bytes that are created from each op-code of the transaction via the constants inherited from EIP-8037, e.g., NEW_ACCOUNT_BYTES = 112. They then simply add a margin m when computing the gas_limit set for the transaction: gas_limit = regular_gas_limit + state_bytes * state_gas_per_byte * m. The margin is tuned to the variation in state_gas_per_byte that is expected before execution, at the upper limit. The wallet may of course add some margin to their estimate for the regular_gas_limit of other op-codes as well when computing the gas_limit. The difference to the alternative "Multidimensional subfee market" outlined previously is thus that the wallet does not send a regular_gas_limit and state_bytes_limit separately, and instead combine them in order to preserve the current transaction format.
Wallets must have a margin when they set the gas_limit so that the transaction does not fail.
Copyright and related rights waived via CC0.