How to clear storage and get incentivized by Ethereum Blockchain ?
Do you know EVM has the functionality of rewarding users with a gas refund for clearing the on-chain storage occupied by the contract data?
Let’s learn more about this. There have been some changes after the London fork. But to understand it better we will look at the scenario before London Upgrade and then we will see the changes proposed in the London upgrade.
But before starting with refunds we should get a bit of understanding about storage gas consumption, this will make the gas calculations easy to understand for you.
I have copied some gas amounts directly from the Ethereum Yellow paper, let’s see what are they.
1. Gcoldsload — 2100 Cost of cold storage access.
2. Gsset — 20000 Paid for an SSTORE operation when the storage value is set to non-zero from zero.
3. Gsreset — 2900 Paid for an SSTORE operation when the storage value’s zeroness remains unchanged or is set to zero.
4. Rsclear — 15000 Refund given (added into refund counter) when the storage value is set to zero from non-zero.
Let’s quickly understand this.
1. Gcoldsload — you have to pay every time you access any storage variable for the first time in a function, the second or consecutive times it costs 100 gas.
2. Gsset — Pay every time you set any variable from zero value to non-zero(false to true in case of bools). In simple words, you are changing the default value and the nodes now have to keep track of that slot.
3. Gsreset — Pay every time you set a non-zero value to a non-zero or zero value.
4. Rsclear — Whenever you set any value to its default value you get the refund.
Remember these points before we start
- Whenever we set any value from non-zero to non-zero or non-zero to zero, we collectively say the gas consumed to be 5000, adding Gcoldsload and Gsreset.
- Every transaction will consume all the related gas while executed and the refund is calculated at the very end of the transaction.
- There is an initial gas attached to every transaction which is 21000. This gas is consumed by the validator to make sure the transaction is valid or not. 21000 is added to every transaction and it is not in our control, so every transaction consumes 21000 + whatever gas the function data needs.
- The gas calculations below will implicitly include 21000 gas.
The refunds work whenever you set something to its default value, for uint it is zero for a boolean it’s false, etc. For ease of understanding, I’ll be taking examples of uint.
Before London Fork
The EVM refunds gas to the user in two cases. First, if you are calling selfdestruct
function, 24000
gas is refunded from the total consumed gas. This was simple and there’s nothing to talk about it as the gas refund functionality for selfdestruct
was removed in EIP-3529 and recently the selfdestruct
method is also deprecated. So, there is no reason to talk about it.
Second, if you set any variable to its default value, you get a refund of 15000. Why does it happen? Setting the value to default value means you are clearing the storage because the nodes don’t need to track that particular slot of storage you just cleared.
This is a screenshot of the Ethereum Yellow paper stating the refund amount.
But there is a proper way of calculating the gas to refund.
Here is the reference from the Ethereum Yellow paper.
Mechanism: The refund is given at the end of any transaction and capped at a maximum number. That maximum number is half of the total used gas.
let’s understand it with the example of uint. What happens when we set any non-zero uint value to zero?
Suppose you are setting a single uint variable to zero in a transaction so the total applicable refund will be 15000
, but the total gas consumed in this transaction is 24000
, half of which is 12000
, now 12000
becomes the maximum amount of gas that can be refunded which means the transaction will now cost you24000 — MAXIMUM_GAS_REFUNDABLE = 24000–12000 = 12000
gas.
Although we were expecting to get 15000
gas back, the limit got capped at 12000
, hence reducing the amount to 12000
. What if our transaction is consuming 40000
gas? In this case, half of 40000
is 20000
which is greater than 15000
so we will get the refund of 15000
gas.
This was the case when you are setting only one uint variable to zero, what will happen if we are setting multiple uint variables to zero? For example
contract {
uint256 count = 1;
uint256 count2 = 2;
uint256 count3 = 3;
uint256 count4 = 3;
uint256 count5 = 3;
uint256 count6 = 5;
uint256 count7 = 6;
uint256 count8 = 6;
function setTOzero() external {
count = 0;
count2 = 0;
count3 = 0;
count4 = 0;
count5 = 0;
count6 = 0;
count7 = 0;
count8 = 0;
}
}
//Transaction cost will be 21000+ execution cost//execution cost = 8 * (Gcoldsload +Gsreset) = 8 * 5000 = 40000// Transaction cost = 21000 + 40000 = 61000
Whatever your transaction cost is the EXECUTION_COST+ 21000
. Now the refund is calculated based on how many variables you are setting to default. In this case, we are setting 8 uint variables to zero so the refundable amount counts to be 15000 * 8 = 1,20,000
.
Wait, if this is the refund amount then how much gas our transaction is consuming? When you will calculate the gas to be consumed you will get the amount of approx 61000
. Now, what will happen if 1,20,000
gas is refunded on a transaction of 61000
? The miner will end up paying for the transaction, this is why the capping mechanism was introduced.
According to the formula, the maximum gas to be refunded will be 61000/2 = 30500
. So the gas consumed will be 61000–30500 =30500
instead of 61000 – 1,20,000
. Now I think this is pretty clear to you, but this method is no more valid now after London upgrade. If you want to test this then you can switch to the Berlin version of VM in Remix IDE and watch everything working.
Now let’s see what has changed in the London upgrade.
Post London Upgrade
These numbers have changed after the introduction of EIP-3529 in the London upgrade. Let’s see what has changed. The refund gas amount is now reduced to 4800
and the maximum amount that can be refunded now is one-fifth of the gas consumed. Let’s understand with the same approach as before.
In the first case, if we are setting only one variable to zero, the transaction is consuming 26000
. when we notice the maximum gas rule in EIP-3529, it says TOTAL_GAS_CONSUMED divided by 5, which is in our case 26000/5
equals 5200
, that means we were eligible for a maximum refund of 5200
in this transaction but as mentioned in the EIP-3529 the refund amount is 4800
. This time 4800
is refunded to us and the transaction costs 21400 gas(some additional gas).
Let’s take the second case where we were setting 8 variables to zero. The gas consumed is still 61000
. Talking about the refund, we expect a total refund of 4800 * 8 =38400
, meaning the consumption we expect is61000–38400 = 22600
. But when you will execute this transaction, you will notice that the gas consumed is 49000
, now you get the point that the gas to be refunded is capped to a maximum of 61000/5 = 12200
which means the transaction will consume a total of 61000–12200 ~ 49000
.
You might be thinking what caused the gas refunds to get lower in the London upgrade. Look at the reasons I have copied from the official EIP website:
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, the benefits of this technique have proven to be far lower than anticipated, and gas refunds have had multiple unexpected 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 concept of gas refund must be clear to you now. The more you play with this on remix the more you will understand the calculations. Why not try executing some real-world functions? As the ones I wrote above are just examples. You can try to see the gas consumption in real contracts like the transfer function ERC20 when the balance is about to get zero. Let me know what you find by these experiments:)
Have any questions about this topic? Ask me anytime, on Twitter, LinkedIn, or telegram.