EIP 3298: Removal of refunds

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

3403 still ruins any incentive to clean up state outside of the same transaction. The misalignment of incentives should offset any perceived gains on the storage-bloat motivation, but with real storage that would need to be rented under any state rent scheme. The concerns described by @thabaptiser have only been addressed in the same-transaction case. Here are some specifically incentivized behaviors, which I listed in ACD.

  • Storage arrays should use 1-indexing such-that length-zero is stored as 1 so adding anther element does not incur SSTORE_SET. Additionally, popped elements should not be cleared for the same reason
  • UI-prompted approvals should always be infinite.
  • When selling all tokens you should leave 1 in your wallet, which will change the “Sell MAX” behavior on most DEX interfaces.

I’m also opposed to 3403’s motivation because I favor elasticity. 4x is the best part of 1559 and as a power user who transacts during congestion I don’t want to see larger spikes.

In ACD, @holiman mentioned that miners have been mining blocks that are only gas tokens. F2Pool clarified this was because this is their default candidate block and it is only mined if they have not yet sealed another.

In ACD it was mentioned that miners may increase the gas limit under 1559 to fight against the base fee. The possibility of 4x may discourage that behavior since they will struggle to capitalize on MEV if 4x exceeded their full capacity. Because of this and sync-time concerns for the node failure case, I don’t expect miners to target the gas limit above 4x their capacity.

Here is a copy of the prepared statement I read during today’s ACD. I didn’t read it very well (8 charisma), and Tim said on Twitter that some asked for the text.

First, the proposal removes any incentive to clear state, except for the same-transaction case. When smart contract engineers build around such incentives we will see more state bloat. Approving less than infinite will be phased out in the UX. Selling all of your tokens on most interfaces will leave 1 unit behind. Storage arrays will be cleared by setting the size field to 1 (which will mean 0) and leaving all entries dirty as it would be foolish to clear them. And these are only a few of the design implications.

The proposal also sacrifices current elasticity, which smooths gas price spikes during peak congestion. 1559 does not provide sufficient elasticity because peak congestion lasts hours not minutes. By sacrificing refund elasticity concurrently with 1559 we should expect a net increase in volatility that would counteract the anticipated improvements in signature-time gas price estimation. While the motivations for the proposal cite brief 4x sprints as possibly dangerous, I believe them to be the top feature of 1559. Grocers don’t raise their prices during peak hours; they hire part-time workers, so their customers don’t complain and flee to other stores. The long-term costs of potential 4x blocks are amortized during periods of lower congestion. But all nodes should be able to verify the consecutive 4x blocks smoothly, else they wouldn’t be able to sync the blockchain in any reasonable timeframe.

As proof that the network can handle 4x today I present Binance Smart Chain, which sets a higher gas limit of 30 million every 3 seconds, approximately 11 times the current Ethereum capacity. My devnode, which runs on an older low-end processor, gets 70 mgas per second in Berlin, so I would still be able to sync Binance Smart Chain even if all of their blocks were 4x.

In the previous meeting we agreed to table 3403 if 4x wasn’t a security concern. There have been some better ideas floated in the discord, such as separate markets for computation gas and storage growth. A less-rushed solution might do more good than harm, and we could free up London engineering bandwidth for more-important work.

I’m in support of this EIP. It cleans up EVM mechanics in a way that feels worth the downsides that I’ve seen thus far.

Addressing some specific points:

I am empathetic to what appears to be a significant financial downside that you may face with this change, but this type of reason isn’t compelling to me. You should probably start looking now at how you can reduce your financial risk from this change.

Refunds have been shown to not be a viable mechanism for mitigating state growth in a meaningful way. Thus, the argument that this will make state growth/bloat worse is not compelling here. See this other post on “state expiry” for how we intend to meaningfully address state growth: Resurrection-conflict-minimized state bounding, take 2 - #17 by vbuterin - Execution Layer Research - Ethereum Research . The “state expiry” plans will be effective with or without refunds or incentives to clear state.

citation needed; refuted several times with specific examples.

We have had refunds since the beginning of the chain. State growth is a problem and continues to be a problem… Removing refunds willmake the problem worse. My intuition is that the amount worse is going to be insignificant when compared to the overall rate of growth. Without firm economic bounds, state growth/bloat will continue to be a problem Refunds do not provide economic bounds for the overall state size. State expiry fully solves this. With state expiry, state clearing incentives no longer matter with respect to limiting total state size. Thus, the added benefit of having some incentive to clear state feel insignificant compared to the complexity cleanup that removal of refunds provides.

Please let me know if something is unclear here.

1 Like