0

I have a basic ERC-20 Token defined as

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

contract Token is ERC20 {
    constructor() ERC20("Token", "TOKEN") {}

    function mint(address account, uint256 amount) public {
        _mint(account, amount);
    }
}

I have another contract Item that has a method I want to cost 1 Token, basing answer off https://ethereum.stackexchange.com/a/78911/30804

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

contract Item {
    IERC20 private token;

    constructor(address tokenAddress) {
        token = IERC20(tokenAddress);
    }

    function myMethod() public {
        bool success = token.transferFrom(msg.sender, address(this), 1 ether); // also tried just 1
        require(success, "Insufficient Funds: Requires 1 Token");
        // do things
    }
}

I'm running this inside a Hardhat test

  const TokenFactory = await ethers.getContractFactory("Token");
  Token = await Token.deploy();
  await Token.mint(owner.address, ethers.utils.parseUnits("1", 18));
  await Token.approve(owner.address, ethers.utils.parseUnits("1", 18));

  const ItemFactory = await ethers.getContractFactory("Item");
  Item = await ItemFactory.deploy(Token.address);
  await Item.myMethod(); // this is the line that errors

Everything runs and I can see debug code from the solidity contracts, but I keep getting an error that

reverted with reason string 'ERC20: insufficient allowance'

that I traced back to the openzeppelin internal function _spendAllowance

After some debugging I thought that the culprit was because I needed to approve, so I added a call to approve but I'm still getting the same error

I'm assuming my issue is something basic can anyone see what it might be? Thanks in advance.

Joshua Ohana
  • 5,613
  • 12
  • 56
  • 112

1 Answers1

1
await Token.approve(owner.address, ethers.utils.parseUnits("1", 18));

You're giving the approval to the owner address, but the actual msg.sender inside the transferFrom() function is the Item address (as the caller of the function is the Item contract).

Solution: Approve the Item address as the spender.

// after the `Item` contract has been deployed
await Token.approve(Item.address, ethers.utils.parseUnits("1", 18));
await Item.myMethod();
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100