2

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 {

2 Answers 2

1
+50

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);
        }
    }
}
7
  • Hi I updated my question to clarify about the contracts
    – 730wavy
    Feb 19, 2022 at 17:39
  • In this case, users can use their tokens as tickets. They pick which token to use as their ticket and you sign that token in mint function as used. Is this a good system for your purpose? Feb 20, 2022 at 13:56
  • So basically users has token A from contract A. In contract B, they claim token B for free if they have token A. They don't need to chose which token, cause they will have to claim all the free ones at the same time.
    – 730wavy
    Feb 20, 2022 at 14:57
  • But we don't know their tokens, right? We just know their token count. We can create a mapping named usedTokenCount and add the used tokens to it. But what if a user sells one of his token and buys another one? The count is the same. How can we know which token is used by the user? Feb 20, 2022 at 20:27
  • 1
    oh ok that then won't be feasible for users with a lot of tokens. I'll have to rethink this security. I awarded you the bounty for your time. I also have other questions if you care to check out. Thanks.
    – 730wavy
    Feb 20, 2022 at 21:30
0

Maybe try to use a mapping like this

mapping(address => bool) private alreadyUsed;

That will become true when the user uses it for the discount.

So just add a require statement like this

require(alreadyUsed["addressOfTheNFT"] == false, "this token was already used to claim the discount");
2
  • I tried your suggestion, using the token contract as the addressOfTheNFT but it's not working. Am I suppose to pass the token ID to addressOfTheNFT instead?
    – 730wavy
    Feb 19, 2022 at 17:24
  • Yeah try that it will be better
    – Kuly14
    Feb 20, 2022 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.