Skip to content

Commit 9c1c5f2

Browse files
committed
[TOOL-3789] Dashboard: Replace simplehash with insight (#6554)
1 parent 45cdb86 commit 9c1c5f2

File tree

24 files changed

+181
-326
lines changed

24 files changed

+181
-326
lines changed

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

Lines changed: 120 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@ import {
1010
isMoralisSupported,
1111
transformMoralisResponseToNFT,
1212
} from "lib/wallet/nfts/moralis";
13-
import {
14-
generateSimpleHashUrl,
15-
isSimpleHashSupported,
16-
transformSimpleHashResponseToNFT,
17-
} from "lib/wallet/nfts/simpleHash";
1813
import type { WalletNFT } from "lib/wallet/nfts/types";
14+
import { getVercelEnv } from "../../lib/vercel-utils";
15+
import { DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/env";
1916

2017
type WalletNFTApiReturn =
2118
| { result: WalletNFT[]; error?: undefined }
@@ -24,40 +21,20 @@ type WalletNFTApiReturn =
2421
export async function getWalletNFTs(params: {
2522
chainId: number;
2623
owner: string;
24+
isInsightSupported: boolean;
2725
}): Promise<WalletNFTApiReturn> {
2826
const { chainId, owner } = params;
29-
const supportedChainSlug = await isSimpleHashSupported(chainId);
3027

31-
if (supportedChainSlug && process.env.SIMPLEHASH_API_KEY) {
32-
const url = generateSimpleHashUrl({ chainSlug: supportedChainSlug, owner });
28+
if (params.isInsightSupported) {
29+
const response = await getWalletNFTsFromInsight({ chainId, owner });
3330

34-
const response = await fetch(url, {
35-
method: "GET",
36-
headers: {
37-
"X-API-KEY": process.env.SIMPLEHASH_API_KEY,
38-
},
39-
next: {
40-
revalidate: 10, // cache for 10 seconds
41-
},
42-
});
43-
44-
if (response.status >= 400) {
31+
if (!response.ok) {
4532
return {
46-
error: response.statusText,
33+
error: response.error,
4734
};
4835
}
4936

50-
try {
51-
const parsedResponse = await response.json();
52-
const result = await transformSimpleHashResponseToNFT(
53-
parsedResponse,
54-
owner,
55-
);
56-
57-
return { result };
58-
} catch {
59-
return { error: "error parsing response" };
60-
}
37+
return { result: response.data };
6138
}
6239

6340
if (isAlchemySupported(chainId)) {
@@ -115,3 +92,115 @@ export async function getWalletNFTs(params: {
11592

11693
return { error: "unsupported chain" };
11794
}
95+
96+
type OwnedNFTInsightResponse = {
97+
name: string;
98+
description: string;
99+
image_url: string;
100+
background_color: string;
101+
external_url: string;
102+
metadata_url: string;
103+
extra_metadata: {
104+
customImage?: string;
105+
customAnimationUrl?: string;
106+
animation_original_url?: string;
107+
image_original_url?: string;
108+
};
109+
collection: {
110+
name: string;
111+
description: string;
112+
extra_metadata: Record<string, string>;
113+
};
114+
contract: {
115+
chain_id: number;
116+
address: string;
117+
type: "erc1155" | "erc721";
118+
name: string;
119+
};
120+
owner_addresses: string[];
121+
token_id: string;
122+
balance: string;
123+
token_type: "erc1155" | "erc721";
124+
};
125+
126+
async function getWalletNFTsFromInsight(params: {
127+
chainId: number;
128+
owner: string;
129+
}): Promise<
130+
| {
131+
data: WalletNFT[];
132+
ok: true;
133+
}
134+
| {
135+
ok: false;
136+
error: string;
137+
}
138+
> {
139+
const { chainId, owner } = params;
140+
141+
const thirdwebDomain =
142+
getVercelEnv() === "production" ? "thirdweb" : "thirdweb-dev";
143+
const url = new URL(`https://insight.${thirdwebDomain}.com/v1/nfts`);
144+
url.searchParams.append("chain", chainId.toString());
145+
url.searchParams.append("limit", "10");
146+
url.searchParams.append("owner_address", owner);
147+
148+
const response = await fetch(url, {
149+
headers: {
150+
"x-client-id": DASHBOARD_THIRDWEB_CLIENT_ID,
151+
},
152+
});
153+
154+
if (!response.ok) {
155+
const errorMessage = await response.text();
156+
return {
157+
ok: false,
158+
error: errorMessage,
159+
};
160+
}
161+
162+
const nftsResponse = (await response.json()) as {
163+
data: OwnedNFTInsightResponse[];
164+
};
165+
166+
const isDev = getVercelEnv() !== "production";
167+
168+
// NOTE: ipfscdn.io/ to thirdwebstorage-dev.com/ replacement is temporary
169+
// This should be fixed in the insight dev endpoint
170+
171+
const walletNFTs = nftsResponse.data.map((nft) => {
172+
const walletNFT: WalletNFT = {
173+
id: nft.token_id,
174+
contractAddress: nft.contract.address,
175+
metadata: {
176+
uri: isDev
177+
? nft.metadata_url.replace("ipfscdn.io/", "thirdwebstorage-dev.com/")
178+
: nft.metadata_url,
179+
name: nft.name,
180+
description: nft.description,
181+
image: isDev
182+
? nft.image_url.replace("ipfscdn.io/", "thirdwebstorage-dev.com/")
183+
: nft.image_url,
184+
animation_url: isDev
185+
? nft.extra_metadata.animation_original_url?.replace(
186+
"ipfscdn.io/",
187+
"thirdwebstorage-dev.com/",
188+
)
189+
: nft.extra_metadata.animation_original_url,
190+
external_url: nft.external_url,
191+
background_color: nft.background_color,
192+
},
193+
owner: params.owner,
194+
tokenURI: nft.metadata_url,
195+
type: nft.token_type === "erc721" ? "ERC721" : "ERC1155",
196+
supply: nft.balance,
197+
};
198+
199+
return walletNFT;
200+
});
201+
202+
return {
203+
data: walletNFTs,
204+
ok: true,
205+
};
206+
}

apps/dashboard/src/@3rdweb-sdk/react/hooks/useWalletNFTs.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import invariant from "tiny-invariant";
55
export function useWalletNFTs(params: {
66
chainId: number;
77
walletAddress?: string;
8+
isInsightSupported: boolean;
89
}) {
910
return useQuery({
1011
queryKey: ["walletNfts", params.chainId, params.walletAddress],
@@ -13,6 +14,7 @@ export function useWalletNFTs(params: {
1314
return getWalletNFTs({
1415
chainId: params.chainId,
1516
owner: params.walletAddress,
17+
isInsightSupported: params.isInsightSupported,
1618
});
1719
},
1820
enabled: !!params.walletAddress,

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only";
1313
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
1414
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
1515
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
16-
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
1716
import { PlusIcon } from "lucide-react";
1817
import { useState } from "react";
1918
import type { ThirdwebContract } from "thirdweb";
@@ -25,6 +24,7 @@ interface CreateListingButtonProps {
2524
createText?: string;
2625
type?: "direct-listings" | "english-auctions";
2726
twAccount: Account | undefined;
27+
isInsightSupported: boolean;
2828
}
2929

3030
const LISTING_MODES = ["Select NFT", "Manual"] as const;
@@ -34,20 +34,20 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
3434
type,
3535
contract,
3636
twAccount,
37+
isInsightSupported,
3738
...restButtonProps
3839
}) => {
3940
const address = useActiveAccount()?.address;
4041
const [open, setOpen] = useState(false);
4142
const [listingMode, setListingMode] =
4243
useState<(typeof LISTING_MODES)[number]>("Select NFT");
4344

44-
const simplehashQuery = useSimplehashSupport(contract.chain.id);
45-
4645
const isSupportedChain =
4746
contract.chain.id &&
48-
(simplehashQuery.data ||
47+
(isInsightSupported ||
4948
isAlchemySupported(contract.chain.id) ||
5049
isMoralisSupported(contract.chain.id));
50+
5151
return (
5252
<ListerOnly contract={contract}>
5353
<Sheet open={open} onOpenChange={setOpen}>
@@ -83,6 +83,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
8383
actionText={createText}
8484
setOpen={setOpen}
8585
mode={listingMode === "Select NFT" ? "automatic" : "manual"}
86+
isInsightSupported={isInsightSupported}
8687
/>
8788
</div>
8889
</>
@@ -95,6 +96,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
9596
actionText={createText}
9697
setOpen={setOpen}
9798
mode="manual"
99+
isInsightSupported={isInsightSupported}
98100
/>
99101
</div>
100102
)}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { useAllChainsData } from "hooks/chains/allChains";
2121
import { useTxNotifications } from "hooks/useTxNotifications";
2222
import { isAlchemySupported } from "lib/wallet/nfts/alchemy";
2323
import { isMoralisSupported } from "lib/wallet/nfts/moralis";
24-
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
2524
import type { WalletNFT } from "lib/wallet/nfts/types";
2625
import { CircleAlertIcon, InfoIcon } from "lucide-react";
2726
import Link from "next/link";
@@ -87,6 +86,7 @@ type CreateListingsFormProps = {
8786
mode: "automatic" | "manual";
8887
type?: "direct-listings" | "english-auctions";
8988
twAccount: Account | undefined;
89+
isInsightSupported: boolean;
9090
};
9191

9292
const auctionTimes = [
@@ -106,16 +106,17 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
106106
setOpen,
107107
twAccount,
108108
mode,
109+
isInsightSupported,
109110
}) => {
110111
const trackEvent = useTrack();
111112
const chainId = contract.chain.id;
112113
const { idToChain } = useAllChainsData();
113114
const network = idToChain.get(chainId);
114115
const [isFormLoading, setIsFormLoading] = useState(false);
115-
const simplehashQuery = useSimplehashSupport(contract.chain.id);
116+
116117
const isSupportedChain =
117118
chainId &&
118-
(!!simplehashQuery.data ||
119+
(isInsightSupported ||
119120
isAlchemySupported(chainId) ||
120121
isMoralisSupported(chainId));
121122

@@ -124,7 +125,9 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
124125
const { data: walletNFTs, isPending: isWalletNFTsLoading } = useWalletNFTs({
125126
chainId,
126127
walletAddress: account?.address,
128+
isInsightSupported,
127129
});
130+
128131
const sendAndConfirmTx = useSendAndConfirmTransaction();
129132
const listingNotifications = useTxNotifications(
130133
"NFT listed Successfully",

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function ContractDirectListingsPageClient(props: {
2929
<ContractDirectListingsPage
3030
contract={props.contract}
3131
twAccount={props.twAccount}
32+
isInsightSupported={metadataQuery.data.isInsightSupported}
3233
/>
3334
);
3435
}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ import { DirectListingsTable } from "./components/table";
88
interface ContractDirectListingsPageProps {
99
contract: ThirdwebContract;
1010
twAccount: Account | undefined;
11+
isInsightSupported: boolean;
1112
}
1213

1314
export const ContractDirectListingsPage: React.FC<
1415
ContractDirectListingsPageProps
15-
> = ({ contract, twAccount }) => {
16+
> = ({ contract, twAccount, isInsightSupported }) => {
1617
return (
1718
<div className="flex flex-col gap-6">
1819
<div className="flex flex-row items-center justify-between">
1920
<p className="text-lg">Contract Listings</p>
2021
<div className="flex flex-row gap-4">
2122
<CreateListingButton
23+
isInsightSupported={isInsightSupported}
2224
contract={contract}
2325
type="direct-listings"
2426
createText="Create Direct Listing"

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ export default async function Page(props: {
2929
);
3030
}
3131

32-
const { isDirectListingSupported } = await getContractPageMetadata(
33-
info.contract,
34-
);
32+
const { isDirectListingSupported, isInsightSupported } =
33+
await getContractPageMetadata(info.contract);
3534

3635
if (!isDirectListingSupported) {
3736
redirect(`/${params.chain_id}/${params.contractAddress}`);
3837
}
3938

4039
return (
41-
<ContractDirectListingsPage contract={info.contract} twAccount={account} />
40+
<ContractDirectListingsPage
41+
contract={info.contract}
42+
twAccount={account}
43+
isInsightSupported={isInsightSupported}
44+
/>
4245
);
4346
}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function ContractEnglishAuctionsPageClient(props: {
2929
<ContractEnglishAuctionsPage
3030
contract={props.contract}
3131
twAccount={props.twAccount}
32+
isInsightSupported={metadataQuery.data.isInsightSupported}
3233
/>
3334
);
3435
}

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { EnglishAuctionsTable } from "./components/table";
88
interface ContractEnglishAuctionsProps {
99
contract: ThirdwebContract;
1010
twAccount: Account | undefined;
11+
isInsightSupported: boolean;
1112
}
1213

1314
export const ContractEnglishAuctionsPage: React.FC<
1415
ContractEnglishAuctionsProps
15-
> = ({ contract, twAccount }) => {
16+
> = ({ contract, twAccount, isInsightSupported }) => {
1617
return (
1718
<div className="flex flex-col gap-6">
1819
<div className="flex flex-row items-center justify-between">
@@ -23,6 +24,7 @@ export const ContractEnglishAuctionsPage: React.FC<
2324
type="english-auctions"
2425
createText="Create English Auction"
2526
twAccount={twAccount}
27+
isInsightSupported={isInsightSupported}
2628
/>
2729
</div>
2830
</div>

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ export default async function Page(props: {
3030
);
3131
}
3232

33-
const { isEnglishAuctionSupported } = await getContractPageMetadata(
34-
info.contract,
35-
);
33+
const { isEnglishAuctionSupported, isInsightSupported } =
34+
await getContractPageMetadata(info.contract);
3635

3736
if (!isEnglishAuctionSupported) {
3837
redirect(`/${params.chain_id}/${params.contractAddress}`);
@@ -42,6 +41,7 @@ export default async function Page(props: {
4241
<ContractEnglishAuctionsPage
4342
contract={info.contract}
4443
twAccount={twAccount}
44+
isInsightSupported={isInsightSupported}
4545
/>
4646
);
4747
}

0 commit comments

Comments
 (0)