Trouble with payable NFT function in multi-mint Solidity contract

Hey everyone, I’m stuck with my NFT contract. I’ve got two minting options: one for whitelisted soulbound NFTs and another for regular sales. The soulbound mint works fine, but I can’t get the sales mint to function properly.

Here’s my safeMintNft() function:

function safeMintNft() public payable whenNotPaused {
    require(nftCost > 0, "Price not set");
    require(whitelistedAddresses[msg.sender].discordUser != "", "Not whitelisted");
    require(keccak256(bytes(whitelistedAddresses[msg.sender].user_type)) == keccak256(bytes("buyer")), "Buyers only");
    require(address(msg.sender).balance >= nftCost, "Insufficient funds");
    
    uint256 tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    tokenIdAddresses[tokenId] = msg.sender;

    address payable contractAddress = payable(address(this));
    contractAddress.transfer(nftCost);

    _safeMint(msg.sender, tokenId);
}

I’m testing with Chai, and when I try to mint, I get an error on the contractAddress.transfer(nftCost) line. The test account has enough ETH, and I’ve set the cost correctly. What am I missing? Any ideas on how to fix this?

yo strummingMelody, looks like ur havin some trouble. have u tried usin call instead of transfer? it’s way more flexible. also, make sure ur contract can actually receive ETH - u might need a receive() function. lemme know if that helps!

hey there strummingmelody! :blush: that’s a tricky situation you’ve got there. have you considered using the payable(address(this)).call{value: nftCost}('') method instead of transfer()? it’s generally more recommended these days due to gas limit issues with transfer().

also, i’m curious about your whitelist implementation. are you checking if the sender is whitelisted twice? the whitelistedAddresses[msg.sender].discordUser != "" and the user_type == "buyer" checks seem a bit redundant. maybe you could combine them?

oh, and one more thing - have you made sure the contract itself can receive ether? you might need to implement a receive() or fallback() function.

what do you think about these suggestions? let me know if you try them out!

I’ve encountered similar issues with payable functions before. The problem likely stems from how you’re handling the transfer. Instead of using transfer(), consider employing call() for better gas efficiency and to avoid potential issues with gas limits. Here’s a modified version of your function that might work:

function safeMintNft() public payable whenNotPaused {
    require(nftCost > 0, "Price not set");
    require(whitelistedAddresses[msg.sender].discordUser != "" && 
            keccak256(bytes(whitelistedAddresses[msg.sender].user_type)) == keccak256(bytes("buyer")), 
            "Not whitelisted or not a buyer");
    require(msg.value >= nftCost, "Insufficient funds");

    uint256 tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    tokenIdAddresses[tokenId] = msg.sender;

    (bool success, ) = payable(address(this)).call{value: msg.value}("");
    require(success, "Transfer failed");

    _safeMint(msg.sender, tokenId);
}

Also, ensure your contract has a receive() function to accept ETH. Let me know if this helps or if you need further clarification.