I am writing a smart contract that uses USDC when a NFT is purchased (so the USDC is transferred from the seller to the buyer).

I am trying to test it in Hardhat but I am having troubles when using USDC on my hardhat tests. I forked the goerli testnet and impersonated by wallet has it has USDC in goerli.

When I do an NFT purchase with my account, it revert with an error "ERC20: transfer amount exceeds balance" so I am trying to display the USDC balance to understand where the problem is, but the USDC balance is returning 0, when I have in my account USDC.

What am I missing?

This is my solidity function:

     function createMarketSale(uint256 tokenId)
        uint priceUSD = idToMarketItem[tokenId].priceUSD;
        address seller = idToMarketItem[tokenId].seller;

        USDc.transfer(seller, priceUSD * 10 ** 6);
        _transfer(idToMarketItem[tokenId].owner, msg.sender, tokenId);

        idToMarketItem[tokenId].owner = msg.sender;
        idToMarketItem[tokenId].sold = true;

This is my hardhat test:

const USDC_ADDRESS = "0x98339D8C260052B7ad81c28c16C0b98420f2B46a";
const USDC_WHALE = "0x682...";
const ERC20ABI = require('./ERC20.json');

beforeEach(async () => {
    [owner, addr1, addr2] = await ethers.getSigners();

    const ArtworkContract = await ethers.getContractFactory("Artwork");
    artworkcontract = await ArtworkContract.deploy();

    // This should send USDC to addr1 but addr1 is not receiving USDC (or I cannot see it!)
    await impersonateFundErc20(

  describe("MarketPlace", () => {

    it("Should create an NFT", async function () {
      const [owner, addr1, addr2] = await ethers.getSigners();
      const whaleAddress = await ethers.getSigner(USDC_WHALE);
      await artworkcontract.deployed();
      await owner.sendTransaction({
        to: whaleAddress.address,
        value: ethers.utils.parseEther("1000.0"), // Send more ether to impersonated account

      const USDC = new ethers.Contract(USDC_ADDRESS, ERC20ABI, ethers.provider);
      USDCBalance = await USDC.balanceOf(USDC_WHALE);
      console.log("USDCBalance: " + USDCBalance); // This is returning 0

      await artworkcontract.connect(addr1).createToken("uri1","url1",2)
      const allItems = await artworkcontract.fetchAllItems()
      const ETHBalance = await ethers.provider.getBalance(USDC_WHALE);
      console.log("ETHBalance: ",ETHBalance);
      await artworkcontract.connect(whaleAddress).createMarketSale(1) // reverts with error ERC20: transfer amount exceeds balance


1 Answer 1


First you have to approve the USDC amount on the USDC contract from the senders wallet on the frontend. Then you can call this createMarketSale function on your contract that will transfer the USDC from the sender to your contract and then to the seller from your contract.

function createMarketSale(uint256 tokenId) public {
   uint256 priceUSD = idToMarketItem[tokenId].priceUSD;
   address seller = idToMarketItem[tokenId].seller;
   // transfer amount from buyer to contract
   USDc.transferFrom(msg.sender, address(this), priceUSD * 10**6);
   // then send from the contract to seller
   USDc.transfer(seller, priceUSD * 10**6);

   _transfer(idToMarketItem[tokenId].owner, msg.sender, tokenId);

   idToMarketItem[tokenId].owner = msg.sender;
   idToMarketItem[tokenId].sold = true;
  • So does it mean that I cannot transfer erc20 tokens directly from the msg.sender to the seller with USDc.transferFrom(msg.sender, seller, priceUSD*10**6)? If not, does it mean I have to fund my contract? Commented Nov 24, 2022 at 2:22
  • I was able to fund the contract from hardhat using await USDC.connect(owner).transfer(artworkcontract.address, 100); and I can check the balance of an address by. having a function in my contract USDc.balanceOf(msg.sender). However, as the funds are going from the contract to the seller, how can I get the funds to get to the buyer? (nothing is reaching the buyer). Commented Nov 24, 2022 at 6:37
  • Okay, I have edited my answer, to send USDC from one person to the other through the contract the buyer has to approve the contract for the usdc amount, then the contract transfers that amount from the buyer, then the contract sends the usdc from the contract to the seller. You may have to approve the usdc from the contract before sending but I'm not sure. Hope it helps! Commented Nov 24, 2022 at 15:26
  • Thanks a lot for your answer :) however this is not working for me. I keep getting the 'ERC20: transfer amount exceeds allowance' error. I have tried several combinations of approve and transfer/transferFrom and as far as I am understanding it's because the caller of the USDC functions is the contract itself? And the approve function has to be called in a separate function? This is quite confusing and I still don't understand how to overcome this problem! Commented Nov 29, 2022 at 8:44
  • yes, the approve method ON USDC contract has to be called by the owner of the tokens seperately and THEN you call the createMarketSale function on your contract. Hope that clears things up Commented Nov 29, 2022 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.