EIP-3529 Gas refund reduction (Reduction in refunds)

Final · Fees & gas · EIP status Final · created 2021-04-22

Removes the gas refund given when a contract is deleted (SELFDESTRUCT) and reduces the refund given for clearing a stored value to zero from 15,000 to 4,800 gas. It also lowers the cap on the refund a single transaction can receive from 1/2 of gas used to 1/5.

Why it is needed

Ethereum introduced gas refunds to encourage clearing unused storage and contracts. However, the cleanup effect it expected was small and the side effects were larger. The best-known case is GasToken. It fills storage slots with values when fees are low, then clears them to collect refunds when fees are high. Because it uses storage slots like a battery for storing gas, it actually grows the state the network must keep and fills block space inefficiently. Refunds also increase variance in block size. Because the refunded gas lets the next transactions in the same block execute more, a block can actually process close to twice its nominal gas limit. Such usage spikes can last much longer than EIP-1559 can suppress. Instead of removing refunds entirely, this EIP reduces them only to the point where this kind of abuse no longer works.

What changes

Today

After EIP-3529

Who is affected

In depth

Remove the SELFDESTRUCT refund

From the FORK_BLOCK onward, the gas refund for SELFDESTRUCT is removed entirely. Destroying a contract on its own no longer returns any gas. This is the first of the three changes in the original specification. Because refunds are applied only after transaction execution ends, it does not affect the amount of gas any call frame can use during execution and does not break code execution itself. However, some applications that relied on the refund become economically unviable.

SSTORE_CLEARS_SCHEDULE from 15,000 to 4,800

Changes SSTORE_CLEARS_SCHEDULE as defined in EIP-2200 (the refund for clearing a slot whose original value is nonzero to zero, currently 15,000 gas) to SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST. Under EIP-2929 and EIP-2930, this sum is 4,800 gas. The other two refund cases in EIP-2200 stay as they are: 19,900 gas (SSTORE_SET_GAS - SLOAD_GAS) when a slot that was originally 0 is changed to a nonzero value and then restored to 0, and 4,900 gas (SSTORE_RESET_GAS - SLOAD_GAS) when a slot is restored to its original value. These two return the cost of an earlier write to the same slot, so they cannot be used for GasToken.

Refund cap to gas_used // 5

Until now, the maximum refund a transaction could receive was gas_used // 2. This EIP gives this divisor the name MAX_REFUND_QUOTIENT and changes its value from 2 to 5. That is, the refund cap drops from 1/2 of gas used to 1/5. The amount of storage write work per block that refunds can add is limited to at most 25%, which makes it harder to use this mechanism for denial-of-service attacks that target storage writes.

Why exactly 4,800: the pairing argument

For GasToken to stop working, no more gas must be extractable from a slot than was put into it. The source derives the condition by pairing each refund with an earlier expenditure on the same slot in the same transaction. If the event that clears a slot whose original value is nonzero to zero is the first one in that transaction, it can be paired with the minimum cost of first reading and modifying that slot, SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST. If it is the second or later, the earlier write that moved the value away from zero deducted SSTORE_CLEARS_SCHEDULE from the refund counter, so the two cancel out. Therefore, if SSTORE_CLEARS_SCHEDULE <= SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST, total gas expenditure per slot is always positive, and this EIP lowers SSTORE_CLEARS_SCHEDULE to exactly that sum. Intuitively, the design leaves no net refund when clearing data that has not yet been read (usually useless), and leaves a net refund when clearing data that has been read (more likely to be useful).

Patterns that are preserved: reentrancy locks and ERC20 approve

A storage pattern that goes from 0 to a nonzero value and back to 0 still costs only about 100 gas. The source gives two important examples: the reentrancy lock (changing from 0 to 1 just before a child call and restoring to 0 when the call ends) and ERC20 approve followed by transfer (the allowance goes from 0 to a nonzero value and returns to 0 once the transfer is processed). Such cases deserve favorable gas treatment, and this EIP keeps that treatment.

How much storage cleanup incentive remains

EIP-3298 and EIP-3403, which remove refunds entirely, were criticized because the incentive to clear slots to zero disappears. For example, if instead of transferring a full 1 unit of an ERC20 token you transfer only 0.999999 and leave a balance, then when you later receive the same token again the SSTORE costs only 5,000 gas (2,100 for the read + 2,900 for a nonzero-to-nonzero write) instead of 22,100 gas (including 20,000 for a zero-to-nonzero write). Today the 15,000 gas refund offsets this, so leaving a balance pays off only when the probability of reusing the slot exceeds 15000/17100 = 87.7%. If refunds are removed entirely, leaving a balance pays off even when that probability is only slightly above 0%. This EIP keeps a 4,800 gas refund, so the incentive to leave a balance arises only when the probability of reuse exceeds 4800/17100 = 28.1%. It is not perfect, but the source judges it to be higher than the average expectation of reacquiring a token with a fully emptied balance at the same address.

Client implementation notes and security considerations

Refunds are not visible during transaction execution, so they do not affect execution logic. Excluding SSTOREs that wrote from 0 to a nonzero value and later restored it to 0, the maximum gas a block can use for execution is bounded by the gas limit. A reverted write does not actually grow storage and the client does not need to adjust the Merkle tree, so the processing burden disappears along with the refunded gas, which means these writes can be left out of the count. The source stresses that when the new value equals the original value (new_value = original_value), no storage write should be performed. This has been a sensible optimization since the early days of Ethereum, but it becomes more important after this EIP. This EIP's status is Final and its specification is fixed.

Original EIP · ← EIPs