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

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/GSN/Context.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.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, ERC20Detailed {

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

TokenReceiver.sol

pragma solidity ^0.5.0;

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

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

truffle migrate

Interact

Setup

$ truffle console
truffle(development)> token = await SimpleToken.deployed()
truffle(development)> receiver = await TokenReceiver.deployed()

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

truffle(development)> 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(development)> token.increaseAllowance(receiver.address, 1000) 

Do stuff (requires 1000 tokens)

truffle(development)> receiver.doStuff() 

Check balance

truffle(development)> (await token.balanceOf(receiver.address)).toString()
'1000'
1 Like