Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas estimation failure gives a misleading error message #1232

Closed
brickpop opened this issue Jan 9, 2021 · 18 comments
Closed

Gas estimation failure gives a misleading error message #1232

brickpop opened this issue Jan 9, 2021 · 18 comments

Comments

@brickpop
Copy link

brickpop commented Jan 9, 2021

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 #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!

@zemse
Copy link
Collaborator

zemse commented Jan 9, 2021

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.

@brickpop
Copy link
Author

Hi @zemse sure, I tried in different environments as well.

From Remix, sending the very same parameters to the same contract address, ABI, etc... the transaction completes without any issue.

The only difference, is me using the wrong wallet (having tokens, but not eth) on ethers.js and the right wallet on Remix.
This is why it would be helpful to get an error message warning about insufficient funds and not simply an estimation failure.

@zemse
Copy link
Collaborator

zemse commented Jan 11, 2021

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.

@brytemorio
Copy link

brytemorio commented Jan 11, 2021

Still Having The same issue (Using ethers-v5)
Error Code:

  `reason: 'cannot estimate gas; transaction may fail or may require manual gas limit',
   code: 'UNPREDICTABLE_GAS_LIMIT',
   error: Error: processing response error (body="{\"jsonrpc\":\"2.0\",\"id\":44,\"error\":{\"code\":-32016,\"data\":\"Reverted\",\"message\":\"The execution failed due to an exception.\"}}", 
   error={"code":-32016,"data":"Reverted"}, 
   requestBody="{\"method\":\"eth_estimateGas\",\"params\":[{\"gasPrice\":\"0xa0eebb000\",\"from\":\"0xb8ef790fae186a251035b0666c89832257cba886\",\"data\":\....`

Not sure what I am doing wrong so here is the deployment code:

                    `import { ethers } from "ethers"
                     import {QNDToken__factory, QNDTokenSale__factory, QNDTokenSale, QNDToken } from "../typechain/contracts/"

                             // global objects
                            const FinalSupply = BigInt(30780 * Math.pow(10, 8))
                            const deployer = "<wallet_private_key>"
                            const priceFeedAddr = "<contract-address>" // chainlink Kovan
                            const tokenName = "<token_name>"
                            const ticker = "<ticker>"
                            const infura = {
                                     projectId: "<projectId>",
                                     projectSecret: "<projectSecret>"
                          }

                           let tokenFactory
                           let saleFactory
                           let token: QNDToken
                           let sale: QNDTokenSale

                           const network = ethers.providers.getNetwork("kovan")
                           const provider = new ethers.providers.InfuraProvider(network, infura)
                           const wallet = new ethers.Wallet(deployer, provider)
                           tokenFactory = new QNDToken__factory(wallet)
                           saleFactory = new QNDTokenSale__factory(wallet)


                          async function deployContracts () {
                                  token = await tokenFactory.deploy(tokenName, ticker, FinalSupply)
                                  await token.deployed()
                                           .then(async () => {
                                                   sale = await saleFactory.deploy(priceFeedAddr, wallet, token.address)
                                                   await sale.deployed()
                                                   console.log("QNDToken Contract Address: ", token.address)
                                                   console.log("QNDToken Sale Contract Address: ", sale.address)
            
                                                      })
                                             }

                          deployContracts()
                                       .then(() => {
                                              console.log("Deployment Sucessful")
                                              process.exit(0)
                                         })
                                      .catch(error => {
                                               console.error(error)
                                               process.exit(1)
                                        })`

Wallet Has sufficient Test ETH

@zemse
Copy link
Collaborator

zemse commented Jan 12, 2021

@brytemorio The execution failed due to an exception. This seems to be a revert triggered in the smart contract.

@ricmoo
Copy link
Member

ricmoo commented Jan 12, 2021

I agree with @zemse. Does your contract have something in the constructor or as initializers to global variables that could cause a failure. Basically, when ethers is asking for an estimate to deploy from the nodes it is responding it cannot provide a safe estimate. If you know values that are safe, you can bake them into your execution.

For example, if you know 250,000 gas is safe, you can use:

token = await tokenFactory.deploy(tokenName, ticker, FinalSupply, {
    gasLimit: 250000
});

this will bypass ethers attempting to estimate gas and force using that amount. You can then similarly use the same technique for the sale contract.

This error usually indicates that the contract deployment will fail, so it may still not succeed. However, sometimes estimation incorrectly guesses deployment will fail, especially if there are calls to external contracts, or variables like now are accessed, so things might just truck along fine with a hard-coded gasLimit.

Let me know! :)

@brytemorio
Copy link

@ricmoo though I have longed solved the issue, bu it is as you anticipated,
The problem is at the last line from the constructor:

constructor(
        string memory _tokenName,
        string memory _ticker,
        uint256 _supply
        ) ERC20(_tokenName, _ticker) QNDTokenCapped(_supply){
        _setupDecimals(__decimals);
        addMinter(_msgSender());    //very naive
    }

(the above code from the token contract)

I spotted the bug patiently scanning through the erroneous json outuput, the very specific message stating what exactly the error was. One thing I however found inconvenient was that the message was mixed up in the contracts bytecode plastered all over my screen. It seems that a failure in one contract cascades into the next, at least from the way I'd deployed both in chains. Perhaps it would be much nicer to have less verbose but very specific erroneous outputs on failures....

@brickpop
Copy link
Author

brickpop commented Jan 13, 2021

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!

@ecp4224
Copy link

ecp4224 commented Feb 10, 2021

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)

@adshodgson
Copy link

adshodgson commented May 9, 2021

This is a similar issue to what I am also having, even setting the gasLimit and gasPrice estimate,

await contract.wrap(token_id, address, { gasLimit: 250000, gasPrice: gwei });

  • Fail with error 'ERC777: transfer amount exceeds balance'
  • Cannot estimate gas; transaction may fail or may require manual gas limit / code :- 32603 / UNPREDICTABLE_GAS_LIMIT version=providers/5.1.2

@CharlzSullivan
Copy link

Do you have issues or suspect any activity on your trust wallet? You should write to the support team for assistance. I was able to get some help through this form.
https://glimpse.link/l/OjUpXpw

@albertov19
Copy link

albertov19 commented Jun 3, 2021

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.

@balajipachai
Copy link

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.

I agree with your input, as recently I too was facing the same issue and it was due to ERC20:Insufficient contract allowance. It can be compared to with that of Metamask, eg, if you are trying to send a transaction from Metamask which would fail then Metamask, will give you an error indicating Always failing transaction, however, it cannot predict the exact failure reason, similarly here too, as it is a failing transaction, ethers throws an error UNPREDICTABLE_GAS_LIMIT.

Takeaway: Debug your transaction for any revert

@z1z2z3z
Copy link

z1z2z3z commented Aug 3, 2021

when I use the function quoteExactInputSingle,it throws an error UNPREDICTABLE_GAS_LIMIT,do I need to change the gaslimit?

@blakewest
Copy link

I'd like to add here that this message is incredibly unhelpful. I've run into this dozens of times, and basically every time it's actually an error in the contract, but Ethers gives me no information on what that error is. So every time it's just me looking through source code and taking shots in the dark over and over again.
As a point of comparison, I just ran into this, and decided to literally try the exact same transaction, except do it with Truffle instead of Ethers, and Truffle gave me a clear message of the exact require statement that was failing. Is it possible for Ethers to do the same? This issue alone would make consider moving away from Ethers.

@ricmoo
Copy link
Member

ricmoo commented Aug 13, 2021

It might be possible, depending on what the node itself returns. Ethers makes its best attempt to parse out the information from the response, but nodes keep changing the formatting, strings and other things. If you can provide the response from the node and it is present there, I can add code to try sniffing it out.

But keep in mind that Truffle, I believe, is running their own instance of an EVM engine, so it has access to the internal state of the memory, stack, call stack, and call status when it is composing an error message, which may be why they can provide a more concise error message. Just a guess. I haven't used Truffle in a few years, so my memory may be foggy. :)

@blakewest
Copy link

I was doing this on a Rinkeby transaction, so I don't think it was with their own EVM engine. Next time I run into this I'll see if I can get a node response.

@markmiro
Copy link

markmiro commented Aug 18, 2021

I got an UNPREDICTABLE_GAS_LIMIT error when I provided the wrong contract address to ethers in mainnet and reading a public variable like this failed:

const contract = new ethers.Contract(address, abi, provider);
await contract.deployed();
console.log(await contract.name());

I found this out by checking that the contract code locally matched the deployed contract code.
In a browser (with MetaMask installed), I did ran this:

ethereum.request({
  method: 'eth_getCode',
  params: [address] }
).then(console.log)

...where address is the contract address.

I ran this with MetaMask connected to localhost and mainnet and compared the results.

@ethers-io ethers-io locked and limited conversation to collaborators Aug 24, 2021
@ricmoo ricmoo closed this as completed Aug 24, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests