HELP ME - erc721: transfer to non erc721receiver implementer

Hey guys,
I am facing this issue with my smart contract when I try to test it.
Now, I know, I saw this issue in many posts but none of them really helped me get it resolved (and trust me I've spent a lot of time on it)

So basically when I try to implement my mint function, if I use inside it the _mint() function it works when I test it, but if I use _safeMint() it doesn't work and it gives me that error.

function mint(address _to, uint256 amount) public payable {

    require(amount> 0);

    for (uint256 i = 0; i <= amount- 1; i++) {
      _safeMint(_to, supply + i);
    }
  }

Few notes that I realized:

  • I checked many contracts for many projects and all of them use _safeMint.
  • does this method work on the mainnet with no problems? but makes problems when I test it locally or on testnet? if it doesn't work on mainnet and needs to be fixed, wat should I do?
  • as much as I understand, it's not working because I am trying to mint with a smart contract address, so is there a way to test if it's working with some test address that's not a smart contract?
  • I also noticed that when I used _mint, even though it's working the token is somehow not being transferred to the recipient address, the money is being transferred to the contract but the token isn't going the other direction (I tested on polygon mumbai), any idea why ?

Please help guys, I tried for long time and nothing seems to be working
Thank you very much :slight_smile:

The Safe function's perform additional checks that the non-safe versions don't.

Part of the ERC-721 specificaton is:

_safeMint(address to, uint256 tokenId) internal

Internal function to safely mint a new token. Reverts if the given token ID already exists. If the target address is a contract, it must implement onERC721Received, which is called upon a safe transfer, and return the magic value bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); otherwise, the transfer is reverted.

There are 4 solutions to your problem:

  • The smart contract you are using as receiver needs to implement ERC721Received
  • Use a non-smart contract to send the token to
  • Don't call _safeMint but _mint instead, the only "additional" check it does is actually check if it is a smart contract and if that is the case if it implements ERC721Received and then just calls _mint (well officially it mints it and then does the check and revert if it fails).
  • Override _safeMint to just call _mint :stuck_out_tongue:

Thanks for replying,
actually I wanted to just use _mint, but when I tested on polygon mumbai, I realized that I don't receive the token to the address I bought with, even though the currency is transferring to the smart contract address. So, is this connected to the fact that it's a smart contract ? or to the fact that I'm testing on mumbai or what could be the issue? (or is this generally normal to not see the token transferring on a testnet?)
That's why I was hesitate to use _mint.. I don't understand what's going on.
Do you know why I'm not receiving the token ?

NOTE: if I go to the ERC-721 Token Txns on the smart contract I actually see them there, so basically the contract is taking the money and he's the one owning the tokens and not the recipient address XD

As you haven't linked your code and the transaction its hard to say what's going on.

It sounds like you are telling the _mint() to mint to a smart contract address, but then you say that the recipient shouldn't be that smart contract but another recipient address ?
Then you should not call the mint function with the smart contract address as the recipient :slight_smile:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT is ERC721, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    mapping(string => uint8) existingURIs;

    constructor() ERC721("MyNFT", "NFT") {}

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function isContentOwned(string memory uri) public view returns (bool) {
        return existingURIs[uri] == 1;
    }

    function mint(address recipient, string memory metadataURI) public payable returns (uint256) {
        require(existingURIs[metadataURI] != 1, 'Token already minted!');
        require(msg.value >= 0.01 ether, 'Not enough ETH!');
        uint256 newItemId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        existingURIs[metadataURI] = 1;

        _safeMint(recipient, newItemId);
        _setTokenURI(newItemId, metadataURI);

        return newItemId;
    }

    function count() public view returns (uint256) {
        return _tokenIdCounter.current();
    }
}

This is the contract code that I'm using, and more clear explanation:
I am deploying this on an address on the polygon mumbai testnet, let's say the addr is 0x0A
I am using a different address on metamask, one that I imported from the ones generated addresses using the command "npx hardhat node", let's say it's 0x0B, I am adding MATIC Tokens to it and trying to mint on the web3 app that I created, and running it on the localhost, while using the contract above.

0.01 is successfully being transferred from 0x0B to 0x0A, but I can't see any tokens in 0x0B, I actually see them under ERC-71 Token Txns in 0x0A (smart contract)

I don't understand if the addresses given by "npx hardhat node" are smart contracts one, thus I can't use _safeMint, or if it's also why I can't receive the token on them.

I hope it's more clear now

As you describe it, it should work without problems.
The addresses generated by hardhat are normal wallet addresses, not smart contracts.
So your web ui is sending the mint command to your nft contract which calls mint() or safemint() with a recipient address.
So your webui should call mint(0x0B, ...) or safeMint(0x0B, ...) which you should be able to see in the transaction log.

If that is the case and the transaction did not revert it should have worked and 0x0B is now the owner of that new tokenId.

So either the min() or safeMint() werent called correctly or you are checking who is the owner of a token incorrectly.

const contractAddress = '0x0A';
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const contract = new ethers.Contract(contractAddress, MyNFT.abi, signer);

const mintNow= async () => {
        const connection = contract.connect(signer);
        const addr = connection.address;
        const result = await contract.mint(addr, tokenId, {
            value: ethers.utils.parseEther('0.01')
        });

        await result.wait();
    };

by the way, tokenId is incremented... is this not correct ?
I am calling mintNow onClick with some button
wait a sec, could addr be holding the contract address and not the wallet address :open_mouth: ?

will return the address of the contract, not the signer, for that you need the signer's address
signer.getAddress( )

tokenId should be incremented yes, although you might want to limit the amount to be minted, and im not sure why you pass the metadataURI in the mint function

1 Like

Oh my god, this was actually the problem, it worked now.
Thank you very much for your time and help
very much appreciated <3

You must implement onERC721Received().

he was just using the wrong recipient address in the end :slight_smile: