Overhead of ERC20Votes Transfer

I am interested in the overhead in the transfer function of ERC20Votes compared to ERC20. This is caused by calling _moveVotingPower(delegates(from), delegates(to), amount) in the _afterTokenTransfer function. Even if the sender and receiver have never delegated or only 0 tokens are transferred, the transfer is more expensive than a normal ERC20 transfer. My question is what is the minimum additional cost?

In the minimum case (e.g. amount = 0), essentially only delegates(from) and delegates(to) have to be called and passed as parameters together with amount. Afterwards, only a simple comparison in _moveVotingPower has to be carried out.

Hello @tom4543

Right now, the ERC20Vote implementation will do the delegate lookup for the from and to accounts, even if the transfer amount is 0. This includes two sload, so the total cost is ~4.2k gas.

If both delegate are equal OR if the value transferred is 0 OR if neither account as a delegate ... then that is basically it.

Otherwise we must push or update a checkpoint for each delegate. Worst case scenario is creating 2 new checkpoints and updating the array lengths, so ~52k gas

How did you get 52k? It looks like a typo, it's much less than that.

In the worst case, in which both source and destination token holders have delegates, _moveVotingPower will emit for each of them an event with two topics (selector + indexed argument) and do the following storage operations:

  • checkpoints[delegate].length: read + read + write
  • checkpoints[delegate][last]: read
  • checkpoints[delegate][last + 1]: write

So: an event (~1500) plus 3 storage accesses (3 x 2000) plus 5 storage operations (5 x 100), all of that times 2, which adds up to 16k.

Additionally you first read both delegates which as mentioned is 4.2k gas.

In total you get 20.2k gas worst case overhead.

If one of the two accounts does not have a delegate, it goes down to 12.2k.

If neither has a delegate, just 4.2k.