javascriptsoliditytransfererc20

Calling a solidity method from javascript


This is a pretty noob question which I thought wouldn't take too much time to answer through research. I am working on my first set of dapp building with basic javascript and solidity. Through it, I've been able to connect a wallet and send eth via javascript to a contract having a payable donate function. Yay.

I am now trying to send an ERC20 token to my contract. I have been able to get approval to spend the ERC20 token through my javascript. I am now attempting to send the token to my solidity contract (using ethers) but for the life of me am failing. I have tried every which way I can think of (via google-fu) but am constantly getting " Cannot read properties of undefined" as an error or "TypeError: contract.transferTokens is not a function". Essentially, I can't seem to get it to recognize the function of the contract. This is the full solidity contract:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TransferContract {
    function transferFrom(address recipient, uint256 amount) public {
        address token = 0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7; IERC20(token).transferFrom(msg.sender, recipient, amount);
    }

   function transferTokens(uint256 _amount) public {
       require(_amount > 0);
       uint256 input = _amount; //It's reverting here?? I don't see the issue
       address token = 0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7;
       IERC20(token).transferFrom(msg.sender, 0x4B8C40757A00eD0479e4B8293C61d8178E23d2f1, input);
   }
}

Here is the javascript line where I am trying to get something from it.

const _return = await contract.transferTokens(10000);

Please have mercy on this poor soul as I can't seem to find anything to help me. LOL.

Thank you!


Solution

  • From your Pastebin, there seems to be something wrong with your abi, I recompiled the contract and used the abi produced by hardhat, and now it works!

    Also, I've changed the place where you initialize the signer since getting it when you approve would force anyone who transfers to approve first or the second method won't work, even if that user has BUSD approved from beforehand.

    I couldn't get to run it since I don't have any BUSD or BNB on my acc, but from what I could see, the execution was successful, since I get an error telling me that the transfer amount exceeds the allowance :)

    enter image description here

    This is triggered by calling the busd contract from your contract.

    IERC20(token).transferFrom(
            msg.sender,
            0x4B8C40757A00eD0479e4B8293C61d8178E23d2f1,
            input
        );
    

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js" type="application/javascript"></script>
      <script>
        //const { ethers } = require("ethers");
        const busdABI = [
          {
            "constant": true,
            "inputs": [],
            "name": "name",
            "outputs": [{
              "name": "",
              "type": "string"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "assetProtectionRole",
            "outputs": [{
              "name": "",
              "type": "address"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "decimals",
            "outputs": [{
              "name": "",
              "type": "uint8"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "paused",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "owner",
            "outputs": [{
              "name": "",
              "type": "address"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "symbol",
            "outputs": [{
              "name": "",
              "type": "string"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "betaDelegateWhitelister",
            "outputs": [{
              "name": "",
              "type": "address"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "proposedOwner",
            "outputs": [{
              "name": "",
              "type": "address"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "EIP712_DOMAIN_HASH",
            "outputs": [{
              "name": "",
              "type": "bytes32"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "supplyController",
            "outputs": [{
              "name": "",
              "type": "address"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "inputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "constructor"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "from",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "to",
              "type": "address"
            },
            {
              "indexed": false,
              "name": "value",
              "type": "uint256"
            }],
            "name": "Transfer",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "owner",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "spender",
              "type": "address"
            },
            {
              "indexed": false,
              "name": "value",
              "type": "uint256"
            }],
            "name": "Approval",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "currentOwner",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "proposedOwner",
              "type": "address"
            }],
            "name": "OwnershipTransferProposed",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldProposedOwner",
              "type": "address"
            }],
            "name": "OwnershipTransferDisregarded",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldOwner",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "newOwner",
              "type": "address"
            }],
            "name": "OwnershipTransferred",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [],
            "name": "Pause",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [],
            "name": "Unpause",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "addr",
              "type": "address"
            }],
            "name": "AddressFrozen",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "addr",
              "type": "address"
            }],
            "name": "AddressUnfrozen",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "addr",
              "type": "address"
            }],
            "name": "FrozenAddressWiped",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldAssetProtectionRole",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "newAssetProtectionRole",
              "type": "address"
            }],
            "name": "AssetProtectionRoleSet",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "to",
              "type": "address"
            },
            {
              "indexed": false,
              "name": "value",
              "type": "uint256"
            }],
            "name": "SupplyIncreased",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "from",
              "type": "address"
            },
            {
              "indexed": false,
              "name": "value",
              "type": "uint256"
            }],
            "name": "SupplyDecreased",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldSupplyController",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "newSupplyController",
              "type": "address"
            }],
            "name": "SupplyControllerSet",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "from",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "to",
              "type": "address"
            },
            {
              "indexed": false,
              "name": "value",
              "type": "uint256"
            },
            {
              "indexed": false,
              "name": "seq",
              "type": "uint256"
            },
            {
              "indexed": false,
              "name": "fee",
              "type": "uint256"
            }],
            "name": "BetaDelegatedTransfer",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldWhitelister",
              "type": "address"
            },
            {
              "indexed": true,
              "name": "newWhitelister",
              "type": "address"
            }],
            "name": "BetaDelegateWhitelisterSet",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "newDelegate",
              "type": "address"
            }],
            "name": "BetaDelegateWhitelisted",
            "type": "event"
          },
          {
            "anonymous": false,
            "inputs": [{
              "indexed": true,
              "name": "oldDelegate",
              "type": "address"
            }],
            "name": "BetaDelegateUnwhitelisted",
            "type": "event"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "initialize",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "initializeDomainSeparator",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [],
            "name": "totalSupply",
            "outputs": [{
              "name": "",
              "type": "uint256"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_to",
              "type": "address"
            },
            {
              "name": "_value",
              "type": "uint256"
            }],
            "name": "transfer",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "balanceOf",
            "outputs": [{
              "name": "",
              "type": "uint256"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_from",
              "type": "address"
            },
            {
              "name": "_to",
              "type": "address"
            },
            {
              "name": "_value",
              "type": "uint256"
            }],
            "name": "transferFrom",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_spender",
              "type": "address"
            },
            {
              "name": "_value",
              "type": "uint256"
            }],
            "name": "approve",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [{
              "name": "_owner",
              "type": "address"
            },
            {
              "name": "_spender",
              "type": "address"
            }],
            "name": "allowance",
            "outputs": [{
              "name": "",
              "type": "uint256"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_proposedOwner",
              "type": "address"
            }],
            "name": "proposeOwner",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "disregardProposeOwner",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "claimOwnership",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "reclaimBUSD",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "pause",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [],
            "name": "unpause",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_newAssetProtectionRole",
              "type": "address"
            }],
            "name": "setAssetProtectionRole",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "freeze",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "unfreeze",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "wipeFrozenAddress",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "isFrozen",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_newSupplyController",
              "type": "address"
            }],
            "name": "setSupplyController",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_value",
              "type": "uint256"
            }],
            "name": "increaseSupply",
            "outputs": [{
              "name": "success",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_value",
              "type": "uint256"
            }],
            "name": "decreaseSupply",
            "outputs": [{
              "name": "success",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [{
              "name": "target",
              "type": "address"
            }],
            "name": "nextSeqOf",
            "outputs": [{
              "name": "",
              "type": "uint256"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "sig",
              "type": "bytes"
            },
            {
              "name": "to",
              "type": "address"
            },
            {
              "name": "value",
              "type": "uint256"
            },
            {
              "name": "fee",
              "type": "uint256"
            },
            {
              "name": "seq",
              "type": "uint256"
            },
            {
              "name": "deadline",
              "type": "uint256"
            }],
            "name": "betaDelegatedTransfer",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "r",
              "type": "bytes32[]"
            },
            {
              "name": "s",
              "type": "bytes32[]"
            },
            {
              "name": "v",
              "type": "uint8[]"
            },
            {
              "name": "to",
              "type": "address[]"
            },
            {
              "name": "value",
              "type": "uint256[]"
            },
            {
              "name": "fee",
              "type": "uint256[]"
            },
            {
              "name": "seq",
              "type": "uint256[]"
            },
            {
              "name": "deadline",
              "type": "uint256[]"
            }],
            "name": "betaDelegatedTransferBatch",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": true,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "isWhitelistedBetaDelegate",
            "outputs": [{
              "name": "",
              "type": "bool"
            }],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_newWhitelister",
              "type": "address"
            }],
            "name": "setBetaDelegateWhitelister",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "whitelistBetaDelegate",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "constant": false,
            "inputs": [{
              "name": "_addr",
              "type": "address"
            }],
            "name": "unwhitelistBetaDelegate",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
          }
        ];
        const busdAddress = '0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7';
        const contractAddress = '0x5aaB65E7f34F73e7B1bEd82155aac332E9f1e7C6';
        const abi = [
          {
            "inputs": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "uint256",
                "name": "amount",
                "type": "uint256"
              }
            ],
            "name": "transferFrom",
            "outputs": [],
            "stateMutability": "nonpayable",
            "type": "function"
          },
          {
            "inputs": [
              {
                "internalType": "uint256",
                "name": "_amount",
                "type": "uint256"
              }
            ],
            "name": "transferTokens",
            "outputs": [],
            "stateMutability": "nonpayable",
            "type": "function"
          }
        ];
    
        let provider;
        let signer;
        let busdContract;
        let contract;
    
        async function connect() {
          if (typeof window.ethereum !== "undefined") {
            window.ethereum.request({
              method: "wallet_addEthereumChain",
              params: [{
                chainId: "0x61",
                rpcUrls: ["https://data-seed-prebsc-1-s1.binance.org:8545"],
                chainName: "Testnet BSC",
                nativeCurrency: {
                  name: "BNB",
                  symbol: "BNB",
                  decimals: 18
                },
                blockExplorerUrls: ["https://testnet.bscscan.com"]
              }]
            });
    
            try {
              await ethereum.request({ method: "eth_requestAccounts" });
            } catch (error) {
              console.log(error);
            }
    
            document.getElementById("connectButton").innerHTML = "Connected";
            const accounts = await ethereum.request({ method: "eth_accounts" });
            document.getElementById("connectButton").innerHTML = accounts;
    
            // Setting the signer here allows both functions to be accessed individually.
            provider = new ethers.providers.Web3Provider(window.ethereum);
            signer = provider.getSigner();
    
            console.log(accounts);
    
          } else {
            document.getElementById("connectButton").innerHTML =
              "Please install MetaMask";
          }
        }
    
        async function transferBUSD() {
          console.log(transferBUSD);
          if (typeof window.ethereum !== "undefined") {
            try {
              busdContract = new ethers.Contract(busdAddress, busdABI, signer);
    
              const amount = ethers.utils.parseEther('0.02');
              busdContract.approve(contractAddress, amount);
    
              contract = new ethers.Contract(contractAddress, abi, signer);
            } catch (error) {
              console.error(error.message, error.data.message);
            }
          }
          else {
            document.getElementById("transferButton").innerHTML = "Please install MetaMask";
          }
        };
    
        async function transfer2BUSD() {
          console.log("transfer2BUSD");
          if (typeof window.ethereum !== "undefined") {
            contract = new ethers.Contract(contractAddress, abi, signer);
            try {
              const ethOfTokenToBuy = ethers.utils.parseEther('0.02');
              const _return = await contract.transferTokens(10000);
              console.log(_return);
            } catch (error) {
              console.error(error.message, error.data.message);
            }
          }
          else {
            document.getElementById("transferButton2").innerHTML = "Please install MetaMask";
          }
        };
      </script>
    </head>
    
    <body>
      <button id="connectButton" onclick="connect()">Connect</button>
      <button id="transferButton" onclick="transferBUSD()">Transfer</button>
      <button id="transferButton2" onclick="transfer2BUSD()">Transfer 2</button>
    </body>
    
    </html>

    This won't work properly on the snippet, so copy it and try it.

    Now, because i don't any BUSD or BNB I couldn't test your deployed contract function, that would be up to you.