ERC-20 Testing – Error ‘ERC20: Insufficient Allowance’ During Tests

erc-20-approvesoliditytesting

First I'm deploying Cafe contract and get deployedId from it and then deploy CAFToken contract with deployedId of cafe contract. I've also added deploy script and test case, I guess everything is going correctly but somehow getting ERC20: insufficient allowance error while calling sendToken function. May be I miss something can you please guide me on this.

Cafe Contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

contract Cafe is Ownable {
    address public cafeToken;

    function setCafeToken(address _cafeToken) public onlyOwner {
        cafeToken = _cafeToken;
    }

    //send token to other receipent
    function sendToken(address _receiverAddress, uint _tokenAmount) public {
        require(_tokenAmount > 0, "token should be more than 0");
        IERC20(cafeToken).transferFrom(
            address(this),
            _receiverAddress,
            _tokenAmount
        );
    }

    // Get rewards of user has 10 or more than 10 tokens
    function getRewards() public returns (bool) {
        require(
            IERC20(cafeToken).balanceOf(msg.sender) >= 10,
            "Not enough token for rewards!"
        );
        IERC20(cafeToken).transferFrom(msg.sender, address(this), 10);
        return true;
    }
}

Cafe Token Contract (ERC20 token)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

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

contract CAFToken is ERC20 {
    constructor(
        uint256 initialSupply,
        address cafeAddress
    ) ERC20("Cafe", "CAF") {
        _mint(cafeAddress, initialSupply); // mint intial supply to cafe contract
    }
}

Deploy script:

const { ethers } = require("hardhat");
const hre = require("hardhat");
const delay = (ms) => new Promise((res) => setTimeout(res, ms));

async function main() {
  const Cafe = await hre.ethers.getContractFactory("Cafe");
  const cafeContract = await Cafe.deploy();

  await cafeContract.deployed();

  const Token = await hre.ethers.getContractFactory("CAFToken");
  const cafeToken = await Token.deploy(
    ethers.utils.parseEther("100000"),
    cafeContract.address
  );

  await cafeToken.deployed();

  console.log(`deployed to cafe contract ${cafeContract.address}`);
  console.log("Token deployed to:", cafeToken.address);

  /////////////// Verification ////////////////////
  console.log("Wait for verifying...");
  await delay(5000);

  // verify cafe contract
  await await hre.run("verify:verify", {
    address: cafeContract.address,
    constructorArguments: [],
    contract: "contracts/Cafe.sol:Cafe",
  });

  // verify cafe token
  await await hre.run("verify:verify", {
    address: cafeToken.address,
    constructorArguments: [
      ethers.utils.parseEther("100000"),
      cafeContract.address,
    ],
    contract: "contracts/CAFToken.sol:CAFToken",
  });
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Test case:

const { expect, assert } = require("chai");
const { ethers } = require("hardhat");

describe("Initial Test case", () => {
  let cafeContract, cafeToken, accounts;

  beforeEach(async () => {
    const Cafe = await ethers.getContractFactory("Cafe");
    cafeContract = await Cafe.deploy();

    await cafeContract.deployed();

    const Token = await ethers.getContractFactory("CAFToken");
    cafeToken = await Token.deploy(
      ethers.utils.parseEther("100000"),
      cafeContract.address
    );

    await cafeToken.deployed();

    accounts = await ethers.getSigners();
  });

  describe("first", async () => {
    it("check", async () => {
      // Before transaction initial logs and balances

      console.log("Balance", await cafeToken.totalSupply());
      // Send token to another address
      await cafeContract.setCafeToken(cafeToken.address);

      await cafeToken.allowance(cafeToken.address, cafeContract.address);

      // Approve transaction
      await cafeToken.approve(
        cafeContract.address,
        ethers.utils.parseEther("10000")
      );

      await cafeContract.sendToken(
        accounts[1].address,
        ethers.utils.parseEther("10")
      );

      // await token.transfer();
      console.log("--after transaction--");

      const balance11 = await cafeToken.balanceOf(accounts[1].address);
      console.log("Balance-11", balance11.toString());

      assert.equal(balance11.toString(), ethers.utils.parseEther("10"));
    });
  });
});

Best Answer

You're having this error because you didn't give allowance for your transferFrom to succeeds. transferFrom works with approve.

You can fix this by using transfer or by calling approve before using transferFrom.

Here's how the sendToken function should look:

IERC20(cafeToken).approve(address(this), _tokenAmount);
IERC20(cafeToken).transferFrom(
    address(this),
    _receiverAddress,
    _tokenAmount
);

or simply

   IERC20(cafeToken).transfer(
        _receiverAddress,
        _tokenAmount
    );
Related Topic