Approve function not working

Hey guys, I have 2 contracts. The first one is the RoyaltiesToken.sol where I mint "N" tokens ERC-20 to the sender account. The second one is the MyNFT.sol which is a ERC-721 tokens where another account deploy it creating a new NFT. These are represented below:

RoyaltiesToken.sol

pragma solidity ^0.8.0;

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

contract RoyaltiesToken is ERC20 {
    constructor() ERC20("My Royaltie token", "MRT") {
        _mint(msg.sender, 200000 * 10**18);
    }
}

MyNFT.sol

pragma solidity ^0.8.0;

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

contract MyNFT is ERC721 {
    address public artist;
    address public admin = 0x3E123Cb3a81E22f4DC4152ad8dBfc30D21984A7e;
    address public txFeeTokenRoyaltie;
    uint256 public txFeeAmountArtist;
    uint256 public txFeeAmountPlatform;
    string public tokenURI;

    mapping(address => bool) public excludedList; // list manipulated by artist to set who need and who does not need to pay royalties

    constructor(
        address _txFeeTokenRoyaltie,
        uint256 _txFeeAmountArtist,
        uint256 _txFeeAmountPlatform,
        string memory _tokenURI
    ) public ERC721("MyNFT", "NFT") {
        artist = msg.sender;
        txFeeTokenRoyaltie = _txFeeTokenRoyaltie;
        txFeeAmountArtist = _txFeeAmountArtist;
        txFeeAmountPlatform = _txFeeAmountPlatform;

        tokenURI = _tokenURI;

        _mint(artist, 0);
    }

    function setExcluded(address excluded, bool status) external {
        require(msg.sender == artist, "Artist only");
        excludedList[excluded] = status;
    }

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public override {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );
        if (excludedList[from] == false) {
            _payTxFeeArtist(from);
        }
        _payTxFeeAdmin(from);
        _transfer(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public override {
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public override {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );
        if (excludedList[from] == false) {
            _payTxFeeArtist(from);
        }
        _payTxFeeAdmin(from);
        _safeTransfer(from, to, tokenId, _data);
    }

    function _payTxFeeArtist(address from) internal {
        IERC20 tokenArtist = IERC20(txFeeTokenRoyaltie);
        tokenArtist.transferFrom(from, artist, txFeeAmountArtist);
    }

    function _payTxFeeAdmin(address from) internal {
        IERC20 tokenAdmin = IERC20(txFeeTokenRoyaltie);
        tokenAdmin.transferFrom(from, admin, txFeeAmountPlatform);
    }
}

My problem is when I try to transferFrom the NFT and pay royalties with the ERC-20. Below, you can check how I wrote my test.

Test.js


const RoyaltiesToken = artifacts.require("RoyaltiesToken");
const MyNFT = artifacts.require("MyNFT");
const assert = require("assert");

before(async () => {
    royaltiesToken = await RoyaltiesToken.new();
});

contract("RoyaltiesToken and NFT Token", async (accounts) => {
    const admin = accounts[0];
    const artist = accounts[1];
    const owner1 = accounts[2];
    const owner2 = accounts[3];
    let mynft;
    let txFee = BigInt(10 ** 18);

    let balanceAdmin;
    let balanceArtist;
    let balanceOwner1;
    let balanceOwner2;

    it("Admin should transfer RoyaltieTokens to artist, owner1 and owner2", async () => {
        await royaltiesToken.transfer(artist, BigInt(500 * 10 ** 18));
        await royaltiesToken.transfer(owner1, BigInt(500 * 10 ** 18));
        await royaltiesToken.transfer(owner2, BigInt(500 * 10 ** 18));

        balanceAdmin = await royaltiesToken.balanceOf(admin);
        balanceArtist = await royaltiesToken.balanceOf(artist);
        balanceOwner1 = await royaltiesToken.balanceOf(owner1);
        balanceOwner2 = await royaltiesToken.balanceOf(owner2);

        assert.equal(balanceAdmin.toString(), "198500000000000000000000");
        assert.equal(balanceArtist.toString(), "500000000000000000000");
        assert.equal(balanceOwner1.toString(), "500000000000000000000");
        assert.equal(balanceOwner2.toString(), "500000000000000000000");
    });

    it("Artist should create an NFT contract", async () => {
        mynft = await MyNFT.new(royaltiesToken.address, txFee, txFee, "test", {
            from: artist,
        });

        let artist_address = await mynft.artist.call();
        let ownerNFT = await mynft.ownerOf(0);

        assert(ownerNFT, artist);
        assert(artist_address, artist);
    });

    it("Artist should transfer NFT to owner1 and pay royalties", async () => {
        await mynft.setExcluded(artist, true, { from: artist });

        await royaltiesToken.approve(artist, txFee, { from: admin });

        let allowance = await royaltiesToken.allowance(admin, artist);
        assert.equal(allowance.toString(), "1000000000000000000");

        **await mynft.transferFrom(artist, owner1, 0, { from: artist });**

        let ownerNFT = await mynft.ownerOf(0);

        assert(ownerNFT, owner1);

        balanceAdmin = await royaltiesToken.balanceOf(admin);
        balanceArtist = await royaltiesToken.balanceOf(artist);
        balanceOwner1 = await royaltiesToken.balanceOf(owner1);
        balanceOwner2 = await royaltiesToken.balanceOf(owner2);

        assert.equal(balanceAdmin.toString(), "198600000000000000000000");
        assert.equal(balanceArtist.toString(), "499000000000000000000");
        assert.equal(balanceOwner1.toString(), "500000000000000000000");
        assert.equal(balanceOwner2.toString(), "500000000000000000000");
    });
});

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 also marked with * the error in Test.js to make the observation easier.

Someone can help me how to fix it?

Hi,

From what I see you are running the approve on a different contract. Unless inside the RoyaltiesToken approve function you also call MyNFT.approve this is not going to work on the MyNFT.transfer call.

1 Like

Thanks for your reply @JulissaDantes !! But I still didn't understand that concept... how do I call mynft.approve() into royaltiesToken.approve() or you are talking about calling mynft.approve() into RoyaltiesToken.sol? In that way, I need to create a function that receives the MyNFT address and call it into RoyaltiesToken.sol contract, am I right?

No one can help here?

Hi Pedro, maybe you already solved, because that logic was changed, it but if not, looking at your tests and contracts more closely I see, that a function you are calling inside the transferFrom is _payTxFeeAdmin, and the from is the artist, so the artist will pay the admin, the approve function you are calling is for the admin to transfer the allowance to the artist, and what you need is to approve the admin from the artist.