'Set gas limit on contract method in ethers.js

Problem

I am trying so use a method of a contract in the testnetwork (ropsten), but it fails due to this error:

reason: 'cannot estimate gas; transaction may fail or may require manual gas limit', code: 'UNPREDICTABLE_GAS_LIMIT'

Code

I created an instance of the smart contract and wanted to call it's register method:

const registrationContract = new ethers.Contract(ADDRESS, abi, signer);
const hashedDomain = utils.keccak256(utils.toUtf8Bytes(domain));

const register = await registrationContract.register(hashedDomain, walletAddress);

Does ethers.js provide a function to set the limit on the contract? Or can this be done otherwise? I didn't find one in the documentation.



Solution 1:[1]

You can set the gas limit with an object as the last argument, for a simple transfer transaction, you could do something like this:

const tx = {
  to: toAddress,
  value: ethers.utils.parseEther(value),
  gasLimit: 50000,
  nonce: nonce || undefined,
};
await signer.sendTransaction(tx);

If you are doing a transaction to a smart contract, the idea is the same, but make sure you set the last parameter in one of your abi methods, example:

const tx = await contract.safeTransferFrom(from, to, tokenId, amount, [], {
  gasLimit: 100000,
  nonce: nonce || undefined,
});

This can fix the UNPREDICTABLE_GAS_LIMIT error, since informing it manually the ethers skips a rpc method call to the provider requesting the calculated gas_limit.

Solution 2:[2]

Complementing the answer, when you define the gasLimit manually it's important to understand:

  1. the value configured is reserved and sent on contract call, so the caller account must have at least that value in his wallet;

  2. of course after the transaction completes the remaining Gas is returned to the caller wallet;

  3. so there is a problem when for the same transaction call, depending for example on the number of arguments, you have a wide range of gas values possibilities, and sometimes the value set is very high and disproportionate for small gas transactions.

So to workaround this problem and set dynamically gasLimit, used the function to estimate the transaction gas (estimateGas) from Ethers and then give an additional margin error percentage.

It could be something like this where gasMargin() calculates the final gas to pass in tx call (in this case just adds 10%).

const gasEstimated = await registrationContract.estimateGas.register(hashedDomain, walletAddress);

const register = await registrationContract.register(hashedDomain, walletAddress), {
      gasLimit: Math.ceil(gasMargin(gasEstimated, 1.1)) 
    });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rodrigo Klosowski
Solution 2 slojg