0

Whenever I the buyTokens function is getting executed, I am getting below error

Reason provided by the contract: "ERC20: transfer amount exceeds allowance". Debug the transaction to get more information.

I am using openzeppelin v3.

Please find attached source code

pragma solidity >=0.4.22 <0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyFirstToken is ERC20, Ownable {
    uint256 rate;
    constructor() public ERC20("MyFirstToken",'MFT'){
        _mint(msg.sender, 1000000000000000000000000);// 1 million tokens
        rate = 1;
    }

    event TokensPurchased(
      address account,
      address token,
      uint amount,
      uint rate
    );

    event TokensSold(
        address account,
        address token,
        uint amount,
        uint rate
    );


    modifier contractHasEnoughBalance() {
        // Calculate the number of tokens to buy
        uint tokenAmount = msg.value * rate;
        // Require that MyFirstToken has enough tokens
        require(balanceOf(this.owner()) >= tokenAmount,"Contract doesn't have enough balance");
        _;
    }

    function buyTokens() public payable contractHasEnoughBalance {
        // Calculate the number of tokens to buy
        uint tokenAmount = msg.value * rate;
        // Transfer tokens to the user
        transferFrom(this.owner(), msg.sender, tokenAmount);
        // // Emit an event
        // emit TokensPurchased(msg.sender, address(this), tokenAmount, rate);
    }

    function sellTokens(uint _amount) public {
        // User can't sell more tokens than they have
        require(balanceOf(msg.sender) >= _amount, "User doesn't have enough balance");
        // Calculate the amount of Ether to redeem
        uint etherAmount = _amount / rate;

        // Require that Contract has enough Ether
        require(address(this).balance >= etherAmount);

        // Perform sale
        transferFrom(msg.sender, address(this), _amount);
        msg.sender.transfer(etherAmount);

        // Emit an event
        // emit TokensSold(msg.sender, address(this), _amount, rate);
    }

}
4
  • how much did you send?
    – haxerl
    Aug 16 '20 at 12:54
  • I have sent 1 eth Aug 16 '20 at 12:54
  • 1
    i dont see you have set the contract owner. The reason you get that error because the owner of the contract doesn't have enough token. Maybe you should set the contract owner at constructor. Or try send 1 wei
    – haxerl
    Aug 16 '20 at 13:06
  • Sure I will check and get back to you Aug 16 '20 at 13:36
1

TL;DR: If you want to get your setup to work as it currently is, you will need to call the approve function of your deployed ERC20 contract from the msg.sender account displayed above, with the contract address of what you have deployed as the spender (you will also need to set the owner in the constructor), however, it would probably be better to mint the tokens into the buyer's account or to mint the buyable portion of the tokens into the contract itself in the constructor (address(this) instead of msg.sender in the constructor) and then to use the transfer function in the buyTokens function instead of transferFrom, (i.e. the only way to make transferFrom work is by setting a positive allowance from the account that owns the tokens, first).

Is there a reason why you are using transferFrom in the buyTokens function? You could probably get away with minting directly into the buyer's account. Just make sure you set a max supply and have a check within your buyTokens function to ensure that the mint won't exceed that max supply. If you really want to "premine" and then run transfers in the buyTokens function, then you should be making use of the transfer function. Using transferFrom is something that you do when you want to transfer tokens on behalf of another account (e.g. getting your smart contract to move tokens which you have in your own wallet address, as such, the ERC20 contract will first make sure that you have granted the smart contract an allowance to move tokens on your wallet address's behalf. You can see this by checking how in the transferFrom function it checks the _allowances of the msg.sender (your contract address) on behalf of the from parameter (2nd param) of the transferFrom function.

In essence, your contract is checking if it has a positive allowance in order to transfer the tokens to the buyer, but since you are not transferring tokens on behalf of an external smart contract or account, you do not need to use the transferFrom function, but rather the transfer function (however you could also just mint tokens directly into the buyer's account from within the buyTokens smart contract, just make sure to check that it doesn't exceed whatever you set the max supply to).

If you ever need to run a transferFrom, you will need to first call the approve function to grant a positive allowance to a contract or account which is going to be calling the transferFrom on their behalf.

Feel free to ask any questions, I know this can be a bit confusing, it might also help to watch this video because I speak a bit about allowances here (slightly off topic but it might help): https://www.youtube.com/watch?v=0Tdrdbn5jYA

2
  • 1
    Thanks Jay for your explanation, it cleared a lot of doubts regarding smart contracts. I have called mint function on address(this) to pre mine all the tokens, and then buyTokens started working. Aug 18 '20 at 10:57
  • Only a pleasure Aditya, happy that it helped! Keep up the great work!
    – Jay
    Aug 18 '20 at 11:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.