Error using ERC20 transferFrom in a buy function selling tokens

I’m having difficulty making a function that is both payable and includes the ERC20 transerFrom method.

I’ve created contracts/Coin.sol:

pragma solidity ^0.6.1;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Coin is ERC20 {
  constructor(uint _initialSupply) public ERC20("RandomCoin", "RC") {
      _mint(msg.sender, _initialSupply);
  }
}

and then contracts/CoinDeploy.sol:

pragma solidity ^0.6.1;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract CoinDeploy {
    ERC20 private coin;
    address payable public beneficiary;
    event DoneStuff(address from);

    constructor(ERC20 _coin) public {
        coin = _coin;
        beneficiary = payable(address(this));
    }

    function buyCoin(address _receiver, uint256 _amount) external payable {
        coin.transferFrom(msg.sender, _receiver, _amount);
        emit DoneStuff(_receiver);
    }
}

and I migrate this with migrations/2_deploy_contract.js

var Coin = artifacts.require("Coin");
var CoinDeploy = artifacts.require("CoinDeploy");

module.exports = function(deployer) {
  deployer.deploy(Coin, 1000).then(function() {
		return deployer.deploy(CoinDeploy, Coin.address);
	});
};

Entering the truffle console I run the following:

token = await Coin.deployed()
receiver = await CoinDeploy.deployed()
token.increaseAllowance(receiver.address, 1000) 

Picking a random address from Ganache I try and buy a Coin for 1 ETH:

receiver.buyCoin("0xE82413334b868122f627eD03157C08dBBD3b2378", 7, {from: "0xE82413334b868122f627eD03157C08dBBD3b2378", value: 1000000000000000000})

this fails with a message “ERC20: transfer amount exceeds balance.”

However if I enter:

receiver.buyCoin("0xE82413334b868122f627eD03157C08dBBD3b2378", 7)

the transaction goes through without problem.

I understand why this is failing. In the first example as I am explicitly sending Eth from a specific account the msg.sender becomes the account I am sending Eth from. And the buyCoin uses msg.sender as the from address in the the transferFrom. And that account has no ERC20, so failure.

In the second example the msg.sender as no Eth is specified, defaults to the first address in Ganache; which is the account that holds the ERC20. So success.

The solution to get the first example to work is therefore obvious (?): change transferFrom so that the first argument refers to the account that currently holds the ERC20, rather than msg.sender. I’ve tried all sorts of combinations: tx.origin, address(coin), coin.address, etc. But none of them work.

Very grateful for any help.

:computer: Environment

Truffle v5.1.30 (core: 5.1.30)
Solidity - ^0.6.0 (solc-js)
Node v14.4.0
Web3.js v1.2.1
OS: Ubuntu 20.04

1 Like

Hi @PreciousChicken,

transferFrom :

  • sender must have a balance of at least amount.
  • the caller must have allowance for sender's tokens of at least amount

Which in your case the sender doesn't have any tokens (except if they deployed the token) as they are the ones buying the tokens.

You need your contract selling tokens to have an amount of tokens to sell. You could transfer tokens from the deployer of the token contract to the selling contract or you could get the CoinDeploy contract to deploy the Coin token contract and then it would be the holder of the total supply.

Your CoinDeploy should then use transfer rather than transferFrom when selling tokens.


Taking a step back, you are effectively creating a Crowdsale for a token. Crowdsales are not part of OpenZeppelin Contracts v3.x but are in OpenZeppelin Contracts v2.x

I suggest looking at the Crowdsale documentation.


As an aside, coin is generally used for the native currency of a blockchain, whilst a token is not the native currency. A blockchain will have one type of coin but potentially lots of different tokens.


Finally I suggest looking at Points to consider when creating a fungible token (ERC20, ERC777)

1 Like

Thanks for your reply, I appreciate it.

So to conclude: in the code above there is no way; at least the way I've structured these contracts; for the 'from' element (currently pointing to msg.sender) to point to the actual holder of the coins in a programmatic way?

Oh, that's interesting. v3 no longer features this way of doing it due to a "decline in their usage and ... complexity." So what would be the v3 way of selling tokens for Ether? Would the preferred way be to do it in just one contract? Or are we just saying that the whole concept of selling tokens for Ether is just so 2017 it isn't the done thing anymore! :slight_smile:

:+1:, I'm just trying to extend my familiarisation here, rather than trying to put anything into production; but it never hurts to be precise with language, so thank you.

1 Like

Correct. transferFrom is used with approve where first a token holder approves an allowance and then calls a smart contract that can use that allowance with transferFrom.

You can still use OpenZeppelin Contracts 2.x to create a crowdsale, though I recommend looking at Points to consider when creating a fungible token (ERC20, ERC777)

You could potentially add your token to a decentralized exchange and provide liquidity. Though a working solution requiring your token should come first.

Distributing tokens to users of your solution is also a way to go.

I think non-fungible tokens (such ERC721) are fun to experiment with. I also like experiments with personal tokens.

1 Like