EIP 3298: Removal of refunds

Simple Summary

Remove gas refunds for SSTORE and SELFDESTRUCT.

Link

1 Like

Rather than remove gas refunds it might be worth trying to educate developers on the usage first. Gas refunds seem like a viable solution to reduce user costs until L2 solutions are more established.

For example there are DeFi protocols that issue contracts (both in the legal and onchain sense) whose operating period has an expiration date. Such protocols also issue new contracts periodically as old ones expire, having a strategy to SELFDESTRUCT expired contracts upon creating new ones will not only prevent state bloat but reduce gas costs for the users that make these calls.

1 Like

Relevant prior discussions: EIP-2751

As a holder of millions of dollars worth of refunds, tokenized and otherwise, I would be strongly interested in a compensation plan. I can prepare an EIP for this. The search for parties to compensate can coincide with cleanup, which would reduce the amount of state formerly dedicated to this purpose. Hence I suggest postponing this change until the ETH2 merge.

My current refund compensation plan would use a 50,000-block average of the gastoken’s Uniswap-V2 WETH token ratios pre-fork to determine their compensation eth value, then replace their contract with another holding that much ETH. The new contracts would replace the free, freeUpTo, freeFrom, and feeFromUpTo methods to “burn” those tokens and send ETH to sender according to the ratio of burned supply to the remaining. The mint function would become a no-op. Then, all of the gastoken state could be removed with the hard fork. The supply-weighted average of GST2 and CHI’s resulting price can be used to value SSTORE refunds as well, such as GST1 and Cancel contracts.

An alternative way to disable refunds gently would be to phase them out, with the refund decreasing by 1 every 1,000 blocks after activation. This approach would not require compensation, and they would clean themselves up automatically over time.

Without compensation, I would be incentivized to floor the gas price to increase the likelihood that my refunds get purchased or used before they are deactivated, and/or seek legal action off-chain. But I suspect cleanup and compensation would be agreeable to all parties.

particularly in exacerbating state size

Quantitatively the space consumed is very minimal, several MB. There are ways to reduce this to about 1 MB that have been proposed in the past; they take advantage of the fact that gastoken contracts are all identical. I mentioned this optimization in the Motivation section of EIP-2185.

inefficiently clogging blockchain gas usage

They only “clog” blockchain gas usage during times of lesser congestion. During that time they are outbidding other activity that would also create space; but unlike that activity, the space is eventually cleaned up. Unlike a hypothetically-functional EIP-1559 variant, state growth is normalized and counter-balanced from the resulting block elasticity.

inefficiently

It can be made more efficient by increasing the refund :stuck_out_tongue:

  • 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.

It is not a problem that ethereum can use double it’s current target because it’s currently far below its capacity, which is good for sync times and state growth. But Binance Smart Chain is demonstrating that modern hardware can still sync the blockchain when the gasLimit/minute is >16x higher. It seems 4x is a recent concern due to EIP-1559, but since we are so very far below capacity I would pitch it as a strong advantage: block elasticity means lower highs during peak gas congestion from the additional capacity. Gastokens are also massively under-utilized, despite efforts to democratize their usage. Next, the duration that gastokens can be used to 2x capacity is limited by the current supply; gastokens are relatively scarce. Shortly, 4x is a non-issue because gas targets are coordinated around sync time and state-growth, not processing time. This is also why the miner-coordinated increase from 10m to 12.5m coincided with an unpredicted decrease in uncle rate.

I’m skeptical that EIP1559 will improve block elasticity, but that is another discussion. For the case that it doesn’t, this should be delayed until after London. The gas price estimation user experience would be much worse with more volatility.

1 Like

It has been suggested to keep metered refunds, where we refund SSTORE gas when you clear storage you set in the same transaction. I want to extend that suggestion: I suggest keeping a metered refund for SELFDESTRUCT so that if the contract is recreated in the same transaction, or destroyed in the same transaction it is created, the CREATE cost is offset. I suspect these would be the most-common use of SELFDESTRUCT post EIP-3298.

2 Likes

I have two points here:

(1) If we do this EIP then eth_estimateGas will return the right gas values, since originally it returned the used gas minus the refunds, but now since the refunds are zero, it returns the actual gas limit value. So this is a good improvement. (Unless I am missing another point where EVM refunds gas, which is not removed in this EIP).
(2) Currently, it is theoretically possible to run a block which executes (almost) twice the gas limit of the block. To do this, each transaction in the block has to have the maximum refund in gas (which is 50%), so we can execute twice as much gas in the block. If we remove the refunds, then this will definitely impact the amount of transactions which fit in a block, since any transaction which originally claimed a refund, will now use up more gas space of the block. This thus halves the amount of gas we can execute in the worst case of a block.

You are mistaken. eth_estimateGas already returns the correct gas limit.

1 Like

This is fine; miners can adjust the gas target. Historically tho, they haven’t.

1 Like

You are mistaken. eth_estimateGas already returns the correct gas limit.

Since when was this changed? Is there an EIP for this one or did clients just upgrade their estimate gas logic?

This is fine; miners can adjust the gas target. Historically tho, they haven’t.

Hmm yeah you are right.

1 Like

For go-ethereum it’s been a gasLimit binary search since at least 2017. Perhaps it behaved differently on other clients tho.

1 Like

As of right now, refunds incentivize both the use of gastokens and the clearing of state when possible. The storage savings from breaking gastokens would be miniscule compared to the cost of de-incentivizing the clearing of state.

When a user transfers their entire balance of an ERC-20, they transfer balanceOf(...), that storage slot gets cleared, and they get a refund. However, if there were no refund, they would be incentivized to not clear the storage slot, and only transfer balanceOf(...) - 1, to save gas in case they ever wanted to re-acquire some of this ERC-20. The same logic can be applied to approvals, deposits/withdrawals into DeFi protocols, deployed contracts, and so on.

Given how high gas prices are, and the competition to be “gas efficient” between DeFi projects, most projects would rush to implement patterns that keep storage slots open to save gas. The size of this state growth would likely be significantly larger than the impact gastokens have. In the worst case of no refunds, every storage slot a user touches would be permanently occupied, as clearing it would be inefficient with regards to gas.

Additionally, gastokens are fairly sustainable. They are created with the purpose of deletion. Looking at CHI, the total supply today (1.6mm) is less thant it was on October 8th, 2020 (1.9mm). It peaked around December, at 3mm. The market cap of gastokens will be limited by gas volatility and the number of transactions per block. There is no reason to have excessive amounts of gastoken on-chain, as there is no profit to be had in minting gastokens that probably won’t be used.

4 Likes

I wrote up the phase out alternative, now EIP-3300

Additionally, gastokens are fairly sustainable. They are created with the purpose of deletion. Looking at CHI , the total supply today (1.6mm) is less thant it was on October 8th, 2020 (1.9mm). It peaked around December, at 3mm.

One other problem that I see with gastokens is that they are inherently inefficient. You need 20000 gas to make a gastoken but you only get 10000 gas from using it (the ratio for GST2 is similar), and so there’s an extra 10000 gas that gets spent that provides no actual value to the network. Because the gas limit is bounded primarily by worst-case DoS attack limits, and not by average usage, we suffer the penalty of this wasted gas being part of the gas limit that everyone can use without getting any benefits out of it.

When a user transfers their entire balance of an ERC-20, they transfer balanceOf(...), that storage slot gets cleared, and they get a refund. However, if there were no refund, they would be incentivized to not clear the storage slot, and only transfer balanceOf(...) - 1, to save gas in case they ever wanted to re-acquire some of this ERC-20. The same logic can be applied to approvals, deposits/withdrawals into DeFi protocols, deployed contracts, and so on.

If we want to mitigate this, one idea is to just reduce the SSTORE gas cost in the nonzero → zero case down to some very minimal value (eg. 100); note that the cold-storage-load cost introduced in EIP 2929 would still be applied on top of this. This would increase the worst-case write count to be about the same as the worst-case read count, but it would fix some of the mispricing issues.

This is insufficient because the difference is dwarfed by SSTORE_SET_GAS. Once you’ve paid for that you should never give it up.

Here is a more comprehensive proposal. This does cut back some of the “simplicity” benefits of removing refunds, but OTOH it does allow us to retain most of the benefits of refunds while achieving the goals of (i) breaking gastoken, and (ii) removing block size variance.


Replace the “refund” counter with two counters: (i) a new_storage_slots_filled counter that increments every time a storage slot goes from zero to nonzero, and (ii) a storage_slots_cleared counter that increments every time a storage slot goes from nonzero to zero. At the end of a transaction, refund 15000 * max(storage_slots_cleared - new_storage_slots_filled, 0) gas. So every 15000 gas refunded must be matched by 15000 gas storage-increase gas paid, and so the maximum amount of gas spent on execution would not be able to exceed the gas limit.

I should hope that’s not the explicit goal, but you don’t achieve it here; GST1 would still work.

You would still have block size variance because you have refunds.

Based on the text I think you got the subtraction backward but it’s problematic either way.


Meaningful refunds seem necessary to incentivize good smart contract architectures where approvals and balances are zeroed when possible. These refunds currently introduce up to 2x block elasticity, which is good. From the Motivation it sounds like there is concern that 4x could be a DoS vector, but since Ethereum is so far under capacity, so-long as the opcodes are correctly priced I don’t think DoS is an issue. With more than 2x elasticity we can have more stable gas prices, reliable confirmations, and handle the irregularities in demand.

I agree that as a gas storage mechanism, current refunds are inefficient and waste storage, but if we had another refund mechanism that was slightly more efficient and didn’t waste storage, classic gas tokens would be out-competed. We would lose the constant state growth property, but the elasticity would come much cheaper in both storage and computation.

This can be done with three opcodes and a persistent account gas refund counter, which could implement a better gas token:

  • SELFGAS which pushed the current account’s refund counter onto the stack
  • USEGAS which reduces the contract’s refund count by up to the amount popped from the stack, adding the amount to the refund counter
  • STOREGAS which increases the current account’s refund counter and consumes additional gas by the amount popped from the stack

This would preserve the gas market and the stability it provides, while phasing out usage of inefficient and wasteful alternatives.

I wrote up the efficient gas storage approach here.

I should hope that’s not the explicit goal, but you don’t achieve it here; GST1 would still work.

Now that I think about it, you are right; the refunds would be still able to cancel out the portion of gas usage that is new-storage-fills. The refund rule would have to be more restrictive (refund only if 0 = original = new != current) to solve that issue.

You would still have block size variance because you have refunds.

But this is not true, because the key invariant that remains is that gas spent on execution would not go above the gaslimit. Every 15000 gas refund would be matched by 15000 gas spent on filling a new storage slot.

Based on the text I think you got the subtraction backward but it’s problematic either way.

I don’t think it’s backward! You refund only if you cleared more storage slots than you fill.

Another thing worth considering is that clearing storage is going to be less useful in the future, if we are implementing either weak statelessness or state expiry. In fact, truly clearing storage would not even be possible; you would need to leave a stub to show that the value is zero, as opposed to the slot being not-yet-edited and the value needing to be dug up from the past. So in the longer term, having less good incentives to clear storage is not even such a useful thing.

The EIP 3403 discussions-to link points here.

I do prefer 3403 to 3298 because it fixes 3298’s storage misincentive, though my preference is still 3322 because I support block elasticity.

Remove the SSTORE refund in all cases except for one specific case: if new value and original value both equal 0 but current value does not, refund 15000 gas.

The 15000 number should probably be higher. I think the Istanbul refund for this scenario is 19800. Since this is a “warm read” it should be cheap and also refund the bulk of the 20000 cost from SSTORE_SET.

Since the proposal removes all refunds except for this case, it doesn’t make sense to maintain a refund counter. Instead, the gas should be added to current gas counter. Should the gas used by a *CALL be negative, the surplus gas is returned to the gas counter, so that the cost negation works recursively. Then the refund counter can be removed entirely.

You are mistaken. eth_estimateGas already returns the correct gas limit.

Huh… if I have a case where this isn’t true–due to refunds–is this a bug I could thereby file and it would get fixed? I’d looked through other issues and I was under the impression that the eth_estimateGas issues were just “this is known to not work and we aren’t going to fix it” ;P.

So, I work on a layer 2 payments system that has to use storage to prevent replays (and the order is intrinsically arbitrary, so it can’t use a monotonic nonce). I use one storage slot per payment. Right now, I’ve got everything set up so that everyone would naturally “want to” delete expired replay prevention slots. (Yes: I also added a way to “forge” replay prevention slots, as it also forms a gas token ;P. I don’t care at all about this behavior.)

The result of this is that, over time, usage of the contract is going to result in O(number of users) storage, as it will be something like “the only storage slots in use are for the payments that haven’t yet been expired, and each user has some small number of payments in flight at any one moment”. Without refunds of any form, this is going to be O(number of payments), which is much much much bigger (something Ethereum obviously itself avoids with its account model).

FWIW, the “viability” of a gas token is strongly related to the “power” of the refund. Have you considered making it so that deleting a storage slot just makes the storage cost non-existent and gives you maybe a tiny 500-800 gas refund? This wouldn’t be viable for a gas token, as you’d need ~500 gas to even specify and find/calculate the storage slot that is storing the gas token you are freeing: the goal of this refund (and associated subsidy) is just to make it economically reasonable to be a bit altruistic and clean up old state.

Put differently, I think there’s something valuable in strategies that don’t necessarily “reward” people for messing with storage state in potentially-weird ways but at least doesn’t penalize people for cleaning up state: without refunds–and with deletions costing thousands of gas–I’d actually be punished pretty hard for bothering to clean up state, and I feel like I should want to clean up state even if it isn’t making me money, as long as it isn’t hurting me.

(edit: And like, I appreciate that maybe in the future state doesn’t matter as much, or it will naturally expire and I won’t need strategies for expiring it myself; I definitely think maintenance of state perpetually isn’t sustainable… but, given the speed of how changes happen, it could be years before we get there, and I want to think that systems like mine should have at least not be disincentivized from avoiding spamming state ;P.)

1 Like