I have a NFT minting DApp that mints with multiple ERC20 tokens.

I'm having an issue with the react code which calls the smart contract minting function multiple times.

When a user mints with their preferred ERC20 token, the react code is structured in a way that a user would see 3 meta-mask popups.

  1. The first metamask popup asks the user to approve the NFT smart contract to access their ERC20 token.

  2. The second metamask popup asks the user to approve the transfer of the ERC20 tokens into the smart contract.

  3. The third and final popup allows the user to go ahead and mint by calling the mintWithERCToken(mintAmount, tokenID) function in the smart contract. This part is problematic because once the ERC20 has been transferred, and then the user decides to cancel/reject the mint, the token would have already been transferred into the smart contract.

All three metamask calls requires the spending of gas.

What is the correct sequence of events? What is the correct way to write the react code?

Could someone help restructure the react code?

React Code

async function mintWithCrypto(tokenId) {

let web3 = new Web3(ethereum);

//get erc20 contract address
var erc20address = await blockchain.smartContract.methods.getCryptotoken(tokenId).call();

//get token contract information
var currency = new web3.eth.Contract(TOKENABI, erc20address);

//get NFT cost
var mintRate = await blockchain.smartContract.methods.getNFTCost(tokenId).call();

//get mint amount and convert to int
var _mintAmount = Number(mintAmount);

//get total cost of NFTs minted
var totalAmount = mintRate * _mintAmount;

let gasLimit = 285000;

//get total gas
let totalGasLimit = String(gasLimit * _mintAmount);

setFeedback(`Minting your NFT, please hold on...`);

//approve contract address for ERC20 token
currency.methods.approve(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account, gasLimit: String(totalGasLimit)})

//transfer ERC20 token to smart contract. **Problematic code**
.then(await currency.methods.transfer(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account},
  async function (error, transactionHash) {
    // console.log("Transfer Submitted, Hash: ", transactionHash)
    let transactionReceipt = null
    while (transactionReceipt == null) {
      transactionReceipt = await web3.eth.getTransactionReceipt(transactionHash);
      await sleep(10000)

//mint NFT **Problematic code**
.then(blockchain.smartContract.methods.mintWithERCToken(_mintAmount, tokenId).send({from: blockchain.account, gasLimit: String(totalGasLimit)})
.once("error", (err) => {
  if (err.message == "MetaMask Tx Signature: User denied transaction signature.") {
    setFeedback("Transaction cancelled.");
  } else {
    setFeedback("Sorry, something went wrong please try again later.");
.then((receipt) => {
  setFeedback(`Congratulations! You've minted a ${CONFIG.NFT_NAME}.`);

Smart Contract code

function mintWithERCToken(uint256 mintAmount, uint256 tokenID) public payable {
    CryptoTokenInfo storage tokens = permittedCrypto[tokenID];
    IERC20 paytoken;
    paytoken = tokens.paytoken;
    uint256 costval;
    costval = tokens.costvalue;
    uint256 supply = totalSupply();

    require(mintAmount > 0, "You need to mint at least 1 NFT");
    require(mintAmount <= maxMintAmount, "Max mint amount per session exceeded");
    require(supply + mintAmount <= maxSupply, "Max NFT exceeded");
    if (msg.sender != owner()) {
     //check if the user is whitelisted
     if(onlyWhitelisted == true) {
            require(isWhitelisted(msg.sender), "Sorry, address is not whitelisted");
        require(msg.value == costval * mintAmount, "Insufficient funds. Please add more funds to address");

        for (uint256 i = 1; i <= mintAmount; i++) {
            require(paytoken.transferFrom(msg.sender, address(this), costval));
            _safeMint(msg.sender, supply + i);


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy