Fail with error 'ERC721: transfer to non ERC721Receiver implementer'

Hello, newbie here!

I have been following the guide on opensea for creating a contract and adding my NFTS/metadata to the contract.

Guide link here: https://docs.opensea.io/docs/setting-up-your-smart-contract-project

On the step where I mint to my contract: "npx hardhat mint --address contract_address

It comes back with a transition hash and no error, but when I check etherscan for my contract 0xCBEEE8A0e5AD026F08bCC70cb906799A2Be5B3dd

It says the error above.

Here is my contract file:

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

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

contract NFT is ERC721 {
  using Counters for Counters.Counter;

  Counters.Counter private currentTokenId;

  /// @dev Base token URI used as a prefix by tokenURI().
  string public baseTokenURI;

  constructor() ERC721("NFTTutorial", "NFT") {
    baseTokenURI = "";
  }

  function mintTo(address recipient) public returns (uint256) {
    currentTokenId.increment();
    uint256 newItemId = currentTokenId.current();
    _safeMint(recipient, newItemId);
    return newItemId;
  }

  /// @dev Returns an URI for a given token ID
  function _baseURI() internal view virtual override returns (string memory) {
    return baseTokenURI;
  }

  /// @dev Sets the base token URI prefix.
  function setBaseTokenURI(string memory _baseTokenURI) public {
    baseTokenURI = _baseTokenURI;
  }

My mint.js file:

const { task } = require("hardhat/config");
const { getContract } = require("./helpers");
const fetch = require("node-fetch");

task("mint", "Mints from the NFT contract")
.addParam("address", "The address to receive a token")
.setAction(async function (taskArguments, hre) {
    const contract = await getContract("NFT", hre);
    const transactionResponse = await contract.mintTo(taskArguments.address, {
        gasLimit: 500_000,
    });
 //   console.log(`Contract: ${transactionResponse.response}`);
    console.log(`Transaction Hash: ${transactionResponse.hash}`);
});

task("set-base-token-uri", "Sets the base token URI for the deployed smart contract")
.addParam("baseUrl", "The base of the tokenURI endpoint to set")
.setAction(async function (taskArguments, hre) {
    const contract = await getContract("NFT", hre);
    const transactionResponse = await contract.setBaseTokenURI(taskArguments.baseUrl, {
        gasLimit: 500_000,
    });
    console.log(`Transaction Hash: ${transactionResponse.hash}`);
});


task("token-uri", "Fetches the token metadata for the given token ID")
.addParam("tokenId", "The tokenID to fetch metadata for")
.setAction(async function (taskArguments, hre) {
    const contract = await getContract("NFT", hre);
    const response = await contract.tokenURI(taskArguments.tokenId, {
        gasLimit: 500_000,
       
    });
    
    const metadata_url = response;
    console.log(`Metadata URL: ${metadata_url}`);

    const metadata = await fetch(metadata_url).then(res => res.json());
    console.log(`Metadata fetch response: ${JSON.stringify(metadata, null, 2)}`);
});

Any help or suggestions appreciated I have been stuck on this for a week trying to figure out what is wrong :smile:

The contract that you are transferring to needs to implement onERC721Received.

Thank you!

So I need to add a function into the NFT.sol? Or is importing the import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; enough?

You can just extend from ERC721Holder. It should be in whichever contract that you will be passing in as the recipient of your mintTo(address recipient) function.

So I tried adding the import in my NFT.sol:

import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"

That didn't have any effect.

For context:

With _safeMint, it mints a token and transfers it to an address.
If that address is an Externally Owned Account (a normal Ethereum account), that is fine.
If that address is a contract address, the contract that is receiving the NFT must implement onERC721Received to signify that it can receive NFTs.

Are you sure that you intend your NFT.sol contract to receive NFTs itself? It does not look like it is set up to do so. You might just need to _safeMint to a regular address.

If you do have a contract that you intend to receive NFTs, then that contract can be declared like

contract MyNFTHolder is ERC721Holder {