How to accept token payments for minting or selling NFTs?

I’m new to smart contracts and trying to set up NFT minting. I’m using Remix and the BSC network because they use 18 decimals like ETH.

I can do free minting and set a cost in BNB, but I want to use BUSD on BSC. I can check BUSD balances, but when I try to mint, I get this error:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Returned error: {"jsonrpc":"2.0","error":"execution reverted","id":5075618818565261}

Here’s my test contract:

pragma solidity ^0.8.0;

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

contract CoolNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _itemCounter;

    ERC20 public _paymentToken;
    uint256 public _mintPrice = 1e18;
    address tokenContract = 0xeB3Eb991D39Dac92616da64b7c6D5af5cCFf1627;

    struct Item {
        string seriesName;
    }

    mapping(uint256 => Item) private _items;

    constructor(uint256 mintPrice) ERC721("CoolNFT", "COOL") {
        _paymentToken = ERC20(tokenContract);
        _mintPrice = mintPrice * 1e18;
    }

    function checkBalance() public view returns (uint256) {
        return _paymentToken.balanceOf(msg.sender);
    }

    function createItem(address to, string memory tokenURI, string memory _seriesName)
        public onlyOwner 
        returns (uint256)
    {   
        require(_paymentToken.balanceOf(msg.sender) >= _mintPrice, "Not enough tokens");
        
        _paymentToken.transferFrom(msg.sender, address(this), _mintPrice);
        
        _itemCounter.increment();

        uint256 newId = _itemCounter.current();
        _mint(to, newId);
        _items[newId].seriesName = _seriesName;
        _setTokenURI(newId, tokenURI);

        return newId;
    }

    function getSeriesName(uint256 tokenId) public view returns (string memory) {
        return _items[tokenId].seriesName;
    }

    function updateMintPrice(uint256 newPrice) external onlyOwner {
        _mintPrice = newPrice * 1e18;
    }
}

What’s wrong with my code? How can I fix it?

I see you’re working on an NFT project with BUSD payments - that’s a cool idea! The error you’re getting is likely because the contract hasn’t been approved to spend the user’s BUSD tokens. Before calling the mint function, make sure to call the approve function on the BUSD contract to allow your contract to transfer tokens.

Also, I’d suggest using a safer pattern for token transfers. Instead of transferFrom, try using safeTransferFrom from OpenZeppelin’s SafeERC20 library. It adds extra checks and is generally more secure.

One other thing - consider implementing the Checks-Effects-Interactions pattern to protect against reentrancy attacks. This means doing all state changes before interacting with external contracts.

Have you thought about adding events to your contract? They can be really helpful for tracking mints and other important actions. Good luck with your project!

hey there! i’m kinda curious about your NFT project. sounds pretty cool! :raised_hands:

have you tried checking if you’ve approved the contract to spend your BUSD? that might be why it’s not working. sometimes we forget that step and it can cause weird errors.

also, i noticed you’re using transferFrom in your code. that can be a bit tricky and might open you up to some nasty attacks. maybe try using transfer instead? it’s usually safer.

oh, and here’s a random thought - have you considered using the Checks-Effects-Interactions pattern? i’ve heard it’s good for avoiding reentrancy stuff.

what kind of NFTs are you making btw? i’d love to hear more about your project!

hey there! make sure you’ve approved the contract to spend your BUSD first. that’s probably why it’s not working.

also, try using transfer instead of transferFrom. it’s safer and might fix your problem.

what kind of NFTs are u making? sounds interesting!