EIP-2929 Gas cost increase for state access opcodes (Gas cost increases for state access opcodes)
Final · Fees & gas · EIP status Final · created 2020-09-01
Raises the gas cost of the first read of an account or storage slot in a transaction by about 3x, while a repeated read within the same transaction costs only 100 gas. This rule was already included and applied in the 2021 Berlin hard fork.
Why it is needed
On Ethereum, gas cost is meant to represent the time it takes to process an operation, converted into money. However, instructions that read data from disk, such as SLOAD for reading storage or instructions that look up another account's balance and code, were priced far below their actual cost. During the 2016 Shanghai DoS attacks, an attacker exploited this gap by sending transactions that touched huge numbers of accounts, slowing down the network. Gas costs were raised afterward, but not enough. Research found that a single maliciously crafted transaction could take 20 to 80 seconds to process, while an average transaction finishes in milliseconds. This proposal raises the cost of these instructions by about 3x, cutting worst case processing time from 7 seconds down to around 27 seconds. At the same time, re-reading something already read once is priced very cheaply, so honestly written contracts are not penalized.
What changes
Today
- Storage reads (SLOAD) cost 800 gas and lookups of other accounts cost 700 gas, both cheaper than the actual cost of a disk read
- Reading the same account or storage slot multiple times within a transaction costs the same amount every time
- A single malicious transaction touching many accounts can delay block processing by tens of seconds
After EIP-2929
- The first storage read in a transaction costs 2100 gas, and the first access to an account costs 2600 gas
- An account or storage slot already accessed within the same transaction costs only 100 gas
- Worst case block processing time drops to roughly 7 to 27 seconds
Who is affected
- Users: Based on an analysis of the Goerli testnet, average gas cost increases by about 2.36%. Transactions that repeatedly read the same storage slot can actually become more than 10% cheaper.
- Contract developer: Contracts that pass a fixed amount of gas to subcalls, or that rely on the 2300 gas stipend provided by default during ETH transfers, may break.
- Client teams: An implementation must maintain a set that records which addresses and storage keys were accessed per transaction, and that set must also be rolled back on revert.
- Stateless client research: The theoretical maximum size of state witness data drops from about 14.3MB to about 3.85MB, laying the groundwork for stateless Ethereum.
In depth
Constants and activation point
FORK_BLOCK is 12244000, and the rule applies at or above this block number. The constants are COLD_SLOAD_COST = 2100, COLD_ACCOUNT_ACCESS_COST = 2600, and WARM_STORAGE_READ_COST = 100. The targeted instructions are SLOAD (0x54), the CALL family (0xF1, 0xF2, 0xF4, 0xFA), BALANCE (0x31), and the EXT* family (0x3B, 0x3C, 0x3F). Precompiles, and addresses/slots already accessed within the same transaction, are excluded from the price increase.
The accessed_addresses and accessed_storage_keys sets
During transaction execution, accessed_addresses: Set[Address] and accessed_storage_keys: Set[Tuple[Address, Bytes32]] are maintained. These sets are scoped to the entire transaction context and are implemented like the self-destruct-list or the global refund counter. If a scope is reverted, the set reverts to the state it was in before entering that scope. At the start of execution, accessed_storage_keys is empty, and accessed_addresses contains tx.sender, tx.to (or the address to be created, for a contract creation transaction), and all precompiles. When CREATE/CREATE2 is called, the address to be created is immediately added to the set before checking whether the address is empty. Even if the subsequent initcode execution fails or runs out of gas for code storage, that address itself remains in the set. However, other entries added within the inner scope are reverted. The gas cost of CREATE/CREATE2 itself is unchanged.
Pricing account access and SLOAD
If the target address of EXTCODESIZE (0x3B), EXTCODECOPY (0x3C), EXTCODEHASH (0x3F), BALANCE (0x31), or CALL (0xF1), CALLCODE (0xF2), DELEGATECALL (0xF4), STATICCALL (0xFA) is not in accessed_addresses, COLD_ACCOUNT_ACCESS_COST is charged and the address is added to the set. If it is already present, WARM_STORAGE_READ_COST is charged. The cost is charged and the set updated at the moment the instruction is invoked. For the CALL family, the 100/2600 cost is charged immediately, just like the existing 700, meaning before computing the 63/64 gas available for the call. For SLOAD, if the (address, storage_key) pair is not in accessed_storage_keys, COLD_SLOAD_COST is charged and the pair is added. If it is already present, WARM_STORAGE_READ_COST is charged. The case of reading a cold slot on a cold account does not occur, since the current execution must already be inside that account to read its slot, and this EIP leaves that behavior undefined. A future EIP that adds remote reads/writes should define its pricing.
SSTORE changes
When SSTORE is called, if the (address, storage_key) pair is not in accessed_storage_keys, COLD_SLOAD_COST is charged in addition and the pair is added to the set. This also changes EIP-2200's parameters. SLOAD_GAS changes from 800 to WARM_STORAGE_READ_COST, and SSTORE_RESET_GAS changes from 5000 to 5000 - COLD_SLOAD_COST, with the rest unchanged. Since SLOAD_GAS is used in composite definitions elsewhere in EIP-2200, such as SSTORE_SET_GAS - SLOAD_GAS, implementations must update those definitions as well. This change is needed because of an attack that rewrites 0 into an arbitrary slot that already holds 0. That attack effectively forces a storage read while paying only 800 gas. Because SSTORE_RESET_GAS is reduced, the total SSTORE cost including COLD_SLOAD_COST stays the same, while the SLOAD-then-SSTORE pattern (e.g. storage_variable += x) actually becomes cheaper.
SELFDESTRUCT changes
If the ETH recipient of SELFDESTRUCT is not in accessed_addresses, COLD_ACCOUNT_ACCESS_COST is charged in addition to the existing cost, regardless of whether the amount sent is zero, and the recipient is added to the set. Unlike other members of the CALL family, WARM_STORAGE_READ_COST is not charged even if the recipient is already warm. This choice keeps the change small, since SELFDESTRUCT already costs 5K gas and calling it more than once has no further effect.
How much does the average cost rise
Based on witness size estimates, an average block's witness is about 1000kB, of which about 750kB is Merkle proofs. Conservatively assuming 2000 bytes per Merkle branch, that is about 375 accesses per block. Since tx.sender and tx.to for the roughly 160 transactions per block are excluded from the price increase, the number of chargeable accesses per block is about 50 to 375. With a 1900 gas increase per access, that is 95000 to 712500 gas, which is offset by raising the gas limit by about 1 to 6%. More precisely, Martin Swende's analysis of Goerli transactions found that average gas cost rises by about 2.36%. Since many contracts inefficiently read the same slot multiple times, some transactions end up saving more than 10%.
Benefits for stateless Ethereum and witness generation
This EIP does most of the work needed to bring stateless witness size down to an acceptable level. Assuming a move to a binary trie, the theoretical maximum witness size excluding code drops from 12500000 / 700 * 800 ≈ 14.3M bytes to 12500000 / 2600 * 800 ≈ 3.85M bytes. The price of code access can be changed once code merkleization is implemented. SNARK/STARK witnesses see a similar benefit. Using Starkware's figures, assuming a typical desktop can prove 10000 Rescue hashes per second, witness generation for a block full of state accesses drops from about 44.64 seconds today to about 12.5 seconds after this EIP, making it feasible to generate witnesses in time on a single desktop machine.
Compatibility risks and mitigations
As with any gas-raising EIP, existing contracts can break in three cases: when they pass a fixed amount of gas to a subcall, when they rely on calls that use gas close to the gas limit, and when they rely on the default 2300 gas stipend given to the recipient of an ETH transfer call. The earlier increase, EIP-1884, did in fact break a small number of contracts. However, this EIP includes an access record set that EIP-1884 did not have, which provides a recovery mechanism. If contract A needs to send funds to address B, calling B separately first to warm the cache, then calling A, means B's SLOAD will only cost 100 gas. The original text proposes, as a more general solution, an idea for a POKE precompile that pre-warms addresses and storage keys, as well as the EIP-2930 access list approach, which covers every known breakage case, including the path from an EOA through a contract to another contract. This EIP is a specification finalized as Final.