ethers.js: Gas estimation failure gives a misleading error message

Launching a transaction to the Goerli network, I have been getting this error:

{
  "reason":"cannot estimate gas; transaction may fail or may require manual gas limit",
  "code":"UNPREDICTABLE_GAS_LIMIT",
  "error":{
    "reason":"processing response error","code":"SERVER_ERROR",
    "body":"{\"jsonrpc\":\"2.0\",\"id\":69,\"error\":{\"code\":-32000,\"message\":\"gas required exceeds allowance (0)\"}}","error":{"code":-32000},"requestBody":"{\"method\":\"eth_estimateGas\",\"params\":[{\"gasPrice\":\"0xb2d05e00\",\"from\":\"0xe101391adf348cd80bb71b97306f3cddd5d34586\",\"to\":\"0x1a72d4949936bda05e187706daf0db6fb96603b6\",\"data\":....}

The transaction is launched like:

const contract = new Contract(addr, abi, wallet)
const tx = await contract.myTransaction(...)

At first I checked https://github.com/ethers-io/ethers.js/issues/484 as a potential duplicate. However, the real error is that the account sending the transaction holds no ether. Nothing related to failing transactions or similar.

In such cases, it would be nice to detect the insufficient or null balance and warn about this instead of the unpredictable gas limit message alone. BTW, thank you for such a great library!

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 8
  • Comments: 18 (2 by maintainers)

Most upvoted comments

I’ve done further debugging in regards to this issue. I think the main problem is that Ethers includes a gasPrice parameter when estimating gas. I’ve added some of the Eth JSON RPC requests done via both Ethers.js and Web3.js as examples.

For Ethers.js, when estimating the gas for a contract method:

Request: {"method":"eth_estimateGas","params":[{"gasPrice":"0x3b9aca00","from":"0x6be02d1d3665660d22ff9624b7be0551ee1ac91b","data":"0x608060405234801561001057600080fd5b506040516102a13803806102a183398181016040528101906100329190610054565b806000819055505061009e565b60008151905061004e81610087565b92915050565b60006020828403121561006657600080fd5b60006100748482850161003f565b91505092915050565b6000819050919050565b6100908161007d565b811461009b57600080fd5b50565b6101f4806100ad6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637cf5dab0146100465780638381f58a14610062578063d826f88f14610080575b600080fd5b610060600480360381019061005b91906100c5565b61008a565b005b61006a6100a1565b60405161007791906100fd565b60405180910390f35b6100886100a7565b005b806000546100989190610118565b60008190555050565b60005481565b60008081905550565b6000813590506100bf816101a7565b92915050565b6000602082840312156100d757600080fd5b60006100e5848285016100b0565b91505092915050565b6100f78161016e565b82525050565b600060208201905061011260008301846100ee565b92915050565b60006101238261016e565b915061012e8361016e565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561016357610162610178565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6101b08161016e565b81146101bb57600080fd5b5056fea2646970667358221220bd81c94057c94a59a3d7a613caf9b6bb9d93827e1c61cfaf444e295e35c2fd8964736f6c634300080000330000000000000000000000000000000000000000000000000000000000000005"}],"id":44,"jsonrpc":"2.0"}.
Response: {"jsonrpc":"2.0","error":{"code":-32603,"message":"execution fatal: Module { index: 10, error: 0, message: Some(\"BalanceLow\") }"},"id":44}

Even though the from account has plenty of funds (testing account) the UNPREDICTABLE GAS LIMIT happened. For the same transaction using the web3.js library the gasPrice is not included (and from is not included as well) and so it works:

 Request: {"jsonrpc":"2.0","id":1,"method":"eth_estimateGas","params":[{"data":"0x608060405234801561001057600080fd5b506040516102a13803806102a183398181016040528101906100329190610054565b806000819055505061009e565b60008151905061004e81610087565b92915050565b60006020828403121561006657600080fd5b60006100748482850161003f565b91505092915050565b6000819050919050565b6100908161007d565b811461009b57600080fd5b50565b6101f4806100ad6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637cf5dab0146100465780638381f58a14610062578063d826f88f14610080575b600080fd5b610060600480360381019061005b91906100c5565b61008a565b005b61006a6100a1565b60405161007791906100fd565b60405180910390f35b6100886100a7565b005b806000546100989190610118565b60008190555050565b60005481565b60008081905550565b6000813590506100bf816101a7565b92915050565b6000602082840312156100d757600080fd5b60006100e5848285016100b0565b91505092915050565b6100f78161016e565b82525050565b600060208201905061011260008301846100ee565b92915050565b60006101238261016e565b915061012e8361016e565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561016357610162610178565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6101b08161016e565b81146101bb57600080fd5b5056fea2646970667358221220bd81c94057c94a59a3d7a613caf9b6bb9d93827e1c61cfaf444e295e35c2fd8964736f6c634300080000330000000000000000000000000000000000000000000000000000000000000005"}]}
Response: {"jsonrpc":"2.0","result":"0x2cbb8","id":1}

Hope this helps. Probably simplifying the eth_estimateGas method to not include gasPrice should solve this.

We do get an insufficient funds error if enough ETH is not present. Did you try using correct wallet in your code, did that work?

But as you say you get cannot estimate gas error instead of insufficient funds error. I think I have an explanation for this.

When you run contract.myTransaction(...), ethers js does 2 rpcalls to node internally:

  1. Makes an estimate gas rpcall to the node. If call reverts you will get error cannot estimate gas that you are getting. (I don’t think a gas estimate requires to have enough eth at an address)
  2. Since estimation was successful, it signs tx and sends to the node. If funds are not present, the node returns an error result, which ethers js throws as insufficient funds

Which means that your tx is probably reverting for that different address even if it had some ETH. Though you can confirm this to be sure.

@zemse As I was saying, the transaction and the contract were both correct. Submitting the very same parameters with Remix (from the right wallet) worked flawlessly. And also, submitting the very same transaction with the right wallet from Ethers.js also worked flawlessly, then.

So:

  • A valid transaction fails to submit because estimating the gas cost fails when the underlying address has no ether.
  • The error message mentions nothing about the lack of ether, and rather focuses on the possibility of a revert (which doesn’t apply in this particular case)

It may be a vicious circle, because you can’t know if you have enough ether for a transaction until you have estimated it. And you can’t estimate as estimation fails because you have insufficient funds.

But at least, if the balance is exactly 0, we do know for sure that the failure to estimate is due to this.

Thank you!

We do get an insufficient funds error if enough ETH is not present. Did you try using correct wallet in your code, did that work?

But as you say you get cannot estimate gas error instead of insufficient funds error. I think I have an explanation for this.

When you run contract.myTransaction(...), ethers js does 2 rpcalls to node internally:

  1. Makes an estimate gas rpcall to the node. If call reverts you will get error cannot estimate gas that you are getting. (I don’t think a gas estimate requires to have enough eth at an address)
  2. Since estimation was successful, it signs tx and sends to the node. If funds are not present, the node returns an error result, which ethers js throws as insufficient funds

Which means that your tx is probably reverting for that different address even if it had some ETH. Though you can confirm this to be sure.

Yes I believe gas estimation will fail if a gas price is given and the from address doesn’t have enough ether to execute the tx at the given gas price

If gas price isn’t provided for gas estimation, this isn’t taken into consideration and the gas estimation will ignore funds (unless the tx causes the funds to be moved)

It seems that your contract is reverting when you call myTransaction function. Can you confirm if any obvious require statements might be failing? Just to mention, there is a recently popular tool to debug on-chain contracts called tenderly. That might help you to confirm if there is any problem occurring in the contract or not.