diff --git a/packages/thirdweb/package.json b/packages/thirdweb/package.json index 5352f6e31fe..9dc4d3d71f2 100644 --- a/packages/thirdweb/package.json +++ b/packages/thirdweb/package.json @@ -197,6 +197,7 @@ "test:watch": "vitest -c ./test/vitest.config.ts dev", "test": "CI=true vitest -c ./test/vitest.config.ts --coverage --retry=3 --bail=1 --pool=forks", "test:cov": "vitest dev -c ./test/vitest.config.ts --coverage", + "test:dev": "vitest dev -c ./test/vitest.config.ts", "typedoc": "node scripts/typedoc.mjs", "update-version": "node scripts/version.mjs" }, diff --git a/packages/thirdweb/src/extensions/erc721/read/getNFT.ts b/packages/thirdweb/src/extensions/erc721/read/getNFT.ts index d54beb00dc9..f9569e0f095 100644 --- a/packages/thirdweb/src/extensions/erc721/read/getNFT.ts +++ b/packages/thirdweb/src/extensions/erc721/read/getNFT.ts @@ -34,18 +34,34 @@ export async function getNFT( options: BaseTransactionOptions, ): Promise> { const [uri, owner] = await Promise.all([ - tokenURI(options), + tokenURI(options).catch(() => null), options.includeOwner - ? import("./ownerOf.js").then((m) => m.ownerOf(options)) + ? import("./ownerOf.js").then((m) => m.ownerOf(options)).catch(() => null) : null, ]); + if (!uri) { + return parseNFT( + { + id: options.tokenId, + type: "ERC721", + uri: "", + }, + { + tokenId: options.tokenId, + tokenUri: "", + type: "ERC721", + owner, + }, + ); + } + return parseNFT( await fetchTokenMetadata({ client: options.contract.client, tokenId: options.tokenId, tokenUri: uri, - }), + }).catch(() => {}), { tokenId: options.tokenId, tokenUri: uri, diff --git a/packages/thirdweb/src/extensions/erc721/read/getNFTs.test.ts b/packages/thirdweb/src/extensions/erc721/read/getNFTs.test.ts new file mode 100644 index 00000000000..ec932a3bb4c --- /dev/null +++ b/packages/thirdweb/src/extensions/erc721/read/getNFTs.test.ts @@ -0,0 +1,378 @@ +import { describe, it, expect, vi, afterEach } from "vitest"; + +import { getNFTs } from "./getNFTs.js"; +import { DOODLES_CONTRACT } from "~test/test-contracts.js"; + +const fetchSpy = vi.spyOn(globalThis, "fetch"); + +describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFTs", () => { + afterEach(() => { + fetchSpy.mockClear(); + }); + it("works for a contract with 0 indexed NFTs", async () => { + const nfts = await getNFTs({ + contract: DOODLES_CONTRACT, + count: 5, + }); + + expect(nfts.length).toBe(5); + expect(nfts).toMatchInlineSnapshot(` + [ + { + "id": 0n, + "metadata": { + "attributes": [ + { + "trait_type": "face", + "value": "mustache", + }, + { + "trait_type": "hair", + "value": "purple long", + }, + { + "trait_type": "body", + "value": "blue and yellow jacket", + }, + { + "trait_type": "background", + "value": "green", + }, + { + "trait_type": "head", + "value": "tan", + }, + ], + "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.", + "image": "ipfs://QmUEfFfwAh4wyB5UfHCVPUxis4j4Q4kJXtm5x5p3g1fVUn", + "name": "Doodle #0", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/0", + "type": "ERC721", + }, + { + "id": 1n, + "metadata": { + "attributes": [ + { + "trait_type": "face", + "value": "holographic beard", + }, + { + "trait_type": "hair", + "value": "white bucket cap", + }, + { + "trait_type": "body", + "value": "purple sweater with satchel", + }, + { + "trait_type": "background", + "value": "grey", + }, + { + "trait_type": "head", + "value": "gradient 2", + }, + ], + "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.", + "image": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9", + "name": "Doodle #1", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1", + "type": "ERC721", + }, + { + "id": 2n, + "metadata": { + "attributes": [ + { + "trait_type": "face", + "value": "designer glasses", + }, + { + "trait_type": "hair", + "value": "poopie", + }, + { + "trait_type": "body", + "value": "blue fleece", + }, + { + "trait_type": "background", + "value": "yellow", + }, + { + "trait_type": "head", + "value": "purple", + }, + ], + "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.", + "image": "ipfs://QmbvZ2hbF3nEq5r3ijMEiSGssAmJvtyFwiejTAGHv74LR5", + "name": "Doodle #2", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/2", + "type": "ERC721", + }, + { + "id": 3n, + "metadata": { + "attributes": [ + { + "trait_type": "face", + "value": "designer glasses", + }, + { + "trait_type": "hair", + "value": "holographic mohawk", + }, + { + "trait_type": "body", + "value": "pink fleece", + }, + { + "trait_type": "background", + "value": "gradient 1", + }, + { + "trait_type": "head", + "value": "pale", + }, + ], + "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.", + "image": "ipfs://QmVpwaCqLut3wqwB5KSQr2fGnbLuJt5e3LhNvzvcisewZB", + "name": "Doodle #3", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/3", + "type": "ERC721", + }, + { + "id": 4n, + "metadata": { + "attributes": [ + { + "trait_type": "face", + "value": "happy", + }, + { + "trait_type": "hair", + "value": "purple long", + }, + { + "trait_type": "body", + "value": "spotted hoodie", + }, + { + "trait_type": "background", + "value": "gradient 2", + }, + { + "trait_type": "head", + "value": "purple", + }, + ], + "description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.", + "image": "ipfs://QmcyuFVLbfBmSeQ9ynu4dk67r97nB1abEekotuVuRGWedm", + "name": "Doodle #4", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/4", + "type": "ERC721", + }, + ] + `); + }); + + it("works for a contract with `1` indexed NFTs", async () => { + const nfts = await getNFTs({ + contract: { + ...DOODLES_CONTRACT, + address: "0x5D62Fb8dcD9b480960f55956fBDD8D9F07f2B402", + }, + count: 5, + }); + + expect(nfts.length).toBe(5); + expect(nfts).toMatchInlineSnapshot(` + [ + { + "id": 0n, + "metadata": { + "id": 0n, + "type": "ERC721", + "uri": "", + }, + "owner": null, + "supply": 1n, + "tokenURI": "", + "type": "ERC721", + }, + { + "id": 1n, + "metadata": { + "attributes": [ + { + "trait_type": "Background", + "value": "Milky Way", + }, + { + "trait_type": "Body", + "value": "Pyro", + }, + { + "trait_type": "Head", + "value": "Match-Fire", + }, + { + "trait_type": "Face", + "value": "Spite", + }, + { + "trait_type": "Cloth", + "value": "Windbreaker-White", + }, + { + "trait_type": "Fire", + "value": "V-Flame", + }, + ], + "description": "Enter into a world full of rebels", + "image": "ipfs://bafybeigyyqe47wqitc7lczextz5epf5rx52iq4uw6fwavcys7uv7nv5uli/1.png", + "name": "MISTFITS #1", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://bafybeid3jgxafevtfxw5gmlxrvftxddkmjnzfq4o6jfgzqdq7zyi5qz6cm/1.json", + "type": "ERC721", + }, + { + "id": 2n, + "metadata": { + "attributes": [ + { + "trait_type": "Background", + "value": "Orange", + }, + { + "trait_type": "Body", + "value": "Sora", + }, + { + "trait_type": "Head", + "value": "Air-Mass", + }, + { + "trait_type": "Face", + "value": "Surrender", + }, + { + "trait_type": "Cloth", + "value": "Sifu yip", + }, + ], + "description": "Enter into a world full of rebels", + "image": "ipfs://bafybeigyyqe47wqitc7lczextz5epf5rx52iq4uw6fwavcys7uv7nv5uli/2.png", + "name": "MISTFITS #2", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://bafybeid3jgxafevtfxw5gmlxrvftxddkmjnzfq4o6jfgzqdq7zyi5qz6cm/2.json", + "type": "ERC721", + }, + { + "id": 3n, + "metadata": { + "attributes": [ + { + "trait_type": "Background", + "value": "Yellow", + }, + { + "trait_type": "Body", + "value": "Cyan", + }, + { + "trait_type": "Tattoo", + "value": "Votary", + }, + { + "trait_type": "Eye", + "value": "Fury", + }, + { + "trait_type": "Cloth", + "value": "Chef-Black", + }, + { + "trait_type": "Head", + "value": "Round Hat-Black", + }, + { + "trait_type": "Hand", + "value": "Puppet", + }, + ], + "description": "Enter into a world full of rebels", + "image": "ipfs://bafybeigyyqe47wqitc7lczextz5epf5rx52iq4uw6fwavcys7uv7nv5uli/3.png", + "name": "MISTFITS #3", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://bafybeid3jgxafevtfxw5gmlxrvftxddkmjnzfq4o6jfgzqdq7zyi5qz6cm/3.json", + "type": "ERC721", + }, + { + "id": 4n, + "metadata": { + "attributes": [ + { + "trait_type": "Background", + "value": "Purple", + }, + { + "trait_type": "Body", + "value": "Warm", + }, + { + "trait_type": "Tattoo", + "value": "Hunting", + }, + { + "trait_type": "Eye", + "value": "Endurance", + }, + { + "trait_type": "Cloth", + "value": "Purple down undershirt", + }, + { + "trait_type": "Head", + "value": "Beanie-White", + }, + { + "trait_type": "Hand", + "value": "Sound", + }, + ], + "description": "Enter into a world full of rebels", + "image": "ipfs://bafybeigyyqe47wqitc7lczextz5epf5rx52iq4uw6fwavcys7uv7nv5uli/4.png", + "name": "MISTFITS #4", + }, + "owner": null, + "supply": 1n, + "tokenURI": "ipfs://bafybeid3jgxafevtfxw5gmlxrvftxddkmjnzfq4o6jfgzqdq7zyi5qz6cm/4.json", + "type": "ERC721", + }, + ] + `); + }); +}); diff --git a/packages/thirdweb/src/extensions/erc721/read/nextTokenIdToMint.ts b/packages/thirdweb/src/extensions/erc721/read/nextTokenIdToMint.ts index a98ef2b3157..f787b4fa9f3 100644 --- a/packages/thirdweb/src/extensions/erc721/read/nextTokenIdToMint.ts +++ b/packages/thirdweb/src/extensions/erc721/read/nextTokenIdToMint.ts @@ -17,6 +17,6 @@ export function nextTokenIdToMint( ): Promise { return readContract({ ...options, - method: "function nextTokenIdToMint() returns (uint256)", + method: "function nextTokenIdToMint() returns (uint256)", }); }