Remove gas refunds for SELFDESTRUCT, and restrict gas refunds for SSTORE to one specific case.
Motivation
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:
Refunds give rise to GasToken. GasToken has benefits in moving gas space from low-fee periods to high-fee periods, but it also has downsides to the network, particularly in exacerbating state size (as state slots are effectively used as a âbatteryâ to save up gas) and inefficiently clogging blockchain gas usage
Refunds increase block size variance. The theoretical maximum amount of actual gas consumed in a block is nearly twice the on-paper gas limit (as refunds add gas space for subsequent transactions in a block, though refunds are capped at 50% of a transactionâs gas used). This is not fatal, but is still undesirable, especially given that refunds can be used to maintain 2x usage spikes for far longer than EIP 1559 can.
The mutex usecase
There are two typical ways to implement mutexes: â0-1-0â and â1-2-1. Letâs see how they differ
â0-1-0â:
Istanbul: 1612
Berlin: 212
NoRefund: 20112
EIP-3403: 1112
â1-2-1â:
Istanbul: 1612
Berlin: 212
NoRefund: 3012
EIP-3403: 3012
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.â
Specification
Parameters
Constant
Value
FORK_BLOCK
TBD
SSTORE_REFUND_GAS
19000
For blocks where block.number >= FORK_BLOCK, the following changes apply.
Remove the SELFDESTRUCT refund.
Remove the 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.
Rationale
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:
Anti-reentrancy locks (typically flipped from 0 to 1 right before a child call begins, and then flipped back to 0 when the child call ends)
ERC20 approve-and-send (the âapproved valueâ goes from zero to nonzero when the token transfer is approved, and then back to zero when the token transfer processes)
It also preserves two key goals of EIP 3298:
Gas tokens continue to be non-viable, because each 19000 refund is only possible because of 19000 extra gas that was paid for flipping that storage slot from zero to nonzero earlier in the same transaction, so you canât clear some storage slots and use that saved gas to fill others.
The total amount of gas spent on execution is capped at the gas limit. Every 19000 refund for flipping a storage slot non from zero -> zero is only possible because of 19000 extra gas paid for flipping that slot from zero -> nonzero earlier in the same transaction; that gas paid for a storage write and expansion that were both reverted and so do not actually need to be applied to the Merkle tree. Hence, this extra gas does not contribute to risk.
Backwards Compatibility
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.
Test Cases
2929 Gas Costs
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
With EIP-3403 partial refunds
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
Security Considerations
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.