0

I am trying to be able to accurately estimate the gas it will take to send an ERC20 token using Ethers js without first needing that token. Currently, I am able to get an estimate though it does not seem to be super accurate. My code is:

const {ethers} = require("ethers");

const provider = new ethers.providers.EtherscanProvider('homestead', 'API_KEY');

const sendGas = async (to, tokenAddress, amount) => {
    const abi = [
        // Some details about the token
        "function name() view returns (string)",
        "function symbol() view returns (string)",
      
        // Get the account balance
        "function balanceOf(address) view returns (uint)",
      
        // Send some of your tokens to someone else
        "function transfer(address to, uint amount)",
      
        // An event triggered whenever anyone transfers to someone else
        "event Transfer(address indexed from, address indexed to, uint amount)"
    ];


    const signer = new ethers.Wallet('PRIVATE_KEY');
    const account = signer.connect(provider);

    const contract = new ethers.Contract(
        tokenAddress,
        abi,
        account
    ); 

    const gasCost = await contract.estimateGas.transfer(to, amount)
    console.log(parseInt(gasCost, 10));
    return gasCost;
    
}

sendGas('PUBLIC_KEY', '0xa36085F69e2889c224210F603D836748e7dC0088', '0');

When I run the code given the proper inputs it seemingly works as expected. Except the actual gas used and the estimated amount can diverge rather substantially. Firstly, to even have the function execute properly I need to pass the amount as 0 since I want to be able to estimate the gas for tokens that I don't have yet. If I try with a non-zero balance for a token I do not have I get an error for an insufficient balance. My issue is that I need the gas to be accurate to within a few thousand gas but it is currently all over the place. For example, I tried transferring a coin on kovan and got an estimate of 21560 gas, however, it ended up using 35322 gas for the real transaction (and I have seen even large discrepancies). Is this caused by having the amount to send be 0? If so, is there a workaround to get accurate estimates without needing to have the token in my account?

Less important but if anyone has insight it would be appreciated, how do I determine the transfer abi for a token? The one I am using currently is just a standard one provided in the ethers js documentation. Will this work for all tokens? if not, is there a way to get the abi for transfers of any token?

1

TLDR; Not all ERC20 transfers cost the same. There are mostly four cases of ERC20 transfers which influence the gas costs. Most of the time you're better of with passing a high value for gasLimit. But if you really want to minimize that, you can try your best to figure out the senario.

Firstly, to even have the function execute properly I need to pass the amount as 0 since I want to be able to estimate the gas for tokens that I don't have yet

If you pass in 0, you might get an estimate which might differ from actual gas consumption due to the way EVM gas is priced.

There are four cases assuming non zero transfer amount:

  • User is transferring to an account already having some tokens (this will be bit cheaper due to SSTORE 5000 pricing)
  • User is transfering to an account which has no balance (this will be costly due to SSTORE 20000 pricing)
  • User is transfering all of their tokens (this will create a refund of 15000 as it empties the slot, this makes tx cheaper but this refund has been exploited using gas tokens and EIP3298 proposes to remove this refund)
  • User is transfering some of their tokens (since slot is not emptied there will be no refund).

ERC20 Transfer tx's gas consumption can be broken as following:

gas = intrinsic + computation

intrinsic = 21000 + calldata cost
data ~= 560 (in case of ERC20 transfer)

computation ~= balanceof SLOAD + updatebalance SSTORE x 2 + event emit

For example, I tried transferring a coin on kovan and got an estimate of 21560 gas

It is likely that you sent tx to an address which doesn't have any bytecode. This won't fail your tx. The gas estimated/consumed will simply the intrinsic cost.

how do I determine the transfer abi for a token? The one I am using currently is just a standard one provided in the ethers js documentation. Will this work for all tokens?

The ABI is defined by the ERC20 standard. It should work for all "ERC20" tokens. If some token is of a different standard (like ERC777), it might not work and you have to use proper ABI.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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