2

I would like to understand the rather large difference in gas costs of transfer operations of the same ERC-20 token. I know that all ETH transactions cost 21000 gas, and I know that all ERC-20 transfer functions have to make a "transaction" call plus some Smart Contract operations, which will generally lead to transfer functions costing ~40 000 to ~60 000 gas. At least, gas cost in that range is what I have observed for USDC, USDT, LINK, OMG and a few others.

Check the ETH Yellow Paper, page 27, where it says that Gtransaction is indeed 21000: https://ethereum.github.io/yellowpaper/paper.pdf. It also details the costs of all other operations.

Smart Contract operations are encoded in the "data" property of an ETH transaction. The transfer operation takes 2 inputs, the destination address and the value to send.

Examples

Here are 2 USDT transfer operations with a 12 gas difference, along with the "data" property value.

In the ETH Yellow Paper page 27 it says a non-zero byte (Gtxdatanonzero) is worth 16 gas, and a zero-byte (Gtxdatazero) 4 so I was guessing that (16 - 4) might be the difference, though I don't see it in the "data" property strings. They seem to be exactly the same amount of bytes.

Here are transactions that cost way more:

Is it the list in the Yellow Paper that explains the differences? If so, which operations exactly are being called? And how would I figure this out?

Related SE posts

Helpful links

2 Answers 2

1

To explain the first difference of 12, you found the correct answer. See the non-zero bytes in the first example, where in the second example those are zero. Since nonzero costs 16 and zero 4, the difference in calldata cost should be 12, which it is.

0xa9059cbb00000000000000000000000075e89d5979e4f6fba9f97c104c2f0afb3f1dcb88000000000000000000000000000000000000000000000000000000000a811f4c

0xa9059cbb00000000000000000000000086bc4655bf0dd2433e32055d00a9c96dadb0e22f00000000000000000000000000000000000000000000000000000000471bbc40

For your second question, it can have many reasons but this is most likely due to the 63k costing call setting the balance of the receiver from zero to non-zero, which costs 20k gas. When we don't change a storage slot zeroness it only costs 2.9k gas. This would almost perfectly explain the difference.

Edit

Check step [524] of the stack traces of both transactions. For the first transaction it costs 20000, whereas for the 2nd transaction it costs 2900.

5
  • Ah I see yes, 17100 + 12 is exactly the difference between 46,109 and 63,221. Nice. This 20000 gas operation SSTORE seems to be the biggest consumer. May 2, 2023 at 9:02
  • I'm trying to find the absolute worst case of USDT transactions, so taking this 20 000 into account, and all I can think of to make it worse is increase the spent value to billions of USDT, to add 16 gas per byte. Is it possible there are other major operations popping up on top, that would increase the gas beyond 70000? Or do you think we have reached an absolute upper limit here? May 2, 2023 at 9:03
  • I'm not familiar with the actual underlying code but if there are branches on any special cases that would sound about right. Just calculate with type(uint256).max and see if that value makes sense. I would never use hard coded gas costs for anything though, as opcode costs might change, or internal state that influences the calculations.
    – Rens
    May 2, 2023 at 12:11
  • Yea I learned that opcode txnonzerobyte used to cost 68 instead of 16 gas nowadays. So I can use the ETH node native call eth_estimateGas for an accurate gas limit. Problem is if the destination address happens to send away all its ERC-20 after my estimate is made, but before I did my transaction, then my transaction will fail because the gas limit is too low :) therefore I would like some sort of hard limit. But I guess the only way to counteract it is to add 17100 on top of the estimated limit to be certain. May 2, 2023 at 12:22
  • I've added a proposed edit with a reference to the ETH VM stacktrace to your answer, something I just discovered existed :) There you can indeed see the 20000 and 2900 costs. May 2, 2023 at 13:04
1

Transferring tokens calls a function in the token contract. This function is implemented by some regular developer(s) and it can include almost anything. The ERC-20 standard dictates some things which should happen upon calling the transfer function, but it doesn't state anything about upper limits for operations.

For all we know, a token transfer function could start calculating prime numbers or doing any sort of weird other stuff.

Even calling the same contract's same transfer multiple times with the same parameters, the gas cost may differ. For example, setting a value from zero to something else has a different cost compared to setting a value from non-zero to something else.

4
  • I see, so then I guess it's impossible to get a perfect estimate beforehand. I use the eth node native function "eth_estimateGas" for estimates. I take +5% over that estimate for insurance, to account for those small byte difference. But 41300 vs 63200 gas is a huge difference, do you happen to know if "eth_estimateGas" is capable of detecting those large gas jumps? Apr 26, 2023 at 7:40
  • 1
    estimateGas should be able to estimate the gas accurately. It's not 100% certain though, but I don't know the details as to why. Apr 26, 2023 at 8:00
  • I see, thanks :) Then I shall keep a small margin. I have upvoted your answer because it helps me and is useful. However I am waiting for a USDT wizard to come along with an answer like: "The difference between the 63,221 gas and the 41,309 transactions is an SSTORE operation which costs 20000" or something, and under which circumstances this happens, like maybe v0 vs v2 transactions, or the address state, or whatever other factors. Apr 26, 2023 at 8:32
  • 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.