Skip to content

Invalid data received for ERC721Storage #129

@v1rtl

Description

@v1rtl

Hello, I was rewriting my contract and came across with this issue.

useNft seems to be requesting a uri method which isn't present in ERC721 storage and this results in an error.

Reproduction

Here's the contract code:

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

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

contract MyNFT is ERC721, Ownable, ERC721URIStorage {
    event Mint(uint256 id);
    event Claim(uint256 id);

    uint256 public constant MAX_TOKENS = 50;

    uint256 private constant PRICE = 50000000000000000;

    using SafeMath for uint256;

    using Counters for Counters.Counter;

    Counters.Counter private _tokenIds;

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

    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);
    }

    // Mint an NFT and add URI to it
    function mint(string memory tokenURI_) public onlyOwner returns (uint256) {
        _tokenIds.increment();

        uint256 tokenId = _tokenIds.current();

        _safeMint(msg.sender, tokenId);

        require(tokenId <= MAX_TOKENS, "Sold out!");
        console.log(tokenId);

        _setTokenURI(tokenId, tokenURI_);

        return tokenId;
    }

    // Claim and mint NFT
    function claim(uint256 id) external payable {
        require(msg.value == PRICE, "Claiming an NFT costs 0.05 ETH");
        require(id <= MAX_TOKENS, "Cannot claim non-existent token");

        // Transfer to seller
        safeTransferFrom(address(this), msg.sender, id);

        emit Claim(id);
    }

    // withdraw bobux
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    function transferTo(address acc, uint256 id) public onlyOwner {
        safeTransferFrom(msg.sender, acc, id);
    }
}

Dapp code:

import React, { useEffect } from 'react'
import { Contract, utils } from 'ethers'
import { useOnboard } from 'use-onboard'
import { useNft, NftProvider } from 'use-nft'

const contractAddress = 'COPY_ADDRESS_FROM_HARDHAT_DEPLOY_SCRIPT'

function NFT() {
  const { loading, error, nft } = useNft(contractAddress, '1')

  console.log(nft)

  // nft.loading is true during load.
  if (loading) return <>Loading…</>

  // nft.error is an Error instance in case of error.

  if (error || !nft) return <>{error.message}</>

  // You can now display the NFT metadata.
  return (
    <section>
      <h1>{nft.name}</h1>
      <img src={nft.image} alt="" />
      <p>{nft.description}</p>
      <p>Owner: {nft.owner}</p>
      <p>Metadata URL: {nft.metadataUrl}</p>
    </section>
  )
}

const App = () => {
  const { selectWallet, address, isWalletSelected, disconnectWallet, balance, provider } = useOnboard({
    options: {
      networkId: 1337,
      networkName: 'localhost'
    }
  })

  return (
    <div>
      {
        <button
          onClick={async () => {
            if (isWalletSelected) disconnectWallet()
            else await selectWallet()
          }}
        >
          {isWalletSelected ? 'Disconnect' : 'Connect'}
        </button>
      }
      <p>Address: {address}</p>
      <p>Balance: {balance} ETH</p>
      {isWalletSelected && provider && (
        <NftProvider
          fetcher={[
            'ethers',
            {
              ethers: { Contract },
              provider
            }
          ]}
        >
          <NFT />
        </NftProvider>
      )}
    </div>
  )
}

export default App

The error I see:

eth_call
  Contract call:       MyNFT#<unrecognized-selector>
  From:                0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
  To:                  0x2e13f7644014f6e934e314f0371585845de7b986

  Error: Transaction reverted: function selector was not recognized and there's no fallback function

Workaround

I came up with this function to fetch NFTs:

async function fetchNFTs(provider: Web3Provider, address: string) {
  const signer = provider.getSigner()

  const account = await signer.getAddress()

  const contract = new Contract(contractAddress, abi, provider)

  const balance = await contract.balanceOf(account)

  console.log(balance.toNumber())

  const data = []
  for (let i = 0; i < balance.toNumber(); ++i) {
    let tokenURI = await contract.tokenURI(1)
    data.push(tokenURI)
  }

  return data
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions