Example on how to use ERC20 token in another contract

When an ERC20 token holder interacts with another contract using the token, two transactions are required:

  1. The token holder calls approve to set an allowance of tokens that the contract can use. (assuming an OpenZeppelin ERC20 implementation can use increaseAllowance)
  2. The token holder calls the contract to perform an action and the contract can transferFrom an amount of tokens within the set allowance.

The receiving contract can use IERC20 to interact with the tokens ERC20 functions.

The example below includes a SimpleToken ERC20 contract and a TokenReceiver contract (that uses the token), along with the migrations script for truffle to deploy the contracts and some sample interactions using truffle console.

SimpleToken.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

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

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is Context, ERC20 {

    /**
     * @dev Constructor that gives _msgSender() all of existing tokens.
     */
    constructor () public ERC20("SimpleToken", "SIM") {
        _mint(_msgSender(), 10000 * (10 ** uint256(decimals())));
    }
}

TokenReceiver.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title TokenReceiver
 * @dev Very simple example of a contract receiving ERC20 tokens.
 */
contract TokenReceiver {

    IERC20 private _token;

    event DoneStuff(address from);

    /**
     * @dev Constructor sets token that can be received
     */
    constructor (IERC20 token) public {
        _token = token;
    }

    /**
     * @dev Do stuff, requires tokens
     */
    function doStuff() external {
        address from = msg.sender;

        _token.transferFrom(from, address(this), 1000);

        emit DoneStuff(from);
    }
}

2_deploy.js

// migrations/2_deploy.js
const SimpleToken = artifacts.require('SimpleToken');
const TokenReceiver = artifacts.require('TokenReceiver');

module.exports = async function (deployer) {
  await deployer.deploy(SimpleToken);
  const token = await SimpleToken.deployed();

  await deployer.deploy(TokenReceiver, token.address);
};

Deploy

npx truffle develop
...
truffle(develop)> migrate

Compiling your contracts...
===========================
...

Starting migrations...
======================
> Network name:    'develop'
...

Interact

Setup

truffle(develop)> token = await SimpleToken.deployed()
undefined
truffle(develop)> receiver = await TokenReceiver.deployed()
undefined

Attempt to do stuff (requires 1000 tokens but no allowance approved)

truffle(develop)> receiver.doStuff()
Thrown:
{ Error: Returned error: VM Exception while processing transaction: revert ERC20: transfer amount exceeds allowance -- Reason given: ERC20: transfer amount exceeds allowance.
...

Approve allowance for receiver contract (for non-OpenZeppelin ERC20 implementations you could call token.approve(receiver.address, 1000))

truffle(develop)> token.increaseAllowance(receiver.address, 1000)

Do stuff (requires 1000 tokens)

truffle(develop)> receiver.doStuff()

Check balance

truffle(develop)> (await token.balanceOf(receiver.address)).toString()
'1000'
12 Likes
Approve ERC20 token on another contract
How transferFrom can be used with addresses other than the contract’s own?
How ERC20 approve function works?
Stake contract test but fail to transfer LPtoken to stake
Cannot upgrade ERC20
Deploy BEP token to BSC
Using ERC20 transferFrom on Remix
Using ERC20 transferFrom on Remix
What is the interface IERC20 contract used for vs implementation ERC20 contract
Address with default unlimited allowance
Override approve in OpenZeppelin Contracts version 2.x
Contracts holding tokens
Example of withdrawing an ERC20 from another contract
Approve USDC using IERC20.approve
Extending ERC20PresetMinterPauser
Approve USDC using IERC20.approve
ERC20: Transferring token from another smart contract errors with transfer amount exceeds balance
Deploy BEP token to BSC
Can't burnFrom using ERC20PresetMinterPauser
Member “increaseAllowance” not found or not visible after argument-dependent lookup in contract IERC20
ERC20 token transferFrom failed even successfully doing approve().reason: 'invalid address (argument="address", value=1000, code=INVALID_ARGUMENT, version=address/5.0.5)'
Getting a "revert" processing transaction on "transfer" call
ERC20 mint on payment of ERC20 token
ERC20 Transfer failing
Allowance error in erc20 token receive contract
Uniswap: transferFrom error Dai/insufficient-allowance
Transfer ERC20 on deploy (truffle migrate)
How to use SafeERC20?
transferFrom fails with error 'Dai/insufficient-allowance'
Transfer and Approve of ERC20 tokens
Using approve to allow transfer of ERC20 tokens in a contract
Uniswap: transferFrom error Dai/insufficient-allowance
Integrate DAI payments in existing contract
Call to Trust.tokensend errored: VM error: invalid opcode
Revert when using ERC20 transferFrom in a contract
ERC20: transfer amount exceeds balance
Token swap function 'Fail with error ‘SafeERC20: low-level call failed’
How to transfer an external ERC20 token with IERC20?
"Dai/insufficient-allowance" on transferFrom

2 posts were split to a new topic: How transferFrom can be used with addresses other than the contract’s own?

A post was split to a new topic: Request example of how to withdraw ERC20 token

A post was split to a new topic: ERC20 transferFrom Returned error: sender account not recognized

2 posts were split to a new topic: How to create a school fees payment system using a custom ERC20 token

A post was split to a new topic: Member “increaseAllowance” not found or not visible after argument-dependent lookup in contract IERC20

I’m trying to transfer a specific token (e.g.) a specific smart contract (0x…) to my contract. How will I be able to achieve that

Hi, welcome! :wave:

Emmm, just call token.approve(contract_address, approval_amount), and in the contract function, it should call token.transferFrom(your_address, contract_address, transfer_amount)

token1 is the erc20 token. token2 is my smart contract. i have increased allowance in token1 for token2(smart contract) address. I can see the increase allowance method triggered in token1. Now when I’m trying to invoke token1.transferFrom() from my smart contract(token2) I get

Uncaught { code: -32000, message: ‘nonce too low’ }

hi dear i tested on testnet work fine but on mainnet my usdt can’t withdraw
after call contract functon withdraw on on mainnet usdt token on tron i see error
“REVERT opcode executed. Message: *SafeERC20: ERC20 operation did not succeed”
my contract is here



  pragma solidity ^0.8.0;

  import "../github/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

  contract Invest {
    
    using SafeERC20 for IERC20;
    
    uint256 public target = 1000 * (10**6);
    
    address public owner;
    
    constructor(uint256 _target){
        target = _target;
        owner = msg.sender;
    }
    
    function withdraw(IERC20 token, address to) external isOwner {
        uint256 balance = token.balanceOf(address(this));
        require(balance >= target,"balance is low");
        transferToken(token, to, balance);
    }
    
    function setTarget(uint256 newTarget) external isOwner {
        require(newTarget > target,'new target must be greater current');
        target = newTarget;
    }
    
    modifier isOwner() 
    {
        require(msg.sender == owner,'only owner can do it');
        _;
    }
    
    function transferToken(IERC20 token, address to, uint256 amount) private {
        token.safeTransfer(to, amount);
    }
    
}

Hi bro thanks for the lovely guide your information is really valuable.Through IERC20 interface I mange to interact with Erc20 token from another contract but there is still one confusion left.
I am working on smart contract in which user can stake my token(token is already deploy in network) in smart contract and when the stacking duration ends stakeholder can get their staking amount along with rewarded token.For rewarded tokens
i want to mint new tokens to give reward to the stakeholder in my smart contract.
How can I use mint function in another smart contract.
I want to use Erc20 mint function in my stacking smart contract.
Waiting for your positive response

Just like the error message, you should use a correct nonce of your account to send transaction, if you use web3.js, I think you can use this method to have a check for the current actual nonce:
gettransactioncount

Could you please share the failed transaction hash?

Hi, welcome! :wave:

Maybe you can use this contract AccessControl.sol to set permission for the contract. For more details, you can have a look at this documentation: Access Control - OpenZeppelin Docs

This is a test token and I want to transfer this token to another account.

This is a smart contract.

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

contract TestContract {
using SafeERC20 for IERC20;
using SafeMath for uint256;

IERC20 testToken = IERC20(0x7692cDc12b188E73bA299e93DBf4FC54700a7391);

function otherTokenApprove(address _to, uint256 _amount) public{
      testToken.safeApprove(_to,_amount);
  }
  
  function sendOtherToken(address _to,uint256 _amount) public payable{
      require(testToken.balanceOf(msg.sender) != 0, "sender have not test USDT");
      testToken.safeTransferFrom(msg.sender,_to, _amount);
  } 
}

I called otherTokenApprove() and tried to call sendOtherToken(), but I got an error

execution reverted: SafeERC20: low-level call failed { "originalError": { "code": 3, "data": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000205361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564", "message": "execution reverted: SafeERC20: low-level call failed" } }

What do you think is the cause?

I tried this code on remix it doesn't work, I think transferFrom function needs to come with approve
Lỗi:
provided by the contract: "ERC20: transfer amount exceeds allowance".

A post was merged into an existing topic: Verify ERC20 token on Etherscan that was deployed through Remix: Step by Step Guide

Hello! I'm currently working on my smart contract base agricultural cooperative and I have following problem:

I deploy ERC20 token (OpenZeppelin implementation@4.0.0). And in deploy phase I call _mint and assignee 1000 COOP tokens to deployer address (msg.sender).

Now I deploy my second (main) SmartCOOP contract to other address and inside that contract I insatiate COOPToken (ERC20) with idea to call that external contract from my main contract and to award agricultural producer when they deposit some fruits to COOP warehouse.

ERC20 private coopToken;

constructor(address erc20ContractAddress) {

    coopToken = ERC20(erc20ContractAddress);

} 

When user connect with their MetaMask wallet to dapp front-end and they submit let`s say 20kg of fruits I'm trying to call COOPToken (ERC20) external contract functions (transfer, transferFrom, approve) and to award them for doing so.

Now problem:

But because they are connect msg.sender is automatically assigned to their account and in 4.0.0 release ERC20 implementation transfer function and approve function sender is automatically set with msg.sender (in my context user connected) and I need to set that argument to owner() of first CoopToken (the one who deploy first contract and who have tokens)

Question: How I can make transfer from within my main contract (Coop) by calling external (COOPTROken ERC20) and transfer tokens to currently connected user?

All this having in mind limitation on first argument in transfer and approve functions of OpenZeppelin ERC20 implementation which is set to msg.sedner (in my case currently connected user) and not to owner() the one who deploy Contract and have initial supply of tokens?

All this I would love to do without changing proposed OpenZepeplin implementation.

Does someone have idea or recommendation?

Thanks in advance!
Ilija

Problem solved!

Thanks in any case...

1 Like