I get this error when doing a transaction through Metamask and a NextJS application using this Solidity contract:

The error:

Error: cannot estimate gas; transaction may fail or may require manual gas limit 
[ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] 
(reason="execution reverted", method="estimateGas", transaction=
"data":"0xde905f830000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c99da3a663604c43f6296f3f95e9ea7b6481f01f000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000143956764a6a6c33674a726f6b6762767a456d4e64000000000000000000000000","accessList":null}, error={"code":-32603,"message":"Internal JSON-RPC error.","data":{"code":-32000,"message":"execution reverted"},"stack":"{\n  \"code\": -32603,\n  \"message\": \"Internal JSON-RPC error.\",\n  \"data\": {\n    \"code\": -32000,\n    \"message\": \"execution reverted\"\n  }

Solidity Code:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

// Import this file to use console.log
import "hardhat/console.sol";

contract CryptoPayments {
    using SafeMath for uint256;

    uint256 public ownerPercentage;
    address payable public owner;

    event NewPayment(string txId, address from, address to, address token, uint256 when);

    constructor() payable {
        owner = payable(msg.sender);
        ownerPercentage = 5;

    function createPayment(string memory txId, address payable seller, address token) public payable {    
        uint256 ownerPortion = msg.value.mul(ownerPercentage).div(100);
        uint256 sellerPortion = msg.value - ownerPortion;

        IERC20 _token = IERC20(token);
        _token.transferFrom(msg.sender, address(this), msg.value);

        _token.transfer(owner, ownerPortion);
        _token.transfer(seller, sellerPortion);

        emit NewPayment(txId, msg.sender, seller, token, block.timestamp);

    function setOwnerPercentage(uint256 percentage) public {
        require(msg.sender == owner, "You aren't the owner");

        ownerPercentage = percentage;

    function setOwner(address newOwner) public {
        require(msg.sender == owner, "You aren't the owner");
        owner = payable(newOwner);

JS code responsible for the execution:

const [ token, setToken ] = useState({
    bnb: {
        name: 'WBNB',
        selected: false,
        address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"

const contractAddr = "<CONTRACT_ADDR_HERE>"
const trxData = {seller_wallet_address: "<WALLET_ADDRESS_HERE>"}

async function initiatePayment(){
   const provider = new ethers.providers.Web3Provider(window.ethereum)
   const signer = provider.getSigner()

   let tokenAbi = ["function approve(address _spender, uint256 _value) public returns 
   (bool success)"];
   let tokenContract = new ethers.Contract(token['bnb'].address, tokenAbi, signer);
   await tokenContract.approve(contractAddr, value);

   const contract = new ethers.Contract(contractAddr, ctrct.abi, signer)
   const val = await contract.createPayment(id, trxData.seller_wallet_address, 
   token['bnb'].address, {value});

  • Why are you using payable modifier but later using ERC20 transfer?
    – Ismael
    Aug 6 at 16:50
  • I'm trying to get some amount of tokens inside the contract and then transfer them to two wallets. I'm not sure this is the correct way Aug 7 at 2:17
  • You don't have to use msg.value if you aren't accepting ether. Add an amount parameter instead. I don't seen an evident error, try debugging step by step, make sure the account has enough token balance to operate.
    – Ismael
    Aug 9 at 2:55


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.