ERC20 Snapshot extension disappeared from wizard

Why did the ERC20Snapshots extension disappear from the wizard? Where can I find the example of its usage now?

ERC20Snapshot removed in version 5.0.0:

Removals Summary

The following contracts, libraries, and functions were removed:

  • Address.isContract (because of its ambiguous nature and potential for misuse)
  • Checkpoints.History
  • Counters
  • ERC20Snapshot
  • ERC20VotesComp
  • ERC165Storage (in favor of inheritance based approach)
  • ERC777
  • ERC1820Implementer
  • GovernorVotesComp
  • GovernorProposalThreshold (deprecated since 4.4)
  • PaymentSplitter
  • PullPayment
  • SafeMath
  • SignedSafeMath
  • Timers
  • TokenTimelock (in favor of VestingWallet)
  • All escrow contracts (Escrow, ConditionalEscrow and RefundEscrow)
  • All cross-chain contracts, including AccessControlCrossChain and all the vendored bridge interfaces
  • All presets in favor of OpenZeppelin Contracts Wizard

It was indeed removed for 5.0, and similarly from the Wizard.

We now recommend using ERC20Votes instead.

Got it.

However, we need the Snapshots extension for different purposes than governance, so Votes are unsuitable.

In theory, considering the previous implementation of the ERC20Snapshots extension, I can write the updated one supporting new features such as the _update function instead of token hooks. But the problem is that the previous ERC20Snapshots used the Counters, which are now removed and are unclear how to be replaced.

How should I act in this situation? I don't really want to use older versions of contracts because I see significantly favorable changes in the new versions.

Note that you've marked this as a solution to your question, so it is likely that no one will try to answer it.

1 Like

Hi @jusikX and @barakman,

I, too, am looking for a way to know about historical balances at certain points in time. While I was not exactly overwhelmed with the snapshot feature, I am much less happy with the Votes feature, because the gas overhead seems much bigger even.

Will look into offchain solutions now. Please keep me up to date on the path you are going to take, maybe we can help each other out!

1 Like

I'm not in charge of OZ path, but regarding your note about events - I usually fetch them from an Ethereum Node, i.e., using an offchain script.

Unlike fetching the value of a state-variable in an older block, which requires an Archive Node, which you will likely need to subscribe and pay for, fetching an event which was emitted in an older block can be done via any Node, which you may be able to use for free (for example, via the Infura service).

Hi @barakman,

thanks for the reply and sorry I was so unclear in my wording. I wanted @jusikX to share which path he is going to take with his problem.

Fetching events is interesting, too, thanks for that hint!

1 Like

I rewrote the ERC20Snapshot extension, taking into consideration the v5 release:

  1. Introduced custom errors
  2. _update function instead of the _beforeTokenTransfer hook
  3. rejected the use of the Counters library in favor of simple uint256 variable

Regarding Counters: the counter turned out to be a simple uint256 value but allowed to overflow (when the counter value reaches a max value of uint256, it will simply reset to its default value, i.e., 0, instead of throwing an exception). I believe the library was deleted because of its rare use, redundant in most cases because you can add unchecked everywhere it is needed, and gas optimization, respectively.
In our case, the fact that unit256 could overflow without reverting is a vulnerability. So, I used a simple uint256 variable instead of a counter from Counters.

When I am finished and have more time, I will try contributing to OpenZeppelin with the updated ERC20Snapshot extension.

Still, I think the current version of the Snapshot extension is far from perfect, probably that's why it was removed.

A uint256 counter which starts at 0 and increments by 1 on every iteration will never overflow.

First off, because the transaction will exceed the block gas-limit and revert way ahead of time.

Second, because 2^256 is approximately the number of atoms in the visible (reachable) universe, so there is not enough mass in that part of the universe, let alone on our planet, to support the energy required for this loop to continue rolling for 2^256 iterations.

Hence, for this type of loops it is considered safe to use unchecked incrementing.

Note that support for unchecked-loop-incrementing was even added on solc version 0.8.22:

Notable Features:

  • Unchecked loop increments
  • Adding support for importing EVM Assembly JSON (experimental)
  • Adjusting Yul optimizer to rematerialize zero literals
1 Like

Oh I see this works for loops.

But what about the use of a counter for snapshotId snapshots. It is not implemented in a loop, it is incremented every time the snapshot() function is called.

It theory it shall not exceed the max value only because of

Second, because 2^256 is approximately the number of atoms in the visible (reachable) universe

Yes, in this case, the block gas-limit is not a factor, since the increment takes place only once during every transaction (which also means that whether or not the counter is unchecked is insignificant).

But in either case, for a counter which starts at 0, it would take 2^256 transactions to overflow, and that - of course - is not a practical scenario.

I recommend checking this reply for more context about how we recommend replicating the ERC20Snapshot functionalities.

1 Like

Thanks, @ernestognw. I really appreciate your help!

I built a contract a few months ago which I have not deployed yet which uses ERC20 snapshot. I designed my logic around the snapshotID model and I do not really want to switch. Is it still safe to use ERC20Snapshot?