Cannot estimate gas

Hello! I am trying to create an autotask that links to a relayer for metatransactions. I am using my custom contract on the Mumbai testnet.
:computer: Environment

AutoTasks
:memo:Details

I am getting a an error saying my gas is unpredictable. The actual code looks like this:

cannot estimate gas; transaction may fail or may require manual gas limit

Here is my autotask code:

const ethers = require('ethers');
const { DefenderRelaySigner, DefenderRelayProvider } = require('defender-relay-client/lib/ethers');

const ForwarderAbi  = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct MinimalForwarder.ForwardRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"gas","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct MinimalForwarder.ForwardRequest","name":"req","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}];
const ForwarderAddress = "0xf78A68a1Ec2950e995bbCdD7129B54e8a9213c74";
const ActiveBatchAddress = "0xd258cE5296C825bAb66E17c3D9CddB45C7fC102D";

async function relay(forwarder, request, signature, whitelist) {
  // Decide if we want to relay this request based on a whitelist
  const accepts = !whitelist || whitelist.includes(request.to);
  if (!accepts) throw new Error(`Rejected request to ${request.to}`);

  // Validate request on the forwarder contract
  const valid = await forwarder.verify(request, signature);
  if (!valid) throw new Error(`Invalid request`);
  
  // Send meta-tx through relayer to the forwarder contract
  const gasLimit = 8000000000;//(parseInt(request.gas)).toString();
  return await forwarder.execute(request, signature, { gasLimit });
}

async function handler(event) {
  // Parse webhook payload
  if (!event.request || !event.request.body) throw new Error(`Missing payload`);
  const { request, signature } = event.request.body;
  console.log(`Relaying`, request);
  
  // Initialize Relayer provider and signer, and forwarder contract
  const credentials = { ... event };
  const provider = new DefenderRelayProvider(credentials);
  const signer = new DefenderRelaySigner(credentials, provider, { speed: 'fast' });
  const forwarder = new ethers.Contract(ActiveBatchAddress, ForwarderAbi, signer);
  
  // Relay transaction!
  const tx = await relay(forwarder, request, signature);
  console.log(`Sent meta-tx: ${tx.hash}`);
  return { txHash: tx.hash };
}

module.exports = {
  handler,
  relay,
}

This is my custom contract, and im calling create function:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/metatx/MinimalForwarder.sol";
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";

/**
 * @title ERC1155Tradable
 * ERC1155Tradable - ERC1155 contract that whitelists an operator address, has create and mint functionality, and supports useful standards from OpenZeppelin,
  like _exists(), name(), symbol(), and totalSupply()
 */
contract ActiveBatch is ERC1155, Ownable, ERC2771Context {
    using SafeMath for uint256;
    uint256 private _currentTokenID = 0;
    mapping(uint256 => uint256) private tokenSupply;
    mapping(uint256 => uint256) private tokenBurnt;
    string baseMetadataURI;
    bool private mintFlag;
    address private admin;
    uint256 _storedInitialSuply;
    event BurnedSupply(uint256 id); // will emit how much is burned so we can store in DB

    function _msgSender()
        internal
        view
        virtual
        override(Context, ERC2771Context)
        returns (address sender)
    {
        sender = ERC2771Context._msgSender();
    }

    function _msgData()
        internal
        view
        virtual
        override(Context, ERC2771Context)
        returns (bytes calldata)
    {
        return ERC2771Context._msgData();
    }

    constructor(
        string memory baseURI,
        MinimalForwarder forwarder,
        address _admin
    ) public ERC2771Context(address(forwarder)) ERC1155(baseURI) {
        _setBaseMetadataURI(baseURI);
        admin = _admin;
    }

    function _setBaseMetadataURI(string memory _newBaseMetadataURI) internal {
        baseMetadataURI = _newBaseMetadataURI;
    }

    function uri(uint256 _tokenID)
        public
        view
        override
        returns (string memory)
    {
        string memory hexstringtokenID;
        hexstringtokenID = Strings.toString(_tokenID);

        return string(abi.encodePacked(baseMetadataURI, hexstringtokenID));
    }

    function totalSupply(uint256 _id) public view returns (uint256) {
        return tokenSupply[_id];
    }

    function totalBurnt(uint256 _id) public view returns (uint256) {
        return tokenBurnt[_id];
    }

    //this is to separate ownership transaction calls, our admin wallet will approve if it passes our bridge inspection
    function approveMint(uint256 _initialSupply, bytes calldata _data)
        external
    {
        require(
            admin == _msgSender(),
            "ActiveBatch#SenderIsAdmin: SENDER_IS_ADMIN"
        );
        mintFlag = true;
        _storedInitialSuply = _initialSupply;
    }

    function create(address _initialOwner, bytes calldata _data)
        external
        onlyOwner
        returns (uint256)
    {
        uint256 _id = _currentTokenID.add(1);
        _currentTokenID++;
        tokenSupply[_id] = _storedInitialSuply;
        _mint(_initialOwner, _id, _storedInitialSuply, _data);
        mintFlag = false;
        return _id;
    }

Is there any other way to set the gas price to a predictable limit? Thanks!

:1234: Code to reproduce

1 Like

Hi @Richard_Zhang, as you have not set a gasLimit on your transaction ethers is trying to estimate the gasLimit for you. Ethers.js fails to estimate the limit as your transaction would revert. My advice would be to determine why your transaction would fail (perhaps the onlyOwner modifier?)

Hi @dylkil , I removed onlyOwner and it still reverted the transaction. I also debugged and found that the code ran up until forwarder.verify(request, signature);, so I'm wondering what does this verify function do? Does it try to run the function and that's why its reverting the transaction?

Here are the logs in that show the errors:

AUTOTASK START
2022-01-06T20:28:47.515Z	INFO	Here
2022-01-06T20:28:47.517Z	INFO	Relaying {
  value: 0,
  gas: 1000000,
  nonce: '0',
  to: '0x8072ACfCDA5A5CAa30bD8b5B0FE2A7a9e977Fde4',
  from: '0x751146151EB6eAa2F723fc11060C0A8e5fEA3976',
  data: '0xa3f697ba000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea3976000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000'
}
2022-01-06T20:28:48.272Z	INFO	HERE2
2022-01-06T20:28:48.272Z	INFO	HERE3
2022-01-06T20:28:49.980Z	ERROR	Invoke Error 	{"errorType":"Error","errorMessage":"cannot estimate gas; transaction may fail or may require manual gas limit (error={\"code\":-32000}, method=\"call\", transaction={\"from\":\"0x41186C22aa796F230d9355F3D1b978Cca3f4534a\",\"to\":\"0xd258cE5296C825bAb66E17c3D9CddB45C7fC102D\",\"data\":\"0xbf5d3bdb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea39760000000000000000000000008072acfcda5a5caa30bd8b5b0fe2a7a9e977fde4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000084a3f697ba000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea3976000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b857c994fe623b484dba161347d8b874b7df53c002cb90bb1f1229da8544f37e727a5582e3a1dff319da4cd0ccc9e2d6512c87faed7d8fd45074572eaeb693de1b00000000000000000000000000000000000000000000000000000000000000\",\"accessList\":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.4.1)","code":"UNPREDICTABLE_GAS_LIMIT","reason":"cannot estimate gas; transaction may fail or may require manual gas limit","error":{"errorType":"Error","errorMessage":"execution reverted","code":-32000,"stack":["Error: execution reverted","    at DefenderRelayProvider.send (/opt/nodejs/node_modules/defender-relay-client/lib/ethers/provider.js:58:31)","    at processTicksAndRejections (internal/process/task_queues.js:97:5)"]},"method":"call","transaction":{"from":"0x41186C22aa796F230d9355F3D1b978Cca3f4534a","to":"0xd258cE5296C825bAb66E17c3D9CddB45C7fC102D","data":"0xbf5d3bdb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea39760000000000000000000000008072acfcda5a5caa30bd8b5b0fe2a7a9e977fde4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000084a3f697ba000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea3976000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b857c994fe623b484dba161347d8b874b7df53c002cb90bb1f1229da8544f37e727a5582e3a1dff319da4cd0ccc9e2d6512c87faed7d8fd45074572eaeb693de1b00000000000000000000000000000000000000000000000000000000000000","accessList":null},"stack":["Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={\"code\":-32000}, method=\"call\", transaction={\"from\":\"0x41186C22aa796F230d9355F3D1b978Cca3f4534a\",\"to\":\"0xd258cE5296C825bAb66E17c3D9CddB45C7fC102D\",\"data\":\"0xbf5d3bdb000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea39760000000000000000000000008072acfcda5a5caa30bd8b5b0fe2a7a9e977fde4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000084a3f697ba000000000000000000000000751146151eb6eaa2f723fc11060c0a8e5fea3976000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b857c994fe623b484dba161347d8b874b7df53c002cb90bb1f1229da8544f37e727a5582e3a1dff319da4cd0ccc9e2d6512c87faed7d8fd45074572eaeb693de1b00000000000000000000000000000000000000000000000000000000000000\",\"accessList\":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.4.1)","    at Logger.makeError (/opt/nodejs/node_modules/@ethersproject/logger/lib/index.js:187:21)","    at Logger.throwError (/opt/nodejs/node_modules/@ethersproject/logger/lib/index.js:196:20)","    at checkError (/opt/nodejs/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:118:16)","    at DefenderRelayProvider.<anonymous> (/opt/nodejs/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:618:47)","    at step (/opt/nodejs/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:48:23)","    at Object.throw (/opt/nodejs/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:29:53)","    at rejected (/opt/nodejs/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:21:65)","    at processTicksAndRejections (internal/process/task_queues.js:97:5)"]}
AUTOTASK COMPLETE

I also added gasLimit to forwarder.verify(request, signature, {gasLimit}). But still giving the same error

I also debugged and found that the code ran up until forwarder.verify(request, signature); , so I'm wondering what does this verify function do?

This function is marked view so it does not require any gasLimit as no transaction will be sent, you are using this function to check that your request is valid, this should return a boolean. When you log out valid do you get true?

Hi @dylkil , the valid flag returned false.

2022-01-10T18:44:15.756Z	INFO	Promise { <pending> }
2022-01-10T18:44:15.760Z	INFO	HERE2
2022-01-10T18:44:15.760Z	INFO	HERE3
2022-01-10T18:44:17.566Z	INFO	false
2022-01-10T18:44:17.566Z	ERROR	Invoke Error 	{"errorType":"Error","errorMessage":"Invalid request","stack":["Error: Invalid request","    at relay (/var/task/index.js:16:21)","    at processTicksAndRejections (internal/process/task_queues.js:97:5)","    at async Runtime.handler (/var/task/index.js:41:14)"]}
AUTOTASK COMPLETE