Allow free mint once for each external ERC1155 and ERC721 token owned

erc-1155erc-721mintsoliditytokens

I modified a mint function to allow users to mint for free if they own tokens from another contract. So far the function works fine but I am having trouble figuring out a security check to prevent users from minting free for the same token.

For example if token 1 was used for the free mint, then it can not be used again.

This is what I have so far –

 ....
modifier mintCompliance(uint256 _mintAmount) {
    // REMOVED TO PUT IN OTHER FUNCTIONS 
        _;
      }
    
      function totalSupply() public view returns (uint256) {
        return supply.current();
      }
    
    // START MINT FUNCTION
      function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) {
        require(!paused, "The contract is paused!");
    
    
    // DETERMINE MINT LOGIC HERE
         if (msg.sender != owner()) { // IF NOT THE OWNER
    
            // MAKE SURE USER OWNS AT LEAST 1 TOKEN
            require(IERC721(0x00...).balanceOf(msg.sender) >= 1, "No tokens owned!");
    
            // CHECK USER TOKEN BALANCE
            uint256 count = IERC721(0x00...).balanceOf(msg.sender);
    
            if (count > 0) { // OWNS AT LEAST 1
            uint256 FreeMints = count * 4; // MULTIPLY AMOUNT OWNED BY 4
            maxMintAmount = FreeMints; // TAKE THAT AND SET AMOUNT FREE FOR THIS ADDRESS
            maxSupply = 100; // MINT FROM A POOL OF RANDOM TOKENS SET ASIDE FOR FREE MINTS ONLY 
            cost = 0 ether; // SET COST TO FREE FOR TOKEN OWNERS
    
            // !NEED SECURITY CHECK SO SYSTEM IS NOT ABSUED/ONLY CLAIM ONCE PER TOKEN. KEEP TRACK SOMEHOW
            // TO DO
    
            // MINT COMPLIANCE REQUIREMENTS
            require(count > 0, "Not Allowed!"); // DONT ALLOW MINT IF NO TOKENS OWNED
            uint256 ownerTokenCount = addressMintedBalance[msg.sender];
            require(ownerTokenCount + _mintAmount <= FreeMints, "max per free mint exceeded");
            require(_mintAmount > 0 && _mintAmount <= maxMintAmount, "Invalid mint amount!");
            require(supply.current() + _mintAmount <= maxSupply, "Max supply exceeded!");
            require(_mintAmount <= maxMintAmount, "max mint amount per session exceeded");
            }
         
        require(msg.value >= cost * _mintAmount, "Insufficient funds!");
    
        _mintLoop(msg.sender, _mintAmount);
      }
      } // END MINT FUNCTION

Also I am trying to do the exact same thing with an erc1155 contract in the same mint function, but it's giving me this error – Undeclared identifier. Did you mean "IERC165"?.

require(IERC1155(0x00...).balanceOf(msg.sender) >= 1, "No tokens owned!");

EDIT:
The current contract is using ERC721 –

contract NFT is ERC721, Ownable {
  using Strings for uint256;
  using Counters for Counters.Counter;

  Counters.Counter private supply;

However, the contract I'm trying to query is –

contract NFT is ERC721Enumerable, Ownable {

Best Answer

If your NFT contract is an ERC721Enumerable contract, then you can use this system.

Add this mapping to your state:

mapping(address => bool) usedTokens;

And modify your code (I know it is not a product code, but you can understand the basics):

modifier mintCompliance(uint256 _mintAmount) {
// REMOVED TO PUT IN OTHER FUNCTIONS 
    _;
}

function totalSupply() public view returns (uint256) {
    return supply.current();
}

// START MINT FUNCTION
function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) {
    require(!paused, "The contract is paused!");

    // DETERMINE MINT LOGIC HERE
    if (msg.sender != owner()) { // IF NOT THE OWNER
        uint _userBalance = IERC721(0x00...).balanceOf(msg.sender);

        if(_userBalance > 0) {
            for(uint i; i < _userBalance; i++) {
                uint _tokenId = IERC721(0x00...).tokenOfOwnerByIndex(msg.sender, i);
                if(!usedTokens[_tokenId]) { // IF TOKEN NOT USED
                    usedTokens[_tokenId] = true;

                    uint256 ownerTokenCount = addressMintedBalance[msg.sender];
                    require(ownerTokenCount + _mintAmount <= FreeMints, "max per free mint exceeded");
                    require(_mintAmount > 0 && _mintAmount <= maxMintAmount, "Invalid mint amount!");
                    require(supply.current() + _mintAmount <= maxSupply, "Max supply exceeded!");
                    require(_mintAmount <= maxMintAmount, "max mint amount per session exceeded");
                }
            }
        } else {
            require(msg.value >= cost * _userBalance, "Insufficient funds!");
            _mintLoop(msg.sender, _mintAmount);
        }
    }
}
Related Topic