Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid data received for ERC721Storage #129

Open
talentlessguy opened this issue Jul 25, 2021 · 1 comment
Open

Invalid data received for ERC721Storage #129

talentlessguy opened this issue Jul 25, 2021 · 1 comment

Comments

@talentlessguy
Copy link

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
}
@bpierre
Copy link
Owner

bpierre commented Jul 26, 2021

I used to check with supportsInterface() before calling either tokenURI() or uri(), but it was having a few downsides, see #90. I changed the behavior so that both methods are now called in parallel, with the first successful one being accepted.

The failing one should be ignored though: https://github.com/spectrexyz/use-nft/blob/83820bb14cba6d3babe375476cc031e3123610e2/src/fetchers/ethers/standard-nft.tsx#L27-L30

And in this example, the call to tokenURI() should work 🤔

I’m going to investigate more and let you know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants