@0xessential/use-many-indexers
v0.0.1
Published
Fallback to alternative NFT metadata indexing providers when your primary service fails
Downloads
5
Readme
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
andimageUrl
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.