Problem/Clarification:
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) {
Web3EthContract.setProvider(ethereum);
let web3 = new Web3(ethereum);
var erc20address = await blockchain.smartContract.methods.getCryptotoken(tokenId).call();
var currency = new web3.eth.Contract(TOKENABI, erc20address);
var mintRate = await blockchain.smartContract.methods.getNFTCost(tokenId).call();
var _mintAmount = Number(mintAmount);
var totalAmount = mintRate * _mintAmount;
let gasLimit = 285000;
let totalGasLimit = String(gasLimit * _mintAmount);
setFeedback(`Minting your NFT, please hold on...`);
currency.methods.approve(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account, gasLimit: String(totalGasLimit)})
.then(await currency.methods.transfer(CONFIG.CONTRACT_ADDRESS, String(totalAmount)).send({from: blockchain.account},
async function (error, transactionHash) {
let transactionReceipt = null
while (transactionReceipt == null) {
transactionReceipt = await web3.eth.getTransactionReceipt(transactionHash);
await sleep(10000)
}
}))
.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) => {
console.log(receipt);
setFeedback(`Congratulations! You've minted a ${CONFIG.NFT_NAME}.`);
dispatch(fetchData(blockchain.account));
})
)
}
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);
}
}
What I have tried:
I've exhausted all avenues with the react code. I'm not sure how to transfer the actual ERC20 token. Mint + ERC20 transfer.