Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 79 additions & 34 deletions EIPS/eip-8038.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ Upon activation of this EIP, the following parameters of the gas model are intro
| `ACCOUNT_WRITE` | Surcharge for when writing to an account changes one account leaf value for the first time | 6,700¹ | 8,000 | +19% | `*CALL` opcodes, `SELFDESTRUCT`, EOA delegations and ETH transfers |
| `COLD_STORAGE_ACCESS` | Cold touch of a storage slot | 2,100 | 3,000 | +43% | `SSTORE` and `SLOAD` |
| `STORAGE_WRITE` | Surcharge for when writing to a storage slot changes its value for the first time | 2,800² | 10,000 | +257% | `SSTORE` |
| `WARM_ACCESS` | Touch of an already-warm account or storage slot | 100 | 100 | +0% | `SSTORE`, `SLOAD`, `*CALL` opcodes, `BALANCE` and `EXT*` opcodes |
| `STORAGE_CLEAR_REFUND` | Refund for clearing a storage slot | 4,800 | 12,480 | +160% | `SSTORE` |
| `LOW_WARM_ACCESS` | Warm access cost for read-only opcodes | - | 5 | - | `SLOAD`, `BALANCE`, and `EXT*` opcodes |
| `WARM_ACCESS` | Warm access cost for opcodes that must account for `REVERT` execution paths | 100 | 100 | +0% | `SSTORE`, `*CALL` opcodes, and `CREATE*` opcodes |
| `CREATE_ACCESS` | State access costs for contract deployment (include account cold access and account write) | 7,000³ | 11,000 | +57% | `CREATE`/ `CREATE2` |
| `ACCESS_LIST_STORAGE_KEY_COST` | Gas charged per storage key included in a transaction's access list | 1,900 | 3,000 | +58% | `SSTORE` and `SLOAD` |
| `ACCESS_LIST_ADDRESS_COST` | Gas charged per address included in a transaction's access list | 2,400 | 3,000 | +25% | `*CALL` opcodes, `BALANCE`, `SELFDESTRUCT` and `EXT*` opcodes |
Expand All @@ -45,40 +45,64 @@ Upon activation of this EIP, the following parameters of the gas model are intro

³: 7,000 = `GAS_CREATE` (32,000) - `GAS_NEW_ACCOUNT` (25,000)

### `SSTORE` pricing
### Introduction of `state_access_gas_reservoir`

A new counter, `state_access_gas_reservoir`, is introduced to replace the refund counter.

This counter is initialized to zero at the beginning of each transaction.

Its operation is analogous to the `state_gas_reservoir` defined in [EIP-8037](./eip-8037.md): it accumulates all gas refills generated by eligible operations, and all state-access charges (excluding the opcode base cost itself) MUST first consume gas from this reservoir before consuming gas from `gas_left`.

### Introduction of `balance_updated` and `account_updated`

Two new global sets, `balance_updated` and `account_updated`, are introduced to track accounts whose state has been modified during transaction execution.

- `balance_updated` records accounts whose balance has changed. Membership in this set is temporary and may be reverted by subsequent operations.

- `account_updated` records accounts that have undergone permanent account-level modifications, such as account creation or changes to account state-root-related fields.

These sets operate alongside existing global sets such as `accessed_addresses`.

The rules governing insertion into and removal from these sets are defined by the relevant opcodes below.

### `SLOAD` Pricing

The base cost of the `SLOAD` opcode is set to `LOW_WARM_ACCESS`.

If the accessed storage slot is cold, an additional `COLD_STORAGE_ACCESS` charge is applied.

### `BALANCE` and `EXTCODEHASH` pricing

In addition, the `SSTORE` cost formula is updated to include the following independent costs:
The base cost of `BALANCE` and `EXTCODEHASH` is set to `LOW_WARM_ACCESS`.

1. Access costs: `COLD_STORAGE_ACCESS` or `WARM_ACCESS`, depending on whether the storage slot is cold or warm.
2. Write cost: additionally charge `STORAGE_WRITE` if the new value is different from the present value and the present value equals the original value (First time change).
3. State creation cost: additionally charge `GAS_STORAGE_SET` (per [EIP-8037](./eip-8037.md)) if the original value is zero, the current value is zero, and the new value is non-zero.
If the accessed account is cold, an additional `COLD_ACCOUNT_ACCESS` charge is applied.

Refunds are also updated as follows. Each rule is an adjustment to the transaction's refund counter and is evaluated on every `SSTORE`:
### `EXTCODESIZE` and `EXTCODECOPY` pricing

1. `STORAGE_CLEAR_REFUND` is added if the original value is non-zero, the current value is non-zero and the new value is zero (a slot is cleared).
2. `STORAGE_CLEAR_REFUND` is subtracted if the original value is non-zero, the current value is zero and the new value is non-zero (a slot that was cleared earlier in the same transaction is restored). This reverses the refund granted by rule 1, so that clearing and then restoring a slot within the same transaction is never net-profitable.
3. `STORAGE_WRITE` is refunded if the new value equals the original value and differs from the current value (a first-time change made earlier in the same transaction is reset back to its original value).
The base cost of `EXTCODESIZE` and `EXTCODECOPY` is set to 2 * `LOW_WARM_ACCESS`.

Rules 2 and 3 mirror the refund accounting of [EIP-2200](./eip-2200.md) and [EIP-3529](./eip-3529.md) for slots that are modified more than once within a transaction: a `STORAGE_WRITE` charge or `STORAGE_CLEAR_REFUND` granted on an earlier `SSTORE` to the same slot is undone when a later `SSTORE` returns the slot to its original value. Without these reversals, a round trip such as `x → 0 → x` would yield a net refund even though the slot ends the transaction unchanged.
If the accessed account is cold, an additional `COLD_ACCOUNT_ACCESS` charge is applied.

These refunds go to the transaction's refund counter and are capped to 20% of the total gas used by the transaction, as per the current refund mechanism.
### `SSTORE` pricing

The base cost of the `SSTORE` opcode is set to `WARM_ACCESS`.

If the accessed storage slot is cold, an additional `COLD_STORAGE_ACCESS` charge is applied.

Per [EIP-7778](./eip-7778.md), the refund counter has been removed, which also eliminates refunds for clearing storage slots.

The rules for state creation cost charges and refills are addressed in [EIP-8037](./eip-8037.md).
Transaction-internal refunds are instead redirected to `state_access_gas_reservoir` for state-access costs and to `state_gas_reservoir` for state-creation costs.

Cases and their corresponding costs are detailed in the table below:

| Original value | Current value | New value | Access status | Description | Regular-gas charges/refunds | State-gas charges/refills |
| Original value | Current value | New value | Access status | Description | Regular-gas charges/State-access-gas refills | State-gas charges/refills |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| 0 | 0 | x | Cold | New slot | `COLD_STORAGE_ACCESS` + `STORAGE_WRITE` charged | `GAS_STORAGE_SET` charged |
| 0 | 0 | x | Cold | New slot | `WARM_ACCESS` + `COLD_STORAGE_ACCESS` + `STORAGE_WRITE` charged | `GAS_STORAGE_SET` charged |
| 0 | 0 | x | Warm | New slot | `WARM_ACCESS` + `STORAGE_WRITE` charged | `GAS_STORAGE_SET` charged |
| x | x | y | Cold | Existing slot, first change | `COLD_STORAGE_ACCESS` + `STORAGE_WRITE` charged | no state-gas adjustments |
| x | x | y | Cold | Existing slot, first change | `WARM_ACCESS` + `COLD_STORAGE_ACCESS` + `STORAGE_WRITE` charged | no state-gas adjustments |
| x | x | y | Warm | Existing slot, first change | `WARM_ACCESS` + `STORAGE_WRITE` charged | no state-gas adjustments |
| x | x | 0 | Cold | Cleared slot, non-zero at transaction start | `COLD_STORAGE_ACCESS` + `STORAGE_WRITE` charged, `STORAGE_CLEAR_REFUND` refunded | no state-gas adjustments |
| x | x | 0 | Warm | Cleared slot, non-zero at transaction start | `WARM_ACCESS` + `STORAGE_WRITE` charged, `STORAGE_CLEAR_REFUND` refunded | no state-gas adjustments |
| 0 | x | 0 | Warm | Set slot reset to zero (zero at transaction start) | `WARM_ACCESS` charged, `STORAGE_WRITE` refunded | `GAS_STORAGE_SET` refilled |
| x | y | x | Warm | Previously-modified slot reset to original value | `WARM_ACCESS` charged, `STORAGE_WRITE` refunded | no state-gas adjustments |
| x | 0 | x | Warm | Cleared slot restored to original value | `WARM_ACCESS` charged, `STORAGE_WRITE` refunded, `STORAGE_CLEAR_REFUND` reversed | no state-gas adjustments |
| x | 0 | y | Warm | Cleared slot written to a new non-zero value | `WARM_ACCESS` charged, `STORAGE_CLEAR_REFUND` reversed | no state-gas adjustments |
| 0 | x | 0 | Warm | Set slot reset to zero (zero at transaction start) | `WARM_ACCESS` charged, `STORAGE_WRITE` refilled | `GAS_STORAGE_SET` refilled |
| x | y | x | Warm | Previously-modified slot reset to original value | `WARM_ACCESS` charged, `STORAGE_WRITE` refilled | no state-gas adjustments |
| x | y | z | Warm | Previously-modified slot written to again | `WARM_ACCESS` charged | no state-gas adjustments |

### Account access pricing
Expand All @@ -87,34 +111,55 @@ The rules that determine when `COLD_ACCOUNT_ACCESS` and `WARM_ACCESS` are charge

#### `CALL`/`CALLCODE`

For `CALL` and `CALLCODE` operations, `CALL_VALUE` is set to `ACCOUNT_WRITE + CALL_STIPEND`, where `CALL_STIPEND` = 2,300.
The base cost of the `*CALL` family is set to `WARM_ACCESS`.

If the target account is cold, an additional `COLD_ACCOUNT_ACCESS` charge is applied.

During transaction execution, each account maintains the following values:

- `original_balance`: the account balance at the beginning of the transaction. This value remains constant throughout the transaction.
- `current_balance`: the current account balance.
- `new_balance`: the balance after the pending update.

For `CALL` and `CALLCODE` operations, `CALL_VALUE` is set to `ACCOUNT_WRITE + CALL_STIPEND`, where `CALL_STIPEND` = 2,300. This represents the maximum state-access charge for a value-transferring call. Upon entering the call frame, gas accounting is performed according to the table below:

| `original_balance` | `current_balance` | `new_balance` | `account_updated` | `balance_updated` update | Description | Regular-gas charges/ State-access-gas refills |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| x | x | y | false | added | First temporary update | no adjustments |
| x | x | y | true | unchanged | First permanent update | `ACCOUNT_WRITE` refilled |
| x | y | x | false | removed | Temporary reset | 2 * `ACCOUNT_WRITE` refilled |
| x | y | x | true | unchanged | Permanent reset | `ACCOUNT_WRITE` refilled |
| x | y | z | false | unchanged | Subsequent temporary update | `ACCOUNT_WRITE` refilled |
| x | y | z | true | unchanged | Subsequent permanent update | `ACCOUNT_WRITE` refilled |

#### `CREATE`/`CREATE2`

`CREATE` and `CREATE2` operations don't have explicit warm/cold pricing. Instead, they are charged `CREATE_ACCESS` in regular-gas and `GAS_NEW_ACCOUNT` in state-gas (per [EIP-8037](./eip-8037.md)). `CREATE_ACCESS` is defined as `ACCOUNT_WRITE` + `COLD_STORAGE_ACCESS`.
The base cost of the `CREATE*` family is set to `WARM_ACCESS`.

This charge is applied uniformly to operations that require `REVERT`-path handling.

`CREATE` and `CREATE2` operations don't have explicit warm/cold pricing. Instead, they are charged `CREATE_ACCESS` in regular-gas and `GAS_NEW_ACCOUNT` in state-gas (per [EIP-8037](./eip-8037.md)). `CREATE_ACCESS` is defined as `ACCOUNT_WRITE` + `COLD_STORAGE_ACCESS`. This represents the maximum state-access charge associated with account accessing.

Note: this definition does not exactly match the legacy decomposition shown in footnote ³ of the Parameters table (`GAS_CREATE` - `GAS_NEW_ACCOUNT` = 7,000). The pre-existing accounting in the protocol is not internally consistent here, and this EIP keeps the discrepancy rather than attempting to reconcile it.

When entering a creation frame, refund accounting is performed if the target account was previously updated before becoming the destination of the creation operation (for example, an account that received ETH before contract creation). The account MUST be permanently inserted into `account_updated`. If the account exists in `balance_updated`, `ACCOUNT_WRITE` MUST be refilled into `state_access_gas_reservoir`, and the account MUST be removed from `balance_updated`.

#### `SELFDESTRUCT`

For `SELFDESTRUCT`, an additional charge of `ACCOUNT_WRITE` is added if a positive balance is sent to an empty account.

#### `EXT*` family update

Besides the current access costs, `EXTCODESIZE` and `EXTCODECOPY` are charged an additional `WARM_ACCESS`.

#### Overview

The cases for account-related operations and their corresponding costs are summarized in the table below:

| Operation | Access charge | `ACCOUNT_WRITE` charged | Additional charge |
| :--- | :---: | :--- | :--- |
| `CALL` / `CALLCODE` | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` | when value is transferred (via `CALL_VALUE` = `ACCOUNT_WRITE` + `CALL_STIPEND`) | — |
| `DELEGATECALL` / `STATICCALL` | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` | never (no value transfer) | — |
| `CREATE` / `CREATE2` | `CREATE_ACCESS` (= `ACCOUNT_WRITE` + `COLD_STORAGE_ACCESS`) | always (folded into `CREATE_ACCESS`) | `GAS_NEW_ACCOUNT` in state-gas (per [EIP-8037](./eip-8037.md)) |
| `CALL` / `CALLCODE` | `WARM_ACCESS`, surcharge `COLD_ACCOUNT_ACCESS` | when value is transferred (via `CALL_VALUE` = `ACCOUNT_WRITE` + `CALL_STIPEND`) | — |
| `DELEGATECALL` / `STATICCALL` | `WARM_ACCESS`, surcharge `COLD_ACCOUNT_ACCESS` | never (no value transfer) | — |
| `CREATE` / `CREATE2` | `WARM_ACCESS` + `CREATE_ACCESS` (= `WARM_ACCESS` + `ACCOUNT_WRITE` + `COLD_STORAGE_ACCESS`) | always (folded into `CREATE_ACCESS`) | `GAS_NEW_ACCOUNT` in state-gas (per [EIP-8037](./eip-8037.md)) |
| `SELFDESTRUCT` | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` | when a positive balance is sent to an empty account | — |
| `BALANCE` / `EXTCODEHASH` | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` | never | — |
| `EXTCODESIZE` / `EXTCODECOPY` | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` | never | `WARM_ACCESS` (second database read) |
| `BALANCE` / `EXTCODEHASH` | `LOW_WARM_ACCESS`, surcharge `COLD_ACCOUNT_ACCESS` | never | — |
| `EXTCODESIZE` / `EXTCODECOPY` | `LOW_WARM_ACCESS`, surcharge `COLD_ACCOUNT_ACCESS` | never | `LOW_WARM_ACCESS` (second database read) |
| EOA delegation ([EIP-7702](./eip-7702.md)) | `COLD_ACCOUNT_ACCESS` or `WARM_ACCESS` per authority | per authorization (intrinsic cost) | see authorizations table below |

### [EIP-7702](./eip-7702.md) authorizations pricing
Expand Down
Loading