Stuck with ERC20 payments for ERC721Enumerable


I am currently just hitting a brick wall atm, tirelessly spending hours and days trying to set a mint function to use USDC payments, while testing on the polygon network. I keep getting errors of "ERC20: transfer amount exceeds balance","id":195781395401883}". I tried giving liquidity to the contract of usdc and still no luck... I had it working once but I cannot get it working since. I do not even know what I am doing wrong at this point. I am always setting price lower than 0.01 eth so i have enough usdc to afford to pay to approve it.

I use this contract $1.00 | USD Coin (PoS) (USDC) Token Tracker | PolygonScan

I have used multiple contracts even tried net2devs contract,

and even made my own, but keep getting the exact same error...

Here is mine i made up and pulls same error.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/PullPayment.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract NFT is ERC721Enumerable, PullPayment, Ownable {
  using Counters for Counters.Counter;

  Counters.Counter private currentTokenId;
  IERC20 public usdc;
  uint256 priceInWei = 1 * 10 ** 6;

  using Strings for uint256;

  string baseURI;
  string public baseExtension = ".json";
  uint256 public cost = 0.05 ether;
  uint256 public maxSupply = 10000;
  uint256 public maxMintAmount = 20;
  bool public paused = false;
  bool public revealed = false;
  string public notRevealedUri;

    string memory _name,
    string memory _symbol,
    string memory _initBaseURI,
    string memory _initNotRevealedUri,
    address usdcAddress
  ) ERC721(_name, _symbol) {
    usdc = IERC20(usdcAddress);

  // internal
  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;


  // public
 function mint(uint256 _mintAmount) public payable {
    uint256 supply = totalSupply();
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require(supply + _mintAmount <= maxSupply);

    if (msg.sender != owner()) {
      require(msg.value >= cost * _mintAmount);

    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(msg.sender, supply + i);

  function usdcmint(uint256 _mintAmount) public payable {
     usdc.transferFrom(msg.sender, address(this), priceInWei);
    uint256 supply = currentTokenId.current();
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(msg.sender, supply + i);

  function walletOfOwner(address _owner)
    returns (uint256[] memory)
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory tokenIds = new uint256[](ownerTokenCount);
    for (uint256 i; i < ownerTokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    return tokenIds;

  function tokenURI(uint256 tokenId)
    returns (string memory)
      "ERC721Metadata: URI query for nonexistent token"
    if(revealed == false) {
        return notRevealedUri;

    string memory currentBaseURI = _baseURI();
    return bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";

  //only owner
  function reveal() public onlyOwner {
      revealed = true;
  function setCost(uint256 _newCost) public onlyOwner {
    cost = _newCost;

  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
    maxMintAmount = _newmaxMintAmount;
  function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
    notRevealedUri = _notRevealedURI;

  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;

  function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
    baseExtension = _newBaseExtension;

  function pause(bool _state) public onlyOwner {
    paused = _state;

  function withdrawToken() public onlyOwner {
  usdc.transfer(msg.sender, usdc.balanceOf(address(this)));
}`Preformatted text`

Hello @JezzerHip

Have you set approval so that the NFT contract is allowed to spend the caller's tokens ?

In this part you probably want to do priceInWei * _mintAmount

It’s difficult trying to tell you what you are doing wrong without posting the failed transaction.
It looks like you don’t have enough Usdc or you didn’t approve the contract to spend your usdc.

Also what does it mean “I tried giving liquidity to the contract”? Either you have usdc or you don’t :slight_smile:

The error-message for that would typically be:

ERC20: insufficient allowance

The error-message stated in the question is:

ERC20: transfer amount exceeds balance

How would you approve the contract to spend the usdc? I just bought 20$ worth of usd so surely i have the balance

here is the transaction that will fail, I just used net2dev contract (

i just sent it.

If you randomly change contract and then tell us about a different error you are making it very hard to help you. Please pick one and stick to it.

As for the transaction you posted, you will need to call the approve function on the Usdc coin contract and approve your contract to spent the correct amount of tokens.

Sorry my bad, I will stick to the one that I made transaction with.

How would I do below?
The USDC contract doesn't have any approve function that I could access, if you are talking about the NFT contract to approve a certain amount of NFTS to USDC contract it says the transaction will fail.

Yes it does:

As I've mentioned above, the error-message given in your original question does not imply that the problem can be solved by calling this function (but there's a whole discussion here which I haven't been following, so it is possible that you also need to call that function).

Are you testing your code locally? It's a lot easier to debug that way. On PolygonScan we can't really see the reason for the revert.

Your code says this:

            if (msg.sender != owner()) {
            require(msg.value == cost * _mintAmount, "Not enough balance to complete transaction.");

If the cost is expressed in paytoken, i.e. USDC, why are you comparing it with msg.value? Is this by design?

You seem to be sending 1 MATIC and the cost is 1 USDC. However, MATIC has 18 decimals and USDC has 6 decimals, so the numbers will not match up.