diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx index f7bfe9e33af..9ba7ca862d0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx @@ -42,6 +42,9 @@ export const ContractNFTPage: React.FC = ({ const isSetSharedMetadataSupported = ERC721Ext.isSetSharedMetadataSupported(functionSelectors); + const isTokenByIndexSupported = + ERC721Ext.isTokenByIndexSupported(functionSelectors); + const canRenderNFTTable = (() => { if (isErc721) { return ERC721Ext.isGetNFTsSupported(functionSelectors); @@ -101,7 +104,11 @@ export const ContractNFTPage: React.FC = ({ {canShowSupplyCards && } {canRenderNFTTable && ( - + )} ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx index d54062e6386..578a2a69f0c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx @@ -85,6 +85,7 @@ export const TokenIdPage: React.FC = ({ contract, tokenId: BigInt(tokenId || 0), includeOwner: true, + tokenByIndex: false, }, ); @@ -222,7 +223,11 @@ export const TokenIdPage: React.FC = ({ 8 + ? `${nft.id.toString().slice(0, 4)}...${nft.id.toString().slice(-4)}` + : nft.id?.toString() + } tooltip="Token ID" copyIconPosition="right" /> diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx index 3454ab07bf3..85d9d4dc7af 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx @@ -45,10 +45,12 @@ import { useReadContract } from "thirdweb/react"; interface ContractOverviewNFTGetAllProps { contract: ThirdwebContract; isErc721: boolean; + tokenByIndex: boolean; } export const NFTGetAllTable: React.FC = ({ contract, isErc721, + tokenByIndex, }) => { // if it's not erc721, it's erc1155 const isErc1155 = !isErc721; @@ -59,7 +61,10 @@ export const NFTGetAllTable: React.FC = ({ const cols: Column[] = [ { Header: "Token Id", - accessor: (row) => row.id?.toString(), + accessor: (row) => + row.id?.toString().length > 8 + ? `${row.id.toString().slice(0, 4)}...${row.id.toString().slice(-4)}` + : row.id?.toString(), Cell: (cell: CellProps) =>

{cell.value}

, }, { @@ -155,6 +160,7 @@ export const NFTGetAllTable: React.FC = ({ start: queryParams.start, count: queryParams.count, includeOwners: true, + tokenByIndex, }, ); diff --git a/packages/thirdweb/src/exports/extensions/erc721.ts b/packages/thirdweb/src/exports/extensions/erc721.ts index eda654f7b45..00efc090ca7 100644 --- a/packages/thirdweb/src/exports/extensions/erc721.ts +++ b/packages/thirdweb/src/exports/extensions/erc721.ts @@ -16,6 +16,7 @@ export { nextTokenIdToMint, isNextTokenIdToMintSupported, } from "../../extensions/erc721/__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; +export { isTokenByIndexSupported } from "../../extensions/erc721/__generated__/IERC721Enumerable/read/tokenByIndex.js"; export { ownerOf, type OwnerOfParams, diff --git a/packages/thirdweb/src/extensions/erc721/read/getNFT.ts b/packages/thirdweb/src/extensions/erc721/read/getNFT.ts index 6d8e1903833..22bcb7231f7 100644 --- a/packages/thirdweb/src/extensions/erc721/read/getNFT.ts +++ b/packages/thirdweb/src/extensions/erc721/read/getNFT.ts @@ -6,6 +6,7 @@ import { type TokenURIParams, tokenURI, } from "../__generated__/IERC721A/read/tokenURI.js"; +import { tokenByIndex } from "../__generated__/IERC721Enumerable/read/tokenByIndex.js"; export { isTokenURISupported as isGetNFTSupported } from "../__generated__/IERC721A/read/tokenURI.js"; @@ -19,6 +20,13 @@ export type GetNFTParams = Prettify< * Whether to include the owner of the NFT. */ includeOwner?: boolean; + /** + * Whether to check and fetch tokenID by index, in case of non-sequential IDs. + * + * It should be set to true if it's an ERC721Enumerable contract, and has `tokenByIndex` function. + * In this case, the provided tokenId will be considered as token-index and actual tokenId will be fetched from the contract. + */ + tokenByIndex?: boolean; } >; @@ -35,15 +43,37 @@ export type GetNFTParams = Prettify< * tokenId: 1n, * }); * ``` + * + * * @example + * ```ts + * import { getNFT } from "thirdweb/extensions/erc721"; + * + * + * const nft = await getNFT({ + * contract, + * tokenId: 1n, + * tokenByIndex: true // use this flag if the contract supports `tokenByIndex` and the above tokenId should be treated as an index. + * }); + * ``` */ export async function getNFT( options: BaseTransactionOptions, ): Promise { + let tokenId = options.tokenId; + if (options.tokenByIndex) { + try { + tokenId = await tokenByIndex({ + contract: options.contract, + index: options.tokenId, + }); + } catch {} + } + const [uri, owner] = await Promise.all([ - tokenURI(options).catch(() => null), + tokenURI({ contract: options.contract, tokenId }).catch(() => null), options.includeOwner ? import("../__generated__/IERC721A/read/ownerOf.js") - .then((m) => m.ownerOf(options)) + .then((m) => m.ownerOf({ contract: options.contract, tokenId })) .catch(() => null) : null, ]); @@ -51,12 +81,12 @@ export async function getNFT( if (!uri?.trim()) { return parseNFT( { - id: options.tokenId, + id: tokenId, type: "ERC721", uri: "", }, { - tokenId: options.tokenId, + tokenId, tokenUri: "", type: "ERC721", owner, @@ -67,15 +97,15 @@ export async function getNFT( return parseNFT( await fetchTokenMetadata({ client: options.contract.client, - tokenId: options.tokenId, + tokenId, tokenUri: uri, }).catch(() => ({ - id: options.tokenId, + id: tokenId, type: "ERC721", uri, })), { - tokenId: options.tokenId, + tokenId: tokenId, tokenUri: uri, type: "ERC721", owner, diff --git a/packages/thirdweb/src/extensions/erc721/read/getNFTs.ts b/packages/thirdweb/src/extensions/erc721/read/getNFTs.ts index 11f26d42845..e25f44488d8 100644 --- a/packages/thirdweb/src/extensions/erc721/read/getNFTs.ts +++ b/packages/thirdweb/src/extensions/erc721/read/getNFTs.ts @@ -34,6 +34,13 @@ export type GetNFTsParams = { * @default false */ includeOwners?: boolean; + /** + * Whether to check and fetch tokenID by index, in case of non-sequential IDs. + * + * It should be set to true if it's an ERC721Enumerable contract, and has `tokenByIndex` function. + * In this case, the provided tokenId will be considered as token-index and actual tokenId will be fetched from the contract. + */ + tokenByIndex?: boolean; }; /**