Giter Club home page Giter Club logo

use-nft's Introduction

npm version bundle size License

useNft() allows to access the metadata of any NFT (EIP 721, EIP 1155 and more) on the Ethereum blockchain.

Install

npm install --save use-nft

Usage

useNft() uses a concept of “fetchers”, in order to provide different ways to retrieve data from Ethereum. If you use the Ethers in your app, using ethersFetcher() is recommended. Otherwise you can use ethereumFetcher(), which only requires a standard Ethereum provider, like the one provided by MetaMask.

import { getDefaultProvider } from "ethers"
import { NftProvider, useNft } from "use-nft"

// We are using the "ethers" fetcher here.
const ethersConfig = {
  provider: getDefaultProvider("homestead"),
}

// Alternatively, you can use the "ethereum" fetcher. Note
// that we are using window.ethereum here (injected by wallets
// like MetaMask), but any standard Ethereum provider would work.
// const fetcher = ["ethereum", { ethereum }]

// Wrap your app with <NftProvider />.
function App() {
  return (
    <NftProvider fetcher={["ethers", ethersConfig]}>
      <Nft />
    </NftProvider>
  )
}

// useNft() is now ready to be used in your app. Pass
// the NFT contract and token ID to fetch the metadata.
function Nft() {
  const { loading, error, nft } = useNft(
    "0xd07dc4262bcdbf85190c01c996b4c06a461d2430",
    "90473"
  )

  // 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.</>

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

API

useNft(contract, tokenId)

The useNft() hook requires two arguments: the NFT contract address, and its token ID.

The returned value is an object containing information about the loading state:

const { status, loading, error, reload, nft } = useNft(
  "0xd07dc4262bcdbf85190c01c996b4c06a461d2430",
  "90473"
)

// one of "error", "loading" and "done"
status

// same as status === "loading"
loading

// undefined or Error instance when status === "error"
error

// call this function to retry in case of error
reload

// nft is undefined when status !== "done"
nft

// name of the NFT (or empty string)
nft.name

// description of the NFT (or empty string)
nft.description

// image / media URL of the NFT (or empty string)
nft.image

// the type of media: "image", "video" or "unknown"
nft.imageType

// current owner of the NFT (or empty string)
nft.owner

// url of the json containing the NFT's metadata
nft.metadataUrl

// the raw NFT metadata, or null if non applicable
nft.rawData

As TypeScript type:

type NftResult = {
  status: "error" | "loading" | "done"
  loading: boolean
  reload: () => void
  error?: Error
  nft?: {
    description: string
    image: string
    imageType: "image" | "video" | "unknown"
    name: string
    owner: string
    metadataUrl?: string
    rawData: Record<string, unknown> | null
  }
}

<NftProvider />

NftProvider requires a prop to be passed: fetcher. It can take a declaration for the embedded fetchers, or you can alternatively pass a custom fetcher.

fetcher

With Ethers.js

Make sure to add either ethers or @ethersproject/contracts to your app:

npm install --save ethers

Then:

<NftProvider fetcher={["ethers", { provider }]} />

Where provider is a provider from the Ethers library (not to be mistaken with standard Ethereum providers).

With an Ethereum provider
<NftProvider fetcher={["ethereum", { ethereum }]} />

Where ethereum is a standard Ethereum provider.

Custom fetcher

A fetcher is an object implementing the Fetcher type:

type Fetcher<Config> = {
  config: Config
  fetchNft: (contractAddress: string, tokenId: string) => Promise<NftMetadata>
}
type NftMetadata = {
  name: string
  description: string
  image: string
}

See the implementation of the Ethers and Ethereum fetchers for more details.

ipfsUrl

A function that allows to define the IPFS gateway (defaults to https://ipfs.io/).

Default value:

function ipfsUrl(cid, path = "") {
  return `https://ipfs.io/ipfs/${cid}${path}`
}

imageProxy

Allows to proxy the image URL. This is useful to optimize (compress / resize) the raw NFT images by passing the URL to a service.

Default value:

function imageProxy(url, metadata) {
  return url
}

jsonProxy

Allows to proxy the JSON URL. This is useful to get around the CORS limitations of certain NFT services.

Default value:

function jsonProxy(url) {
  return url
}

FetchWrapper

FetchWrapper is a class that allows to use the library with other frontend libraries than React, or with NodeJS. Unlike the useNft() hook, FetchWrapper#fetchNft() does not retry, cache, or do anything else than attempting to fetch the NFT data once.

import { FetchWrapper } from "use-nft"

Pass the fetcher declaration to the FetchWrapper and call the fetchNft function to retreive the NFT data.

// See the documentation for <NftProvider /> fetcher prop
const fetcher = ["ethers", { provider: ethers.getDefaultProvider() }]

const fetchWrapper = new FetchWrapper(fetcher)

// You can also pass options to the constructor (same as the <NftProvider /> props):
// const fetchWrapper = new FetchWrapper(fetcher, {
//   ipfsUrl: (cid, path) => `…`,
//   imageProxy: (url) => `…`,
//   jsonProxy: (url) => `…`,
// })

const result = await fetchWrapper.fetchNft(
  "0xd07dc4262bcdbf85190c01c996b4c06a461d2430",
  "90473"
)

The fetchNft() function returns a promise which resolves to an NftMetadata object.

Supported NFT formats

Any standard NFT (EIP 721 or EIP 1155) is, in theory supported by useNft(). In practice, some adjustments are needed to support some NFT formats, either because their implementation doesn’t follow the specification or because some parts of the specifications can be interpreted in different ways.

This table keeps track of the NFT minting services that have been tested with useNft() and the adaptations needed.

NFT minting service Supported Specific adaptations done by useNft()
AITO Yes
Async Art Yes
Clovers Yes
CryptoKitties Yes Non standard NFT, dedicated mechanism.
CryptoPunks Yes Non standard NFT, dedicated mechanism.
Cryptovoxels Yes
Decentraland Yes Estate and parcels are fetched from The Graph. Wearables are fetched as standard NFTs.
Foundation Yes
JOYWORLD Yes
KnownOrigin Yes
MakersPlace Yes Incorrect JSON format (uses imageUrl instead of image).
Meebits Yes CORS restricted, requires a JSON proxy to be set (see jsonProxy).
MoonCats Yes Non standard NFT, dedicated mechanism.
Nifty Gateway Yes Incorrect metadata URL.
OpenSea Yes Incorrect metadata URL.
Portion.io Yes Non-standard JSON format.
Rarible Yes
SuperRare Yes
Uniswap V3 Yes
Zora Yes

License

MIT

Special Thanks 🙏

Thanks to ImageKit.io for supporting the project by providing a free plan.

ImageKit

use-nft's People

Contributors

bpierre avatar dependabot[bot] avatar dibeling avatar esemeniuc avatar gretzke avatar gwendall avatar iparsrc avatar macelai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

use-nft's Issues

CORS Issue when fetching NFT

When trying to fetch this NFT:
https://opensea.io/assets/0x2acab3dea77832c09420663b0e1cb386031ba17b/967
it fails due to CORS:

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled

image

Any suggestion on how this can be solved?

Detect image type

It could be nice to rely on the URL and provide a hint about the image type (e.g. we could have "image", "video" and "unknown"), instead of letting app authors do it themselves (example).

Cannot find namespace 'JSX'.

Error: node_modules/use-nft/dist/core.d.ts:6:5 - error TS2503: Cannot find namespace 'JSX'.

6 }): JSX.Element;
      ~~~

Somehow the @types/react dependency is lost when installing the package. This seems to be the same issue.

It can be solved by installing @types/react manually in the project.

Invalid data received for ERC721Storage

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
}

Usage within NextJS

Attempting to use this module with NextJS results in the following message being displayed.

ReferenceError: exports is not defined in ES module scope This file is being treated as an ES module because it has a '.js' file extension and '/Users/naj/code/project/node_modules/use-nft/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.

This happens both when using it with the useNFT hook and using FetchWrapper
This is using NextJS 12.1.5 with React 18.1.0

Module not found error

Hi there,

I am having some issues importing use-nft. Here is the error I get:

Compiled with problems:

ERROR in ./node_modules/use-nft/dist/index.js 5678:53-60

Module not found: Error: Can't resolve 'process/browser' in '/path/node_modules/use-nft/dist'
Did you mean 'browser.js'?
BREAKING CHANGE: The request 'process/browser' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

I am using:

  • React ^18.1.0
  • npm 8.12.1
  • node v16.14.0

The app was created using React-Create-App.

Has anyone got the same error and found a solution? Thanks.

Why?

bro like use cors wtf

Usage without React

Hi,
Great job, thank you!
I am trying to use the fetcher without react and got the following error:

./node_modules/swr/dist/index.esm.jsModule not found: Error: Can't resolve 'react' in '/Users/gen4/Gits/BigDay/STD-visual/node_modules/swr/dist'./node_modules/use-nft/dist/use-nft.jsModule not found: Error: Can't resolve 'react' in '/Users/gen4/Gits/BigDay/STD-visual/node_modules/use-nft/dist'

this is a code

import { FetchWrapper } from "use-nft"
import ethers from 'ethers';

const fetcher = ["ethers", { provider: ethers.getDefaultProvider() }]

const fetchWrapper = new FetchWrapper(fetcher)
async function fetchMetadata(collId, artId){
    const result = await fetchWrapper.fetchNft(
        collId,
        artId
      )
    return result;
}
export default fetchMetadata;

Do I HAVE to use React even using only fecher?

Can`t get an image using useNft or fetchNft

Try to get image data from NFT URI, eg: eip155:1/erc721:0xa3b7cee4e082183e69a03fc03476f28b12c545a7/5566. There is an issue for Brave browser and in a case with Chrome everything is ok. I try to get data using useNf() hook and fetchNft() function with the same result.
image

Could you please add Brave browser supporting to your lib?

When using OpenSea Polygon Minting Contract, no NFT image is found

When using OpenSea Polygon Minting Contract 0x2953399124F0cBB46d2CbACD8A89cF0599974963, no NFT image is found.
For example, this NFT on open sea:

https://opensea.io/assets/matic/0x2953399124f0cbb46d2cbacd8a89cf0599974963/104266429279353206028968205808007262092071789057064791600420254975229853433857

is using smart contract 0x2953399124F0cBB46d2CbACD8A89cF0599974963 and token 104266429279353206028968205808007262092071789057064791600420254975229853433857
yet, useNFT is unable to find the image

Fetch the list of NFTs owned by an account

I was able to use this library to pull a token using the contract address + the tokenId.
What I want to know now is to only fetch the TokenIds that belong for a given user (by connecting his Metamask wallet) and fetching that information from it.

I tried to look for example but could not find any.
Is this something possible using this library?

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.