Remove gas refunds for SELFDESTRUCT, and restrict gas refunds for SSTORE to one specific case.
Gas refunds for SSTORE and SELFDESTRUCT were originally introduced to motivate application developers to write applications that practice "good state hygiene", clearing storage slots and contracts that are no longer needed. However, they are not widely used for this, and poor state hygiene continues to be the norm. It is now widely accepted that the only solution to state growth is some form of statelessness or state expiry, and if such a solution is implemented, then disused storage slots and contracts would start to be ignored automatically.
Gas refunds additionally have multiple harmful consequences:
There are two typical ways to implement mutexes: '0-1-0' and '1-2-1. Let's see how they differ
Note: In reality, there are never a negative gas cost, since the refund is capped at 0.5 * gasUsed. However, these tables show the negative values, since a more real-world scenario would likely spend the extra gas on other operations.'
Constant | Value |
---|---|
FORK_BLOCK |
TBD |
SSTORE_REFUND_GAS |
19000 |
For blocks where block.number >= FORK_BLOCK
, the following changes apply.
SELFDESTRUCT
refund.SSTORE
refund in all cases except for one specific case: if the new value and original value of the storage slot both equal 0 but the current value does not (those terms being defined as in EIP-1283), refund SSTORE_REFUND_GAS
gas.Preserving refunds in the new = original = 0 != current
case ensures that a few key use cases that deserve favorable gas cost treatment continue to receive favorable gas cost treatment, particularly:
It also preserves two key goals of EIP 3298:
Refunds are currently only applied after transaction execution, so they cannot affect how much gas is available to any particular call frame during execution. Hence, removing them will not break the ability of any code to execute, though it will render some applications economically nonviable.
Gas tokens in particular will become valueless. DeFi arbitrage bots, which today frequently use either established gas token schemes or a custom alternative to reduce on-chain costs, would benefit from rewriting their code to remove calls to these no-longer-functional gas storage mechanisms.
Note, there is a difference between 'hot' and 'cold' slots. This table shows the values as of EIP-2929 assuming that all touched storage slots were already 'hot' (the difference being a one-time cost of 2100
gas).
Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) |
---|---|---|---|---|---|---|---|
0x60006000556000600055 |
212 | 0 | 0 | 0 | 0 | 212 | |
0x60006000556001600055 |
20112 | 0 | 0 | 0 | 1 | 20112 | |
0x60016000556000600055 |
20112 | 19900 | 0 | 1 | 0 | 212 | |
0x60016000556002600055 |
20112 | 0 | 0 | 1 | 2 | 20112 | |
0x60016000556001600055 |
20112 | 0 | 0 | 1 | 1 | 20112 | |
0x60006000556000600055 |
3012 | 15000 | 1 | 0 | 0 | -11988 | |
0x60006000556001600055 |
3012 | 2800 | 1 | 0 | 1 | 212 | |
0x60006000556002600055 |
3012 | 0 | 1 | 0 | 2 | 3012 | |
0x60026000556000600055 |
3012 | 15000 | 1 | 2 | 0 | -11988 | |
0x60026000556003600055 |
3012 | 0 | 1 | 2 | 3 | 3012 | |
0x60026000556001600055 |
3012 | 2800 | 1 | 2 | 1 | 212 | |
0x60026000556002600055 |
3012 | 0 | 1 | 2 | 2 | 3012 | |
0x60016000556000600055 |
3012 | 15000 | 1 | 1 | 0 | -11988 | |
0x60016000556002600055 |
3012 | 0 | 1 | 1 | 2 | 3012 | |
0x60016000556001600055 |
212 | 0 | 1 | 1 | 1 | 212 | |
0x600160005560006000556001600055 |
40118 | 19900 | 0 | 1 | 0 | 1 | 20218 |
0x600060005560016000556000600055 |
5918 | 17800 | 1 | 0 | 1 | 0 | -11882 |
If refunds were to be partially removed, as specified here, this would be the comparative table. This table also assumes touched storage slots were already 'hot'.
Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) |
---|---|---|---|---|---|---|---|
0x60006000556000600055 |
212 | 0 | 0 | 0 | 0 | 212 | |
0x60006000556001600055 |
20112 | 0 | 0 | 0 | 1 | 20112 | |
0x60016000556000600055 |
20112 | 19000 | 0 | 1 | 0 | 1112 | |
0x60016000556002600055 |
20112 | 0 | 0 | 1 | 2 | 20112 | |
0x60016000556001600055 |
20112 | 0 | 0 | 1 | 1 | 20112 | |
0x60006000556000600055 |
3012 | 0 | 1 | 0 | 0 | 3012 | |
0x60006000556001600055 |
3012 | 0 | 1 | 0 | 1 | 3012 | |
0x60006000556002600055 |
3012 | 0 | 1 | 0 | 2 | 3012 | |
0x60026000556000600055 |
3012 | 0 | 1 | 2 | 0 | 3012 | |
0x60026000556003600055 |
3012 | 0 | 1 | 2 | 3 | 3012 | |
0x60026000556001600055 |
3012 | 0 | 1 | 2 | 1 | 3012 | |
0x60026000556002600055 |
3012 | 0 | 1 | 2 | 2 | 3012 | |
0x60016000556000600055 |
3012 | 0 | 1 | 1 | 0 | 3012 | |
0x60016000556002600055 |
3012 | 0 | 1 | 1 | 2 | 3012 | |
0x60016000556001600055 |
212 | 0 | 1 | 1 | 1 | 212 | |
0x600160005560006000556001600055 |
40118 | 19000 | 0 | 1 | 0 | 1 | 21118 |
0x600060005560016000556000600055 |
5918 | 0 | 1 | 0 | 1 | 0 | 5918 |
Refunds are not visible to transaction execution, so this should not have any impact on transaction execution logic.
The maximum amount of gas that can be spent on execution in a block is limited to the gas limit, if we do not count zero-to-nonzero SSTOREs that were later reset back to zero. It is okay to not count those, because if such an SSTORE is reset, storage is not expanded and the client does not need to actually adjust the Merke tree; the gas consumption is refunded, but the effort normally required by the client to process those opcodes is also cancelled. Clients should make sure to not do a storage write if new_value = original_value
; this was a prudent optimization since the beginning of Ethereum but it becomes more important now.
Copyright and related rights waived via CC0.