4

I'm trying to estimate gas fees of a contract that I'm about to execute. My code is as follows.

import Web3 from "web3";
const web3 = new Web3();

const estimateGasLimit = async (
  fromAddress: string,
  outputAddress: string,
  amount: number,
  gasPrice: number,
  gasLimit: number,
  chain = 1,
  contractAddress: string
) => {
  const contract = new web3.eth.Contract(minABI, contractAddress, {
    from: fromAddress,
  });

  return await contract.methods.transfer(outputAddress, amount).estimateGas();
};

I'm testing for USDT right now, whose contract address is '0xdac17f958d2ee523a2206206994597c13d831ec7'. It works perfectly for amount 1000000, which is 1 USDT as the decimal for USDT is 6, but fails for 10000000 that is 10 USDT. I get the following error

UnhandledPromiseRejectionWarning: Error: Returned error: invalid opcode: opcode 0xfe not defined
    at Object.ErrorResponse (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at /home/gaurav/Work/Revamped-Everything/wallet/node_modules/web3-core-requestmanager/lib/index.js:288:36
    at XMLHttpRequest.request.onreadystatechange (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/web3-providers-http/lib/index.js:98:13)
    at XMLHttpRequestEventTarget.dispatchEvent (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
    at XMLHttpRequest._setReadyState (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
    at XMLHttpRequest._onHttpResponseEnd (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/xhr2-cookies/dist/xml-http-request.js:318:14)
    at IncomingMessage.<anonymous> (/home/gaurav/Work/Revamped-Everything/wallet/node_modules/xhr2-cookies/dist/xml-http-request.js:289:61)
    at IncomingMessage.emit (events.js:327:22)
    at IncomingMessage.EventEmitter.emit (domain.js:506:15)
    at endReadableNT (_stream_readable.js:1220:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)

I am very new at ethereum and blockchain, so I would appreciate any help that I could get. Thank you.

3
  • 1
    Well, do you have 10 USDT in your account??? Commented Dec 5, 2020 at 14:31
  • 1
    Oh dang, I had 9.98. Is that why I was stuck for a day?
    – Gaurav
    Commented Dec 5, 2020 at 14:38
  • Well, that would be the very first thing that comes to mind, in light of the fact that it works successfully with 1 USDT. Commented Dec 5, 2020 at 15:05

1 Answer 1

5

An invalid opcode is typically the result of either one of the following runtime operations:

  1. assert of an expression which evaluates to false
  2. array[i] where i >= array.length
  3. Dividing by zero
  4. Calling a function which receives an enum parameter as input, and passing it a value larger than that enum's maximum value

It is custom to use require in order to verify user input, and assert in order to avoid executing potential bugs in the code.

Therefore, the SafeMath is typically implemented with require and not with assert.

Unfortunately, some earlier implementations of this library have used assert instead of require, and such is the one implemented in the USDT token contract.

Thus, any of the following operations could trigger an invalid opcode:

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        ...
        assert(c / a == b);
        ...
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        ...
        uint256 c = a / b;
        ...
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        ...
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        ...
        assert(c >= a);
        ...
    }

As you can see, these functions are called from function transfer, which you are trying to execute:

    function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
        uint fee = (_value.mul(basisPointsRate)).div(10000);
        if (fee > maximumFee) {
            fee = maximumFee;
        }
        uint sendAmount = _value.sub(fee);
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(sendAmount);
        if (fee > 0) {
            balances[owner] = balances[owner].add(fee);
            Transfer(msg.sender, owner, fee);
        }
        Transfer(msg.sender, _to, sendAmount);
    }

The most typical reason for this function to revert is when it is called with an input amount larger the amount owned by the caller, which causes balances[msg.sender].sub(_value) to revert.

1
  • are those 4 reasons all why we get invalid opcode or are there any other reasons too ? Commented Feb 9, 2021 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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