Mint NFT and send a fee to address

Trying to send a fee (in ether) while minting an NFT

:computer: Environment
@openzeppelin/contracts-upgradeable”: “^4.0.0”,
“truffle”: “^5.3.1”

:memo:Details
Implemented following code:

    function sendMintingFee() internal {
    require(!locked, "Reentrant detected!");

    locked = true;
    uint256 feevalue = _calculateMintingFee();
    (bool success, bytes memory transactionBytes) = FeeAddress.call{value: feevalue}("");
    
    require(success, "Transfer failed.");
    locked = false;

    emit FeePaid(msg.sender, feevalue);
}

function mintWithFee(
    address _to,
    Fee[] memory _fees,
    AttributesStruct[] memory _attributes
) external {
    require(
        FeeAddress != address(0),
        "minting Fee Address not set"
    );
    if (FeeMultiplier > 0) {
        sendMintingFee();
    }

    mint(_to, _fees, _attributes);
}

/**
 * @dev set the wallet address where fees will be collected
 */
function setFeeAddress(address payable _FeeAddress)
    public
{
    require(
        hasRole(DEFAULT_ADMIN_ROLE, _msgSender()),
        "Caller must have admin role to set minting fee address"
    );

    FeeAddress = _FeeAddress;
    emit FeeAddressChanged(FeeAddress);
}

/**
 * @dev sets the transfer fee multiplier
 */
function setFeeMultiplier(uint256 _FeeMultiplier)
    public
{
    require(
        hasRole(DEFAULT_ADMIN_ROLE, _msgSender()),
        "Caller must have admin role to set minting fee percent"
    );
    FeeMultiplier = _FeeMultiplier;
    emit FeeMultiplierChanged(FeeMultiplier);
}

/**
 * @dev sets the transfer fee multiplier
 */
function setMintFee(uint256 _MintingFee) public {
    require(
        hasRole(DEFAULT_ADMIN_ROLE, _msgSender()),
        "Caller must have admin role to set minting fee percent"
    );
    MintingFee = _MintingFee;
    emit MintFeeChanged(MintingFee);
}

function _calculateMintingFee() internal view returns (uint256) {
    return MintingFee * FeeMultiplier;
}

The trasnfer never succeeds and bool success = false
as expeced the following error is thrown:
Error: Returned error: VM Exception while processing transaction: revert Transfer failed. -- Reason given: Transfer failed..

How do solve the issue that the user invoking the contract must pay a fee for minting process?
Must the contract be funded?

Method can be tested with this test function:

  it.only("mint with fee", async function () {
    const minter = accounts[1]
    const feeAddress = accounts[2]
    await this.NFT.setFeeAddress(feeAddress)
    await this.NFT.setFeeMultiplier(1)
    await this.NFT.setMintFee("100000000000000000")
    console.log("FeeMultiplier: ",await this.NFT.FeeMultiplier)
    console.log("MintingFee: ",await this.NFT.MintingFee)
    console.log("FeeAddress: ",await this.NFT.FeeAddress)

    const receipt = await this.NFT.mintWithFee(minter, [{ recipient: minter, value: 100 }], [{ key: "bar", value: "1234" }]);
    const tokenId = new BN(parseInt(await this.NFT.getCurrentCounter()) - 1)

    let balance = (await this.NFT.balanceOf(feeAddress)).toString();
    assert.equal(balance, 100000000000000000);
  });

:1234: Code to reproduce
https://github.com/selimerunkut/nft_troubleshoot

1 Like

Hi @intoverq,

Your function needs to be payable to accept Ether.

I suggest looking at the Crowdsale contracts (they are Solidity 0.5 but the concepts are similar)

As an aside, you may want to have sales in a separate contract if the sales aren’t for the life of the token e.g. there is some cap.

1 Like

Thanks.

works with payable modifier

and

function _forwardFunds() internal {
        (bool success, ) = _address1.call{value: msg.value}("");
        require(success, "Transfer failed.");
    }

Unfortunately could not make the nonReentrant modifier work

Getting this error
Identifier not found or not unique.

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

function mintWithFee(
        address _to,
        Fee[] memory _fees,
        AttributesStruct[] memory _attributes
    ) public nonReentrant payable {
...
}