Catalog npm
Back to packageRELEASE DETAILS

0.0.1

npm

@0xessential/use-many-indexers

Fallback to alternative NFT metadata indexing providers when your primary service fails

hooksreactreact-hooks
Published7 Oct 2022
Direct dependencies42
Transitive dependenciesNot calculated

README

Current package documentation

use-many-indexers

React hook for fetching NFT metadata serially from multiple indexers.

NFT metadata indexing services face an immense challenge in covering edge cases for every NFT on multiple blockchains.

This React library allows developers to specify an array of indexing services, and each will be queried in order until a successful response is returned.

We recommend using this project to catch or rescue token metadata when your primary service fails. The library does not support fetching metadata for an array of NFTs - you should choose a primary indexing service that provides metadata for many NFTs in one query, then use this library as a fallback to fetch any metadata your primary service missed.

Status

This project is in beta. 0xEssential got tired of trying to find the best indexer, so we built this project to serve our own needs. We're happy to share it with the community, but make no promises about maintenance or additional functionality.

TODOs We Wont Do But You Can

  • More metadata fields: The package returns a barebones object with name and imageUrl strings. We'd be happy to return more data, but standardizing API responses and TS types across a handful of providers is a lot of busy work. PRs welcome!

  • More indexers & indexer docs: We're pretty happy with coverage we get from the providers we integrated and rarely fall back to RPC calls. We've also standardized an API for adding indexing services and expect that indexers who want to be integrated will submit PRs for their own services.

  • Tests: lol

  • Logging: ok we might actually want to make logging better. Using console logs now, should probably use a Logger that can be configured. But it's also kinda nice to see in the console which service provides the best data.

Install

 yarn add @0xessential/use-many-indexers

MetadataProvider

Import

Import the <MetadataProvider> to wrap any components that require NFT metadata. Typically you'll add this to an outer component like NextJS _app.tsx, but we recommend adding the context provider as deep in your React component tree as possible for best performance.

  // _app.tsx
  import type { AppProps } from 'next/app';
  import { MetadataProvider } from '@0xessential/use-many-indexers';
  import React from 'react';

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  export default MyApp;

Configure

Each supported indexer provides a configuration function that accepts arguments for authentication and network support. You must pass at least one indexer to the <MetadataProvider>.

Indexers will be queried in the order you provide them in MetadataProvider configuration.

We suggest working bottom up, first implementing the rpc indexer - this fallback fetches the tokenURI and then metadata directly from a contract. Then add configuration above until you're happy with coverage.

rpc

The rpc indexer provides a fallback that fetches an NFT's tokenURI from the NFT contract via an RPC provider. The user's browser then fetches metadata from the tokenURI.

This should be included last in your array of indexers as a final fallback, but is a sensible configuration to start with.

The rpc configuration expects an RPC URL for each network you support in your dapp, plus a function for resolving ipfs:// protocol URIs to an https protocol URL.

  //...
  import { MetadataProvider, rpc } from '@0xessential/use-many-indexers';
  //...

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider indexers={[
        rpc(
          {
            '1': process.env.MAINNET_RPC_URL,
            '137': process.env.POLYGON_RPC_URL
          },
          (hash) => url.replace('ipfs://', 'https://ipfs.io/ipfs/')
        )
      ]}>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  return MyApp;

Infura

The infura indexer provides a fallback that fetches an NFT's tokenURI from the NFT contract using an Infura RPC URL.

If you're an Infura customer, this option is a bit simpler than the rpc fallback - just provide your Infura API key and the package will query the correct network.

You'll also need to provide a function to resolve IPFS URIs like in the rpc indexer.

  //...
  import { MetadataProvider, infura } from '@0xessential/use-many-indexers';
  import { ipfs } from '~utils/network.ts';
  //...

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider indexers={[
        infura(
          process.env.INFURA_API_KEY,
          (hash) => ipfs(hash),
        )
      ]}>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  return MyApp;

Zora

Zora supports Ethereuem Mainnet and Goerli Testnet. An API key is optional. Coverage is very good but supports limited networks.

  //...
  import { MetadataProvider, zora } from '@0xessential/use-many-indexers';
  //...

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider indexers={[
        zora(process.env.ZORA_API_KEY_IS_OPTIONAL)
      ]}>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  return MyApp;

Alchemy

Alchemy requires an API key per network your dApp supports. Configuration is passed as a nested object - string keys for each chain ID you support, and an object value with apiKey and network keys.

  //...
  import { MetadataProvider, alchemy } from '@0xessential/use-many-indexers';
  import { Network } from 'alchemy-sdk';
  //...

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider indexers={[
        alchemy({
          '1': {
            apiKey: process.env.ALCHEMY_ETH_MAINNET_API_KEY,
            network: Network.ETH_MAINNET,
          },
          '137': {
            apiKey: process.env.ALCHEMY_POLYGON_MAINNET_API_KEY,
            network: Network.MATIC_MAINNET,
          }
        })
      ]}>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  return MyApp;

Center

Center supports Ethereuem and Polygon mainnets. An API key is required but works with every Center-supported chain.

  //...
  import { MetadataProvider, center } from '@0xessential/use-many-indexers';
  //...

  function MyApp({ Component, pageProps }: AppProps) {
    return (      
      <MetadataProvider indexers={[
        center(process.env.CENTER_API_KEY)
      ]}>
        <Component {...pageProps} />
      </MetadataProvider>
    );
  }

  return MyApp;

Usage

Once your <MetadataProvider> is configured you're ready to start catching missing metadata throughout your dapp.

The library provides a "lazy hook" useMetadataRescue that returns a callable function should you need to rescue metadata.

Consider the following example, a React component for an NFT Image based on next/image.

Remember that this library is intended to be used as a fallback for your primary indexing service.

Here we build a React component that expects an nft prop that includes contractAddress, tokenId and hopefully valid imageUrl values.

import { useMetadataRescue } from '@0xessential/use-many-indexers';
import Image from 'next/image';
import React, { useEffect, useState } from 'react';

const NFTImage = ({
  nft,
  alt,
  width,
  height,
}: {
  alt?: string;
  width: number;
  height: number;
  nft: { 
    tokenId: string;
    contractAddress: string;
    imageUrl: string;
  };
}) => {
  const [src, setSrc] = useState<string>(nft.imageUrl || '/placeholder.png');
  const rescueMetadata = useMetadataRescue();

  useEffect(() => {
    if (!nft.imageUrl) {
      const rescue = async () => {
        const md = await rescueMetadata({
          chainId: '1',
          contractAddress: nft.contractAddress,
          tokenId: nft.tokenId,
        });
        md?.imageUrl && setSrc(md?.imageUrl);
      };
      rescue();
    }
  }, []);

  return <Image src={src} alt={alt} width={width} height={height} />;
};

export default NFTImage;

If your primary metadata indexing service fails to provide an image URL, this component will query each configured indexer in order until a valid response is returned.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 47

Native requirements
42 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
@commitlint/cli
Environment
{
  "local_name": "@commitlint/cli",
  "alias": false,
  "peer_metadata": null
}
^13.1.0development
@commitlint/config-conventional
Environment
{
  "local_name": "@commitlint/config-conventional",
  "alias": false,
  "peer_metadata": null
}
^13.1.0development
@rollup/plugin-commonjs
Environment
{
  "local_name": "@rollup/plugin-commonjs",
  "alias": false,
  "peer_metadata": null
}
^20.0.0development
@rollup/plugin-json
Environment
{
  "local_name": "@rollup/plugin-json",
  "alias": false,
  "peer_metadata": null
}
^4.1.0development
@rollup/plugin-node-resolve
Environment
{
  "local_name": "@rollup/plugin-node-resolve",
  "alias": false,
  "peer_metadata": null
}
^13.0.4development
@rollup/plugin-typescript
Environment
{
  "local_name": "@rollup/plugin-typescript",
  "alias": false,
  "peer_metadata": null
}
^8.5.0development
@semantic-release/changelog
Environment
{
  "local_name": "@semantic-release/changelog",
  "alias": false,
  "peer_metadata": null
}
^5.0.1development
@semantic-release/git
Environment
{
  "local_name": "@semantic-release/git",
  "alias": false,
  "peer_metadata": null
}
^9.0.0development
@testing-library/react-hooks
Environment
{
  "local_name": "@testing-library/react-hooks",
  "alias": false,
  "peer_metadata": null
}
^7.0.1development
@types/jest
Environment
{
  "local_name": "@types/jest",
  "alias": false,
  "peer_metadata": null
}
^26.0.24development
@types/react
Environment
{
  "local_name": "@types/react",
  "alias": false,
  "peer_metadata": null
}
^18.0.20development
@typescript-eslint/eslint-plugin
Environment
{
  "local_name": "@typescript-eslint/eslint-plugin",
  "alias": false,
  "peer_metadata": null
}
^2.34.0development
@zoralabs/zdk
Environment
{
  "local_name": "@zoralabs/zdk",
  "alias": false,
  "peer_metadata": null
}
^2.1.8development
alchemy-sdk
Environment
{
  "local_name": "alchemy-sdk",
  "alias": false,
  "peer_metadata": null
}
^2.0.4development
commitizen
Environment
{
  "local_name": "commitizen",
  "alias": false,
  "peer_metadata": null
}
^4.2.4development
cz-conventional-changelog
Environment
{
  "local_name": "cz-conventional-changelog",
  "alias": false,
  "peer_metadata": null
}
^3.3.0development
eslint
Environment
{
  "local_name": "eslint",
  "alias": false,
  "peer_metadata": null
}
^6.8.0development
eslint-config-airbnb-typescript
Environment
{
  "local_name": "eslint-config-airbnb-typescript",
  "alias": false,
  "peer_metadata": null
}
^7.2.1development
eslint-config-prettier
Environment
{
  "local_name": "eslint-config-prettier",
  "alias": false,
  "peer_metadata": null
}
^6.15.0development
eslint-plugin-import
Environment
{
  "local_name": "eslint-plugin-import",
  "alias": false,
  "peer_metadata": null
}
^2.23.4development
eslint-plugin-jsx-a11y
Environment
{
  "local_name": "eslint-plugin-jsx-a11y",
  "alias": false,
  "peer_metadata": null
}
^6.4.1development
eslint-plugin-prettier
Environment
{
  "local_name": "eslint-plugin-prettier",
  "alias": false,
  "peer_metadata": null
}
^3.4.0development
eslint-plugin-react
Environment
{
  "local_name": "eslint-plugin-react",
  "alias": false,
  "peer_metadata": null
}
^7.24.0development
eslint-plugin-react-hooks
Environment
{
  "local_name": "eslint-plugin-react-hooks",
  "alias": false,
  "peer_metadata": null
}
^4.2.0development
ethers
Environment
{
  "local_name": "ethers",
  "alias": false,
  "peer_metadata": null
}
^5.7.1development
git-cz
Environment
{
  "local_name": "git-cz",
  "alias": false,
  "peer_metadata": null
}
^4.7.6development
husky
Environment
{
  "local_name": "husky",
  "alias": false,
  "peer_metadata": null
}
^7.0.1development
jest
Environment
{
  "local_name": "jest",
  "alias": false,
  "peer_metadata": null
}
^25.5.4development
lint-staged
Environment
{
  "local_name": "lint-staged",
  "alias": false,
  "peer_metadata": null
}
^11.1.1development
nft-react
Environment
{
  "local_name": "nft-react",
  "alias": false,
  "peer_metadata": null
}
^0.5.10development
prettier
Environment
{
  "local_name": "prettier",
  "alias": false,
  "peer_metadata": null
}
^2.3.2development
react
Environment
{
  "local_name": "react",
  "alias": false,
  "peer_metadata": null
}
17.0.2development
react-test-renderer
Environment
{
  "local_name": "react-test-renderer",
  "alias": false,
  "peer_metadata": null
}
^16.14.0development
rimraf
Environment
{
  "local_name": "rimraf",
  "alias": false,
  "peer_metadata": null
}
^3.0.2development
rollup
Environment
{
  "local_name": "rollup",
  "alias": false,
  "peer_metadata": null
}
^2.55.1development
rollup-plugin-filesize
Environment
{
  "local_name": "rollup-plugin-filesize",
  "alias": false,
  "peer_metadata": null
}
^9.1.1development
rollup-plugin-peer-deps-external
Environment
{
  "local_name": "rollup-plugin-peer-deps-external",
  "alias": false,
  "peer_metadata": null
}
^2.2.4development
rollup-plugin-sourcemaps
Environment
{
  "local_name": "rollup-plugin-sourcemaps",
  "alias": false,
  "peer_metadata": null
}
^0.6.3development
rollup-plugin-terser
Environment
{
  "local_name": "rollup-plugin-terser",
  "alias": false,
  "peer_metadata": null
}
^7.0.2development
semantic-release
Environment
{
  "local_name": "semantic-release",
  "alias": false,
  "peer_metadata": null
}
^17.4.4development
ts-jest
Environment
{
  "local_name": "ts-jest",
  "alias": false,
  "peer_metadata": null
}
^25.5.1development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
^4.8.3development
@zoralabs/zdk
Environment
{
  "local_name": "@zoralabs/zdk",
  "alias": false,
  "peer_metadata": null
}
^2.1.8peer
alchemy-sdk
Environment
{
  "local_name": "alchemy-sdk",
  "alias": false,
  "peer_metadata": null
}
^2.0.4peer
ethers
Environment
{
  "local_name": "ethers",
  "alias": false,
  "peer_metadata": null
}
^5.7.1peer
nft-react
Environment
{
  "local_name": "nft-react",
  "alias": false,
  "peer_metadata": null
}
^0.5.10peer
react
Environment
{
  "local_name": "react",
  "alias": false,
  "peer_metadata": null
}
^16.14.0peer

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "integrity": "sha512-3j+WLxgkPhYB3qd5O9kYqF/jWTZPmPokVIO515J0AK/h8Vs9kXy2Mjoyff8ndTVJ3lLFdeiLbEP/g6TAlvpE2Q==",
  "shasum": "4170483248120aaa1392a9c0d3feda497ea90312",
  "tarball": "https://registry.npmjs.org/@0xessential/use-many-indexers/-/use-many-indexers-0.0.1.tgz",
  "fileCount": 19,
  "unpackedSize": 158848,
  "signatures": [
    {
      "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
      "sig": "MEQCID8XBXOFZo22gGk7geynlKCr8kU1WDdc7rI3lujbpIzRAiAh4UiFwQgNRxKXipXhEeyBpRQ3KL3lQOmogLS0xtshqg=="
    }
  ],
  "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjP6q+ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoBGw/+NCG9bS4AF0Qc5oaLtflgpN2i8IvpChRDTMZuQ/jw/N6BnMXI\r\nkUhBmt0ChoAf8RYnQMm9WyapKN0jixqU6gfaem5OvdtzFUQGd9z8PDw6zsId\r\nQkll62gx9oWyP8OVcI6VM0cnUGmay41rOdj20LKrmr2cVetsp1BY3PfwRaaV\r\nNqD0gvfzIXsJ8VRaeBJYNkiyxD6q5JACzOqc+pysJQlDi5SUBe/s1TXQJfWh\r\nwrNxpKznDFSZZCCGrYVnejQJdHqx87Mya41PolwUmzPM67TMvj6/gmBnpmbF\r\nYpsNAygHzf0Pc+yh7wYG2nuuIFdAiXsTN5c5mPzu9v69tZkgF/GZbMYPxjKt\r\nzQUyfLScvCkdtaYEYNCA59LPiE3n7EBd2SxZNKtNSc3TWfYl7rt6OwSA1F1/\r\nwF7feq8q8b7kUYBV3p6Abnj7AgA3yxLUQvz5RdIZcYrIwBML7IBPU0BqtKuK\r\nFkYMGwUUBwoXOKq/AEY3ZLr4nbbZM3KMPsq0U/qTjqMszdWTkyMCMpMxQSN7\r\n7WXp0ccwNYd4BwnzdpfC2KzcZtuGS4t0a+1PbqCgIABEtrfkn5gUIJqgsI8J\r\nUaHL8MbbdxtXR9livfvRN/Fnrj1oWLPVOtXVPAASEyROp9rg2ofOqgQGiWpa\r\neJKGUQd00KM/BZWC0W5yB6tw1xY2fHBkfQg=\r\n=neyc\r\n-----END PGP SIGNATURE-----\r\n"
}

Repository

PENDING
0xessential/use-many-indexers

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 23:07 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:53da4c9cf0b78058bbd51ce91a34f26ef78ce0875c4469e06b74ada884aa0087

Other collected versions

View all collected versions →