2

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.

(reason="Error: VM Exception while processing transaction: reverted with reason string 'Not enough Ether sent to buy the shares.'", method="estimateGas",

can anyone guide me on what I should do?

this is my smart contract right now:

    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(
        tokenId,
        payable(currentOwner),
        payable(address(this)),
        fractionalToken.totalShares,
        fractionalToken.pricePerShare,
        true
    );

    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);
        newERC20Token.mint(msg.sender, totalShares);

        _fractionalTokens[newTokenId] = FractionalToken(newERC20Token, totalShares, pricePerShare);
        _shareOwners[newTokenId][msg.sender] = totalShares;
        approveContractForToken(newTokenId,totalShares);
        createMarketItem(newTokenId);

        emit TokenCreated(newTokenId, totalShares, pricePerShare);
        return newTokenId;
    }

    function approveContractForToken(uint256 tokenId, uint256 amount) 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(address(this), 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.");

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

        address seller = ownerOf(tokenId);

        CustomERC20 customERC20 = CustomERC20(customERC20Address);
        uint256 allowance = customERC20.allowance(seller, address(this));

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

        customERC20.transferFrom(seller, msg.sender, shares);
        payable(seller).transfer(totalPrice);
        fractionalToken.totalShares -= shares;
        _shareOwners[tokenId][seller] -= shares;
        _shareOwners[tokenId][msg.sender] += shares;
        emit TokenBought(tokenId, msg.sender, shares);
    }

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

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 = `https://infura-ipfs.io/ipfs/${added.path}`;

          await createSale(url, price, shares);

        } catch (error) {
          console.log(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);
    
        await transaction.wait();
        console.log("Transaction Completed: ", transaction);
      } catch (error) {
        console.log("Error while creating sale: ", error);
      }
  };

    //Fetch NFTS
    const FetchNFTs = async () => {
        try {
          const provider = new ethers.providers.JsonRpcProvider();
          const contract = fetchSmartContract(provider);
      
          const data = await contract.fetchMarketItems();
          // console.log("Data: ", data);
          const items = await Promise.all(
            data.map(async ({ tokenId, seller,owner, pricePerShare,totalShares  }) => {
              const TokenURI = await contract.tokenURI(tokenId);
              const {
                data: { image, name, description },
              } = await axios.get(TokenURI);
      
              const price = ethers.utils.formatUnits(pricePerShare.toString(), "ether");

              return {
                price,
                tokenId: tokenId.toNumber(),
                seller,
                owner,
                image,
                name,
                description,
                TokenURI,
                totalShares: totalShares.toNumber()
              };
            })
          );
          // console.log("Fetch success: ", items);
          return items;
        } catch (error) {
          console.log("Error while fetching NFTs:  ", error);
        }
      };

    useEffect(()=>{
        FetchNFTs();
    },[]);

//Buy NFTs
    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);
      
          await transaction.wait();
          router.push("/profile");
        } catch (error) {
          console.log("Error while buying NFT", error);
        }
    };

1 Answer 1

1

There is an issue with this line:

      const transaction = await contract.buyToken(NFT.tokenId,totalPrice, shares);

You are not correctly specifying the value, which is the amount of Ether you want to send with the transaction. In this case the amount to buyToken

To fix the issue, you need to include the value when calling the buyToken function.

Like this:

const transaction = await contract.buyToken(NFT.tokenId, totalPrice, shares, {
    value: totalPrice, // Add this line
});
4
  • I noticed this a while ago but I'm now facing another issue: Error: VM Exception while processing transaction: reverted with reason string 'ERC20: insufficient allowance' I have updated my code in the question above for the most recent one, can you help me figuring it out?
    – Abde
    Apr 11, 2023 at 0:53
  • Please create a new question I would be happy to answer it :)
    – Adam Boudj
    Apr 11, 2023 at 1:15
  • posted it in a new question, thanks for replying!
    – Abde
    Apr 11, 2023 at 1:24
  • Hey adam, would appreciate any help in the other question as i'm facing hard time solving it, thank you
    – Abde
    Apr 11, 2023 at 3:49

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.