Fraction NFT Marketplace - Error when buying NFTs: ERC20: insufficient allowance

I'm trying to build fraction NFT Marketplace, It's now working when I try to create a new NFT and show it in the frontend. I'm now facing an error in my buyNFT function and no matter what I try it keeps happening:

Error: VM Exception while processing transaction: reverted with reason string 'ERC20: insufficient allowance'

can anyone guide me on what I should do?

this is my smart contract right now:

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

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "./CustomERC20.sol";

contract FractionalNFTMarketplace is ERC721URIStorage, Ownable {
    using Strings for uint256;

    uint256 private _currentTokenId = 0;
    address public customERC20Address;

    struct FractionalToken {
        CustomERC20 token;
        uint256 totalShares;
        uint256 pricePerShare;

    struct MarketItem {
        uint256 tokenId;
        address payable seller;
        address payable owner;
        uint256 totalShares;
        uint256 pricePerShare;
        bool isForSale;

    mapping(uint256 => FractionalToken) private _fractionalTokens;
    mapping(uint256 => mapping(address => uint256)) private _shareOwners;
    mapping(uint256 => MarketItem) private _marketItems;

    Counters.Counter private _marketItemId;

    event TokenCreated(uint256 indexed tokenId, uint256 totalShares, uint256 pricePerShare);
    event TokenBought(uint256 indexed tokenId, address indexed buyer, uint256 shares);
    event MarketItemCreated(uint256 indexed tokenId, address indexed seller, uint256 totalShares, uint256 pricePerShare);
    event MarketItemSold(uint256 indexed marketItemId, address indexed buyer);
    event ApprovalForToken(uint256 indexed tokenId, address indexed owner, uint256 amount);

    constructor(address _customERC20Address) ERC721("Fractional NFT Marketplace", "FNFTM") {
        customERC20Address = _customERC20Address;

    function createMarketItem(uint256 tokenId) private {
    require(_exists(tokenId), "Token ID does not exist.");

    address currentOwner = ownerOf(tokenId);

    FractionalToken storage fractionalToken = _fractionalTokens[tokenId];

    _marketItems[tokenId] = MarketItem(

    emit MarketItemCreated(tokenId, currentOwner, fractionalToken.totalShares, fractionalToken.pricePerShare);

    function createToken(uint256 totalShares, uint256 pricePerShare, string memory tokenURI) public payable returns (uint256) {
        require(totalShares > 0, "Total shares must be greater than 0.");
        require(pricePerShare > 0, "Price per share must be greater than 0.");
        uint256 newTokenId = _currentTokenId++;
        _mint(msg.sender, newTokenId);
        _setTokenURI(newTokenId, tokenURI);

        string memory name = string(abi.encodePacked("Shares of NFT #", newTokenId.toString()));
        string memory symbol = string(abi.encodePacked("SNFT", newTokenId.toString()));
        CustomERC20 newERC20Token = new CustomERC20(name, symbol);, totalShares);

            _fractionalTokens[newTokenId] = FractionalToken(newERC20Token, totalShares, pricePerShare);
            _shareOwners[newTokenId][msg.sender] = totalShares;
            newERC20Token.approve(address(this), totalShares);
            emit TokenCreated(newTokenId, totalShares, pricePerShare);
            return newTokenId;
function approveContractForToken(uint256 tokenId, uint256 amount, address spender) public {
        require(_exists(tokenId), "Token ID does not exist.");
        require(ownerOf(tokenId) == msg.sender, "Caller is not the owner of the token.");

        FractionalToken storage fractionalToken = _fractionalTokens[tokenId];
        fractionalToken.token.approve(spender, amount);
        emit ApprovalForToken(tokenId, msg.sender, amount);

    function getAllowance(uint256 tokenId, address owner, address spender) public view returns (uint256) {
        require(_exists(tokenId), "Token ID does not exist.");
        FractionalToken storage fractionalToken = _fractionalTokens[tokenId];
        return fractionalToken.token.allowance(owner, spender);

    function buyToken(uint256 tokenId,uint256 totalPrice, uint256 shares) public payable {
        require(_exists(tokenId), "Token ID does not exist.");
        require(shares > 0, "Shares must be greater than 0.");

        FractionalToken storage fractionalToken = _fractionalTokens[tokenId];
        require(fractionalToken.totalShares >= shares, "Not enough shares available.");

        require(msg.value >= totalPrice, "Not enough Ether sent to buy the shares.");

        address seller = ownerOf(tokenId);

        CustomERC20 token = fractionalToken.token;
        uint256 allowance = token.allowance(seller, address(this));

        //require(allowance >= shares, "Not enough allowance to transfer shares.");

        token.transferFrom(seller, msg.sender, shares);
        fractionalToken.totalShares -= shares;
        _shareOwners[tokenId][seller] -= shares;
        _shareOwners[tokenId][msg.sender] += shares;

        // Re-approve the remaining allowance
        uint256 remainingAllowance = allowance - shares;
        approveContractForToken(tokenId, remainingAllowance, address(this)); // Call the function to re-approve the remaining allowance

        emit TokenBought(tokenId, msg.sender, shares);

This is my context.js file that connects it with the front-end:

const ApproveShares = async (NFT, shares) => {
      try {
        const contract = await SmartContractConnect();
        const customERC20Address = await contract.customERC20Address();
        const customERC20 = new ethers.Contract(customERC20Address, CustomERC20ABI, contract.signer);
        const totalPrice = ethers.utils.parseUnits((NFT.price * shares).toString(), "ether");
        console.log("Approving shares:", {
          tokenId: NFT.tokenId,

        const transaction = await customERC20.approve(contract.address, totalPrice);
        await transaction.wait();
        const allowance = await customERC20.allowance(contract.signer.getAddress(), contract.address);
        console.log("Allowance set:", {
          allowance: ethers.utils.formatUnits(allowance.toString(), "ether"),

        console.log("Shares approved for transfer");
      } catch (error) {
        console.log("Error while approving shares", error);

    //Create NFt
    const CreateNft = async (name, price, shares, image, description, router) => {
        if (!name || !description || !price || !image || !shares)
          return console.log("Data is Missing!");
        const data = JSON.stringify({ name, description, image });
        try {
          const added = await client.add(data);
          const url = `${added.path}`;
          const tokenId = await createSale(url, price, shares);
          await ApproveShares({ tokenId, price }, shares);
        } catch (error) {

    //Create Sale
    const createSale = async (url, Price, shares, isResale, id) => {
      try {
        const price = ethers.utils.parseUnits(Price, "ether");
        const contract = await SmartContractConnect();
        const transaction = !isResale
          ? await contract.createToken(shares, price, url)
          : await contract.resellToken(id, price);
        const receipt = await transaction.wait(); // Get the transaction receipt
        const tokenId = => event.event === "TokenCreated").args.tokenId; // Find the tokenId from the "TokenCreated" event
        console.log("Transaction Completed: ", transaction);

        return tokenId; // Return the tokenId      
        //await transaction.wait();
      } catch (error) {
        console.log("Error while creating sale: ", error);
 const BuyNFT = async (NFT, shares) => {
        try {
          const contract = await SmartContractConnect();
          const price = NFT.price*(shares)
          const totalPrice = ethers.utils.parseUnits(price.toString(), "ether");
          const transaction = await contract.buyToken(NFT.tokenId,totalPrice, shares,{
            gasLimit: 5000000,
            gasPrice: 10000000000,
            value: totalPrice
          await transaction.wait();
        } catch (error) {
          console.log("Error while buying NFT", error);