Skip to content

Commit 0c36874

Browse files
[SDK] Use insight for erc721/getNFTs and erc721/getOwnedNFTs
1 parent e29f749 commit 0c36874

File tree

32 files changed

+1053
-199
lines changed

32 files changed

+1053
-199
lines changed

.changeset/purple-bats-march.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Use insight for erc821/getNFT, erc721/getNFTs and erc721/getOwnedNFTs
6+
7+
Standard ERC721 getNFT, getNFTs and getOwnedNFTs now use insight, our in house indexer by default. If indexer is not availbale, will fallback to RPC.
8+
9+
You can also use the indexer directly using the Insight API:
10+
11+
for an entire collection
12+
13+
```ts
14+
import { Insight } from "thirdweb";
15+
16+
const events = await Insight.getContractNFTs({
17+
client,
18+
chains: [sepolia],
19+
contractAddress: "0x1234567890123456789012345678901234567890",
20+
});
21+
```
22+
23+
or for a single NFT
24+
25+
```ts
26+
import { Insight } from "thirdweb";
27+
28+
const events = await Insight.getNFT({
29+
client,
30+
chains: [sepolia],
31+
contractAddress: "0x1234567890123456789012345678901234567890",
32+
tokenId: 1n,
33+
});
34+
```

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ env:
1616
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
1717
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
1818
TW_SECRET_KEY: ${{ secrets.TW_SECRET_KEY }}
19+
TW_CLIENT_ID: ${{ secrets.TW_CLIENT_ID }}
1920
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
2021

2122
jobs:

apps/dashboard/src/@/actions/getWalletNFTs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ async function getWalletNFTsFromInsight(params: {
194194
tokenURI: nft.metadata_url,
195195
type: nft.token_type === "erc721" ? "ERC721" : "ERC1155",
196196
supply: nft.balance,
197+
tokenAddress: nft.contract.address,
198+
chainId: nft.contract.chain_id,
197199
};
198200

199201
return walletNFT;

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
205205
owner: nft.owner,
206206
type: "ERC721",
207207
tokenURI: nft.tokenURI,
208+
chainId: nft.chainId,
209+
tokenAddress: nft.tokenAddress,
208210
};
209211
}
210212
return {
@@ -216,6 +218,8 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
216218
owner: nft.owner,
217219
type: "ERC1155",
218220
tokenURI: nft.tokenURI,
221+
chainId: nft.chainId,
222+
tokenAddress: nft.tokenAddress,
219223
};
220224
}) as WalletNFT[];
221225
}, [ownedNFTs, form]);

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/NFTCards.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const dummyMetadata: (idx: number) => NFTWithContract = (idx) => ({
2121
owner: `0x_fake_${idx}`,
2222
type: "ERC721",
2323
supply: 1n,
24+
tokenAddress: ZERO_ADDRESS,
2425
});
2526

2627
interface NFTCardsProps {

packages/thirdweb/src/event/actions/get-events.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type {
55
ExtractAbiEventNames,
66
} from "abitype";
77
import { type Log, formatLog } from "viem";
8-
import { getChainServices } from "../../chains/utils.js";
98
import { resolveContractAbi } from "../../contract/actions/resolve-abi.js";
109
import type { ThirdwebContract } from "../../contract/contract.js";
1110
import { getContractEvents as getContractEventsInsight } from "../../insight/get-events.js";
@@ -197,7 +196,7 @@ export async function getContractEvents<
197196
),
198197
);
199198
} catch (e) {
200-
console.warn("Error fetching from insight", e);
199+
console.warn("Error fetching from insight, falling back to rpc", e);
201200
// fetch from rpc
202201
logs = await Promise.all(
203202
logsParams.map((ethLogParams) => eth_getLogs(rpcRequest, ethLogParams)),
@@ -225,17 +224,6 @@ async function getLogsFromInsight(options: {
225224
}): Promise<Log[]> {
226225
const { params, contract } = options;
227226

228-
const chainServices = await getChainServices(contract.chain);
229-
const insightEnabled = chainServices.some(
230-
(c) => c.service === "insight" && c.enabled,
231-
);
232-
233-
if (!insightEnabled) {
234-
throw new Error(
235-
`Insight is not available for chainId ${contract.chain.id}`,
236-
);
237-
}
238-
239227
const fromBlock =
240228
typeof params.fromBlock === "bigint" ? Number(params.fromBlock) : undefined;
241229

packages/thirdweb/src/extensions/erc1155/read/getNFT.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc1155.getNFT", () => {
1111
});
1212
expect(nft).toMatchInlineSnapshot(`
1313
{
14+
"chainId": 1,
1415
"id": 2n,
1516
"metadata": {
1617
"animation_url": "ipfs://QmYoM63qaumQznBRx38tQjkY4ewbymeFb2KWBhkfMqNHax/3.mp4",
@@ -36,6 +37,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc1155.getNFT", () => {
3637
},
3738
"owner": null,
3839
"supply": 2519n,
40+
"tokenAddress": "0x42d3641255C946CC451474295d29D3505173F22A",
3941
"tokenURI": "ipfs://QmbMXdbnNUAuGRoY6c6G792c6T9utfaBGqRUaMaRUf52Cb/2",
4042
"type": "ERC1155",
4143
}

packages/thirdweb/src/extensions/erc1155/read/getNFT.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export async function getNFT(
5858
type: "ERC1155",
5959
owner: null,
6060
supply,
61+
tokenAddress: options.contract.address,
62+
chainId: options.contract.chain.id,
6163
},
6264
);
6365
}

packages/thirdweb/src/extensions/erc20/read/getBalance.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export type GetBalanceResult = {
2323
displayValue: string;
2424
symbol: string;
2525
name: string;
26+
tokenAddress: string;
27+
chainId: number;
2628
};
2729

2830
/**
@@ -48,5 +50,7 @@ export async function getBalance(
4850
...currencyMetadata,
4951
value: balanceWei,
5052
displayValue: toTokens(balanceWei, currencyMetadata.decimals),
53+
tokenAddress: options.contract.address,
54+
chainId: options.contract.chain.id,
5155
};
5256
}

packages/thirdweb/src/extensions/erc721/read/getNFT.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,117 @@
11
import { describe, expect, it } from "vitest";
22
import { DOODLES_CONTRACT } from "~test/test-contracts.js";
3+
import { TEST_CLIENT } from "../../../../test/src/test-clients.js";
34
import { getNFT } from "./getNFT.js";
45

56
describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
7+
it("without owner using indexer", async () => {
8+
const clientId = TEST_CLIENT.clientId;
9+
const nft = await getNFT({
10+
contract: { ...DOODLES_CONTRACT },
11+
tokenId: 1n,
12+
includeOwner: false,
13+
});
14+
expect(nft).toMatchInlineSnapshot(`
15+
{
16+
"chainId": 1,
17+
"id": 1n,
18+
"metadata": {
19+
"attributes": [
20+
{
21+
"trait_type": "face",
22+
"value": "holographic beard",
23+
},
24+
{
25+
"trait_type": "hair",
26+
"value": "white bucket cap",
27+
},
28+
{
29+
"trait_type": "body",
30+
"value": "purple sweater with satchel",
31+
},
32+
{
33+
"trait_type": "background",
34+
"value": "grey",
35+
},
36+
{
37+
"trait_type": "head",
38+
"value": "gradient 2",
39+
},
40+
],
41+
"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.",
42+
"image": "https://${clientId}.ipfscdn.io/ipfs/QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
43+
"image_url": "https://${clientId}.ipfscdn.io/ipfs/QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
44+
"name": "Doodle #1",
45+
"uri": "https://${clientId}.ipfscdn.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
46+
},
47+
"owner": null,
48+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
49+
"tokenURI": "https://${clientId}.ipfscdn.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
50+
"type": "ERC721",
51+
}
52+
`);
53+
});
54+
55+
it("with owner using indexer", async () => {
56+
const nft = await getNFT({
57+
contract: { ...DOODLES_CONTRACT },
58+
tokenId: 1n,
59+
includeOwner: true,
60+
});
61+
expect(nft).toMatchInlineSnapshot(`
62+
{
63+
"chainId": 1,
64+
"id": 1n,
65+
"metadata": {
66+
"attributes": [
67+
{
68+
"trait_type": "face",
69+
"value": "holographic beard",
70+
},
71+
{
72+
"trait_type": "hair",
73+
"value": "white bucket cap",
74+
},
75+
{
76+
"trait_type": "body",
77+
"value": "purple sweater with satchel",
78+
},
79+
{
80+
"trait_type": "background",
81+
"value": "grey",
82+
},
83+
{
84+
"trait_type": "head",
85+
"value": "gradient 2",
86+
},
87+
],
88+
"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.",
89+
"image": "https://${clientId}.ipfscdn.io/ipfs/QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
90+
"image_url": "https://${clientId}.ipfscdn.io/ipfs/QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
91+
"name": "Doodle #1",
92+
"owner_addresses": [
93+
"0xbe9936fcfc50666f5425fde4a9decc59cef73b24",
94+
],
95+
"uri": "https://${clientId}.ipfscdn.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
96+
},
97+
"owner": "0xbe9936fcfc50666f5425fde4a9decc59cef73b24",
98+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
99+
"tokenURI": "https://${clientId}.ipfscdn.io/ipfs/QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
100+
"type": "ERC721",
101+
}
102+
`);
103+
});
104+
6105
it("without owner", async () => {
7106
const nft = await getNFT({
8107
contract: { ...DOODLES_CONTRACT },
9108
tokenId: 1n,
10109
includeOwner: false,
110+
useIndexer: false,
11111
});
12112
expect(nft).toMatchInlineSnapshot(`
13113
{
114+
"chainId": 1,
14115
"id": 1n,
15116
"metadata": {
16117
"attributes": [
@@ -40,6 +141,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
40141
"name": "Doodle #1",
41142
},
42143
"owner": null,
144+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
43145
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
44146
"type": "ERC721",
45147
}
@@ -51,9 +153,11 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
51153
contract: { ...DOODLES_CONTRACT },
52154
tokenId: 1n,
53155
includeOwner: true,
156+
useIndexer: false,
54157
});
55158
expect(nft).toMatchInlineSnapshot(`
56159
{
160+
"chainId": 1,
57161
"id": 1n,
58162
"metadata": {
59163
"attributes": [
@@ -83,6 +187,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
83187
"name": "Doodle #1",
84188
},
85189
"owner": "0xbE9936FCFC50666f5425FDE4A9decC59cEF73b24",
190+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
86191
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
87192
"type": "ERC721",
88193
}

0 commit comments

Comments
 (0)