Skip to content

Commit 0a7beb8

Browse files
committed
[Dashboard] Dynamically fetch supported chains from Simplehash (#5866)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on refactoring the handling of SimpleHash support in the `wallet` API and related components. It introduces an asynchronous check for SimpleHash support and replaces direct checks with a hook for better data management. ### Detailed summary - Changed `isSimpleHashSupported` to return a chain slug instead of a boolean. - Updated the `generateSimpleHashUrl` function to use `chainSlug`. - Introduced `useSimplehashSupport` hook for querying SimpleHash support. - Refactored components to use the new hook instead of direct checks. - Removed unused `simpleHashSupportedChainIdsMap` and related types. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent e01193a commit 0a7beb8

File tree

6 files changed

+89
-112
lines changed

6 files changed

+89
-112
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ 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 { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash";
16+
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
1717
import { PlusIcon } from "lucide-react";
1818
import { useState } from "react";
1919
import type { ThirdwebContract } from "thirdweb";
@@ -40,9 +40,12 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
4040
const [open, setOpen] = useState(false);
4141
const [listingMode, setListingMode] =
4242
useState<(typeof LISTING_MODES)[number]>("Select NFT");
43+
44+
const simplehashQuery = useSimplehashSupport(contract.chain.id);
45+
4346
const isSupportedChain =
4447
contract.chain.id &&
45-
(isSimpleHashSupported(contract.chain.id) ||
48+
(simplehashQuery.data ||
4649
isAlchemySupported(contract.chain.id) ||
4750
isMoralisSupported(contract.chain.id));
4851
return (

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ 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 { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash";
24+
import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash";
2525
import type { WalletNFT } from "lib/wallet/nfts/types";
2626
import { CircleAlertIcon, InfoIcon } from "lucide-react";
2727
import Link from "next/link";
@@ -112,10 +112,10 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
112112
const { idToChain } = useAllChainsData();
113113
const network = idToChain.get(chainId);
114114
const [isFormLoading, setIsFormLoading] = useState(false);
115-
115+
const simplehashQuery = useSimplehashSupport(contract.chain.id);
116116
const isSupportedChain =
117117
chainId &&
118-
(isSimpleHashSupported(chainId) ||
118+
(!!simplehashQuery.data ||
119119
isAlchemySupported(chainId) ||
120120
isMoralisSupported(chainId));
121121

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash";
2+
import { NextResponse } from "next/server";
3+
import type { NextRequest } from "next/server";
4+
5+
export const runtime = "edge";
6+
7+
export const GET = async (req: NextRequest) => {
8+
const searchParams = req.nextUrl.searchParams;
9+
const chainIdStr = searchParams.get("chainId");
10+
11+
if (!chainIdStr) {
12+
return NextResponse.json(
13+
{
14+
error: "Missing chain ID parameter.",
15+
},
16+
{ status: 400 },
17+
);
18+
}
19+
20+
const chainId = Number.parseInt(chainIdStr);
21+
22+
if (!Number.isInteger(chainId)) {
23+
return NextResponse.json(
24+
{
25+
error: "Invalid chain ID parameter.",
26+
},
27+
{ status: 400 },
28+
);
29+
}
30+
31+
const found = await isSimpleHashSupported(chainId);
32+
33+
return NextResponse.json({ result: found });
34+
};

apps/dashboard/src/lib/wallet/nfts/simpleHash.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,50 @@
1-
import {
2-
type GenerateURLParams,
3-
type SimpleHashSupportedChainId,
4-
type WalletNFT,
5-
simpleHashSupportedChainIdsMap,
6-
simpleHashSupportedNetworks,
7-
} from "./types";
8-
9-
export function isSimpleHashSupported(
1+
import { useQuery } from "@tanstack/react-query";
2+
import type { WalletNFT } from "./types";
3+
4+
/**
5+
* Return the chain slug if the chain is supported by Simplehash, otherwise `undefined`
6+
* Chain slug because we need it to fetch the owned NFTs using another endpoint
7+
*/
8+
export async function isSimpleHashSupported(
109
chainId: number,
11-
): chainId is SimpleHashSupportedChainId {
12-
return simpleHashSupportedNetworks.includes(chainId.toString());
13-
}
10+
): Promise<string | undefined> {
11+
if (!process.env.SIMPLEHASH_API_KEY) {
12+
throw new Error("No Simplehash API Key");
13+
}
1414

15-
export function generateSimpleHashUrl({ chainId, owner }: GenerateURLParams) {
15+
const options = {
16+
method: "GET",
17+
headers: {
18+
"X-API-KEY": process.env.SIMPLEHASH_API_KEY,
19+
},
20+
};
21+
const response: Array<{
22+
chain: string;
23+
eip155_network_id: number;
24+
is_testnet: boolean;
25+
}> = await fetch("https://api.simplehash.com/api/v0/chains", options)
26+
.then((r) => r.json())
27+
.catch(() => []);
28+
29+
const found = response.find((chain) => chain.eip155_network_id === chainId);
30+
return found?.chain;
31+
}
32+
33+
export function useSimplehashSupport(chainId: number) {
34+
return useQuery({
35+
queryKey: ["simplehash-supported", chainId],
36+
queryFn: () => isSimpleHashSupported(chainId),
37+
enabled: !!chainId,
38+
});
39+
}
40+
41+
export function generateSimpleHashUrl({
42+
chainSlug,
43+
owner,
44+
}: { chainSlug: string; owner: string }) {
1645
const url = new URL("https://api.simplehash.com/api/v0/nfts/owners");
17-
1846
url.searchParams.append("wallet_addresses", owner);
19-
if (simpleHashSupportedChainIdsMap[chainId]) {
20-
url.searchParams.append("chains", simpleHashSupportedChainIdsMap[chainId]);
21-
}
22-
47+
url.searchParams.append("chains", chainSlug);
2348
return url.toString();
2449
}
2550

apps/dashboard/src/lib/wallet/nfts/types.ts

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,29 @@
11
import type { NFT } from "thirdweb";
22
import {
33
arbitrum,
4-
arbitrumNova,
5-
arbitrumSepolia,
6-
astriaEvmDusknet,
74
avalanche,
8-
avalancheFuji,
95
base,
10-
baseSepolia,
116
blast,
127
blastSepolia,
138
bsc,
149
bscTestnet,
15-
celo,
1610
cronos,
17-
degen,
1811
ethereum,
1912
fantom,
20-
frameTestnet,
2113
gnosis,
2214
gnosisChiadoTestnet,
23-
godWoken,
24-
godWokenTestnetV1,
25-
hokumTestnet,
2615
linea,
27-
lineaSepolia,
28-
loot,
29-
mantaPacific,
30-
mantaPacificTestnet,
3116
moonbeam,
3217
optimism,
33-
optimismSepolia,
3418
palm,
35-
palmTestnet,
3619
polygon,
3720
polygonAmoy,
3821
polygonMumbai,
3922
polygonZkEvm,
4023
polygonZkEvmTestnet,
41-
rari,
42-
rariTestnet,
43-
scroll,
44-
scrollAlphaTestnet,
45-
scrollSepoliaTestnet,
4624
sepolia,
47-
xai,
48-
xaiSepolia,
4925
zkSync,
5026
zkSyncSepolia,
51-
zora,
52-
zoraSepolia,
5327
} from "thirdweb/chains";
5428

5529
// Cannot use BigInt for the values here because it will result in error: "fail to serialize bigint"
@@ -132,65 +106,8 @@ const moralisSupportedChainIdsMap: Record<number, string> = {
132106
[4202]: "",
133107
};
134108

135-
// List: https://docs.simplehash.com/reference/supported-chains-testnets
136-
export const simpleHashSupportedChainIdsMap: Record<number, string> = {
137-
[arbitrum.id]: "arbitrum",
138-
[arbitrumNova.id]: "arbitrum-nova",
139-
[arbitrumSepolia.id]: "arbitrum-sepolia",
140-
[astriaEvmDusknet.id]: "astria-devnet",
141-
[avalanche.id]: "avalanche",
142-
[avalancheFuji.id]: "avalanche-fuji",
143-
[base.id]: "base",
144-
[baseSepolia.id]: "base-sepolia",
145-
[bsc.id]: "bsc",
146-
[bscTestnet.id]: "bsc-testnet",
147-
[blast.id]: "blast",
148-
[blastSepolia.id]: "blast-sepolia",
149-
[celo.id]: "celo",
150-
[ethereum.id]: "ethereum",
151-
[degen.id]: "degen",
152-
[fantom.id]: "fantom",
153-
[frameTestnet.id]: "frame-testnet",
154-
[gnosis.id]: "gnosis",
155-
[godWoken.id]: "godwoken",
156-
[godWokenTestnetV1.id]: "godwoken-testnet",
157-
[hokumTestnet.id]: "hokum-testnet",
158-
[linea.id]: "linea",
159-
[lineaSepolia.id]: "linea-testnet",
160-
[loot.id]: "loot",
161-
[mantaPacific.id]: "manta",
162-
[mantaPacificTestnet.id]: "manta-testnet",
163-
[moonbeam.id]: "moonbeam",
164-
[polygonMumbai.id]: "polygon-mumbai",
165-
[optimism.id]: "optimism",
166-
[optimismSepolia.id]: "optimism-sepolia",
167-
[palm.id]: "palm",
168-
[palmTestnet.id]: "palm-testnet",
169-
[polygon.id]: "polygon",
170-
[polygonAmoy.id]: "polygon-amoy",
171-
[polygonZkEvm.id]: "polygon-zkevm",
172-
[polygonZkEvmTestnet.id]: "polygon-zkevm-testnet",
173-
[rari.id]: "rari",
174-
[rariTestnet.id]: "rari-testnet",
175-
[scroll.id]: "scroll",
176-
[scrollAlphaTestnet.id]: "scroll-testnet",
177-
[scrollSepoliaTestnet.id]: "scroll-sepolia",
178-
[sepolia.id]: "ethereum-sepolia",
179-
[xai.id]: "xai",
180-
[xaiSepolia.id]: "xai-sepolia",
181-
[zkSync.id]: "zksync-era",
182-
[zora.id]: "zora",
183-
[zoraSepolia.id]: "zora-sepolia",
184-
[1329]: "sei",
185-
[1328]: "sei-atlantic-2",
186-
[360]: "shape",
187-
[33139]: "apechain",
188-
};
189-
190109
export type AlchemySupportedChainId = keyof typeof alchemySupportedChainIdsMap;
191110
export type MoralisSupportedChainId = keyof typeof moralisSupportedChainIdsMap;
192-
export type SimpleHashSupportedChainId =
193-
keyof typeof simpleHashSupportedChainIdsMap;
194111

195112
export const alchemySupportedChainIds = Object.keys(
196113
alchemySupportedChainIdsMap,
@@ -200,10 +117,6 @@ export const moralisSupportedChainIds = Object.keys(
200117
moralisSupportedChainIdsMap,
201118
);
202119

203-
export const simpleHashSupportedNetworks = Object.keys(
204-
simpleHashSupportedChainIdsMap,
205-
);
206-
207120
export interface GenerateURLParams {
208121
chainId: number;
209122
owner: string;

apps/dashboard/src/pages/api/wallet/nfts/[chainId].ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ const handler = async (
3838
}
3939
const chainId = Number.parseInt(queryChainId);
4040

41-
if (isSimpleHashSupported(chainId) && process.env.SIMPLEHASH_API_KEY) {
42-
const url = generateSimpleHashUrl({ chainId, owner });
41+
const supportedChainSlug = await isSimpleHashSupported(chainId);
42+
43+
if (supportedChainSlug && process.env.SIMPLEHASH_API_KEY) {
44+
const url = generateSimpleHashUrl({ chainSlug: supportedChainSlug, owner });
4345

4446
const options = {
4547
method: "GET",

0 commit comments

Comments
 (0)