Search code examples
soliditytruffle

"revert ERC20: transfer amount exceeds allowance" error when transferring from a smart contract


I have a smart contract named "Manager" with a "claimRewards" method, that will be called by users and should transfer to them some ERC20 tokens. The ERC20 is another smart contract named "rdk".

 function claimRewards() public {
        uint256 howMany = calculateRewards(msg.sender);
        if (howMany > 0) {
            // does the contract wallet have tokens enough?
            uint256 walletBalance = rdk.balanceOf(address(this));

            emit Log(walletBalance, howMany);

            require (walletBalance >= howMany, "empty wallet");

            // transfer tokens from contract wallet to player
            rdk.transferFrom(address(this), msg.sender, howMany); 
        }
}

The ERC20 tokens are in the smart contract wallet. The event "Log" shows these values:

Manager.Log(
      balance: 101000 (type: uint256),
      howMany: 9 (type: uint256)
    )

So there are 101000 tokens on the smart contract wallet, and it has to transfer 9 to msg.sender

Before calling the method "claimRewards" I approve the smart contract to spend 100000 of those tokens:

it("Should transfer tokens from operator for rewards ", async function () {
    await token.approve(manager.address, 100000, { from: accounts[1] });
    await token.transfer(manager.address, 100000, { from: accounts[1] });
  });

But when running my tests on Truffle, I got the error: Error: Returned error: VM Exception while processing transaction: revert ERC20: transfer amount exceeds allowance -- Reason given: ERC20: transfer amount exceeds allowance.

I cannot understand if the smart contract "Manager" is the owner of 100000 RDK tokens, and even has been approved for spending 100000 RDK tokens, why is showing that error when transferring 9 of those tokens to the caller of the method.

Anyone could help me with this issue?


Solution

  • await token.approve(manager.address, 100000, { from: accounts[1] });
    

    This JS snippet approves the Manager to spend accounts[1]'s tokens.

    rdk.transferFrom(address(this), msg.sender, howMany); 
    

    But this Solidity snippet checks for approval from the Manager address (effectively the msg.sender in the rdk contract) to the same Manager address (1st argument of address(this)).


    If you want to transfer tokens from the Manager contract, you can simply invoke the token's transfer() function in the Manager contract.

    // transfer `howMany` of `rdk` tokens
    // from the `Manager` address
    // to the user invoking the `claimRewards()` function
    
    rdk.transfer(msg.sender, howMany); 
    

    This will not check the allowance but only the sender (Manager) balance.