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'
14 Likes

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...

2 Likes

I'm trying to do this use case, and I'm getting this reversion, and it's not clear to me why....
I have pre-approved the contract so I can transfer, and I have a balance of the token I use...
any suggestions?

transact to myToken.buyToken errored: Returned error: Error: VM Exception while processing transaction: reverted with reason string 'ERC20: transfer amount exceeds balance'

thanks