Solidity – How to Transfer ERC20 Tokens to Contract on Function Call

metamaskremixsolidityweb3js

I created a Smart Contract, and i want that custom tokens interact with it.

Example:

pragma solidity >=0.7.0 <0.9.0;

contract PaymentInvoiceSplit {
    uint productPrice;

    constructor (uint defaultProductPrice) {
        owner = msg.sender;
        productPrice = defaultProductPrice;
    }

    function payInvoice (uint invoiceId) payable public {
      // Receive Amount
      // Split payment
      // Emit event
    }

    function kill () public onlyOwner() {
        selfdestruct(payable(owner));
    }
}

I'm using MetaMask:

  • Network: Smart Chain
  • Account Imported
  • Custom Token add as asset
  • I have funds on BNB for gas and Custom Token

MetaMask Account and Assets

Then i connected the front-end and try to call contract, but when the contract is called it is using ETH, but i want to use custom token instead:

window.onload = async () => {
    await window.ethereum.enable()

    web3 = new Web3(web3.currentProvider)
    const accounts = await ethereum.request({ method: 'eth_accounts' })

    const paymentInvoiceAbi = [
      {
        "inputs": [
          {
            "internalType": "uint256",
            "name": "invoiceId",
            "type": "uint256"
          }
        ],
        "name": "payInvoice",
        "outputs": [],
        "stateMutability": "payable",
        "type": "function"
      }
    ]

    const paymentInvoiceAddress = '0x'
    const paymentInvoiceContract = new web3.eth.Contract(paymentInvoiceAbi, paymentInvoiceAddress)

    const invoiceId = '123'
    const data = paymentInvoiceContract.methods.payInvoice(invoiceId).send({ from: accounts[0] })
  }

I can transfer funds from custom token to my smart contract, but this way i only can send amount and i can't call a contract method:

window.onload = async () => {
    await window.ethereum.enable()

    web3 = new Web3(web3.currentProvider)
    const accounts = await ethereum.request({ method: 'eth_accounts' })

    const tokenAbi = [
      {
        inputs: [
          {
            internalType: "address",
            name: "recipient",
            type: "address"
          },
          {
            internalType: "uint256",
            name: "amount",
            type: "uint256"
          }
        ],
        name: "transfer",
        outputs: [
          {
            internalType: "bool",
            name: "",
            type: "bool"
          }
        ],
        stateMutability: "nonpayable",
        type: "function"
      }
    ]

    const tokenAddress = '0xa'
    const paymentInvoiceAddress = '0xb'
    const tokenContract = new web3.eth.Contract(tokenAbi, tokenAddress)

    const value = web3.utils.toHex('3000000')
    const data = tokenContract.methods.transfer(paymentInvoiceAddress, value).send({ from: accounts[0] })
  }

There is any way to interact this two contract? Handle my smart contract using a custom token?

My objective is create a smart contract that receive funds from a custom token like BEP20 and handle it with a Smart Contract.

Thanks!

Best Answer

If you want the user to pay with tokens.

Your contract should look like this

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// npm install @openzeppelin-contracts required

contract PaymentInvoiceSplit {
    uint productPrice;

    constructor (uint defaultProductPrice) {
        owner = msg.sender;
        productPrice = defaultProductPrice;
    }

    function payInvoice (uint invoiceId, address token) payable public {

    IERC20 paymentToken = IERC20(token);
    uint256 amountToPay;

      // You must pull the amount
      // The amount must be approved by the account before the transfer

    require(paymentToken.allowance(msg.sender, address(this)) >= amountToPay,"Insuficient Allowance");
    require(paymentToken.transferFrom(msg.sender,address(this),amountToPay),"transfer Failed");

      // At this point you have the amountToPay in the contract
      // Do your stuff
      // Emit event
    }

}

In this example the caller would provide the address of the token he want's to pay with and the function would execute the transfer.

If you have special custom function in your token Contract you will have to interface them.