Skip to content

Commit 053b1f9

Browse files
author
openweb3
committed
add network selector
1 parent 568b29a commit 053b1f9

File tree

3 files changed

+180
-42
lines changed

3 files changed

+180
-42
lines changed

demo/app/swap/atoms.ts

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,54 +23,58 @@ export const lpProgramAtom = atom<PvqProgram | null>((get) => {
2323
return new PvqProgram(api, guestSwapInfoProgram as `0x${string}`, metadata);
2424
});
2525

26-
export const poolListAtom = atomWithQuery((get) => ({
27-
queryKey: ["poolList"],
28-
onError: (error: Error) => {
29-
console.error("Error fetching pool list", error.message);
30-
},
31-
throwOnError: true,
32-
retry: false,
33-
queryFn: async () => {
34-
console.log("fetching pool list");
35-
const program = get(lpProgramAtom);
36-
if (!program) throw new Error("Program not initialized");
37-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38-
const lpList = await program.executeQuery<Vec<ITuple<[any, any]>>>(
39-
"entrypoint_list_pools",
40-
undefined,
41-
[]
42-
);
43-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
44-
const getAssetInfo = (asset: any): AssetInfo => {
45-
return {
46-
assetId: asset.assetId.toHex() as string,
47-
decimals: asset.decimals.toNumber() as number,
48-
name: u8aToString(asset.name),
49-
symbol: u8aToString(asset.symbol).replace(/[^a-zA-Z0-9]/g, ""),
26+
export const poolListAtom = atomWithQuery((get) => {
27+
const api = get(apiAtom);
28+
return {
29+
queryKey: ["poolList", api?.genesisHash.toHex()],
30+
onError: (error: Error) => {
31+
console.error("Error fetching pool list", error.message);
32+
},
33+
throwOnError: true,
34+
retry: false,
35+
queryFn: async () => {
36+
console.log("fetching pool list");
37+
const program = get(lpProgramAtom);
38+
if (!program) throw new Error("Program not initialized");
39+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
40+
const lpList = await program.executeQuery<Vec<ITuple<[any, any]>>>(
41+
"entrypoint_list_pools",
42+
undefined,
43+
[]
44+
);
45+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46+
const getAssetInfo = (asset: any): AssetInfo => {
47+
return {
48+
assetId: asset.assetId.toHex() as string,
49+
decimals: asset.decimals.toNumber() as number,
50+
name: u8aToString(asset.name),
51+
symbol: u8aToString(asset.symbol).replace(/[^a-zA-Z0-9]/g, ""),
52+
};
5053
};
51-
};
5254

53-
const result = lpList.map(([assetId1, assetId2]) => {
54-
const asset1 = getAssetInfo(assetId1);
55-
const asset2 = getAssetInfo(assetId2);
56-
return {
57-
asset1,
58-
asset2,
59-
key: `${assetId1.assetId}-${assetId2.assetId}`,
60-
};
61-
});
55+
const result = lpList.map(([assetId1, assetId2]) => {
56+
const asset1 = getAssetInfo(assetId1);
57+
const asset2 = getAssetInfo(assetId2);
58+
return {
59+
asset1,
60+
asset2,
61+
key: `${assetId1.assetId}-${assetId2.assetId}`,
62+
};
63+
});
6264

63-
console.log("pools", result);
64-
return result;
65-
},
66-
enabled: !!get(lpProgramAtom),
67-
}));
65+
console.log("pools", result);
66+
return result;
67+
},
68+
enabled: !!get(lpProgramAtom),
69+
};
70+
});
6871

6972
export const assetsInfoAtom = atomWithQuery((get) => {
7073
const { data: poolList } = get(poolListAtom);
74+
const api = get(apiAtom);
7175

7276
return {
73-
queryKey: ["assetsInfo"],
77+
queryKey: ["assetsInfo", api?.genesisHash.toHex()],
7478
queryFn: async () => {
7579
const assetsInfo =
7680
poolList?.flatMap((pool) => [pool.asset1, pool.asset2]) ?? [];

demo/app/swap/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { Connect } from "@/components/Connect";
3+
import { NetworkSelector } from "@/components/NetworkSelector";
44
import { ImageWithFallback } from "@/components/ImageWithFallback";
55
import { Button } from "@/components/ui/button";
66
import { Card, CardContent } from "@/components/ui/card";
@@ -266,7 +266,7 @@ export default function SwapPage() {
266266
<CardContent>
267267
<div className="flex">
268268
<div className="flex-1 px-4">
269-
<Connect className="mt-2" onDisconnect={handleDisconnect} />
269+
<NetworkSelector className="mt-2" onDisconnect={handleDisconnect} />
270270
<div className="mt-8 flex flex-col gap-4">
271271
{/* Token Pair Selector */}
272272
<div>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"use client";
2+
import { Button } from "@/components/ui/button";
3+
import {
4+
Select,
5+
SelectContent,
6+
SelectItem,
7+
SelectTrigger,
8+
SelectValue,
9+
} from "@/components/ui/select";
10+
import { apiAtom, apiConnectingAtom } from "@/lib/atoms";
11+
import { cn } from "@/lib/utils";
12+
import { ApiPromise, WsProvider } from "@polkadot/api";
13+
import { useAtom } from "jotai";
14+
import { useCallback, useState } from "react";
15+
16+
const NETWORKS = [
17+
{
18+
name: "Acala",
19+
endpoint: "wss://pvq-mandala.aca-staging.network",
20+
},
21+
{
22+
name: "AssetHub",
23+
endpoint: "wss://pvq-assethub.aca-staging.network",
24+
},
25+
] as const;
26+
27+
export const NetworkSelector = ({
28+
className,
29+
onDisconnect,
30+
}: {
31+
className?: string;
32+
onDisconnect?: () => void;
33+
}) => {
34+
const [api, setApi] = useAtom(apiAtom);
35+
const [connecting, setConnecting] = useAtom(apiConnectingAtom);
36+
const [selectedNetwork, setSelectedNetwork] = useState<string>("");
37+
const [error, setError] = useState<Error | null>(null);
38+
39+
const handleConnect = useCallback(async () => {
40+
if (connecting || !selectedNetwork) return;
41+
42+
const network = NETWORKS.find(n => n.name === selectedNetwork);
43+
if (!network) return;
44+
45+
try {
46+
setError(null);
47+
setConnecting(true);
48+
const wsProvider = new WsProvider(network.endpoint);
49+
50+
await new Promise((resolve, reject) => {
51+
const unsubError = wsProvider.on("error", (err) => {
52+
console.error("API error:", err);
53+
unsubError();
54+
wsProvider.disconnect();
55+
reject(new Error(`Failed to connect to ${network.name}`));
56+
});
57+
const unsubConnect = wsProvider.on("connected", () => {
58+
console.log(`Connected to ${network.name}`);
59+
unsubConnect();
60+
resolve(null);
61+
});
62+
});
63+
64+
const api = await ApiPromise.create({ provider: wsProvider });
65+
66+
await api.isReady;
67+
setApi(api);
68+
} catch (error) {
69+
console.error("Failed to connect to the API:", error);
70+
setError(error as Error);
71+
setApi(null);
72+
} finally {
73+
setConnecting(false);
74+
}
75+
}, [connecting, selectedNetwork, setApi, setConnecting]);
76+
77+
const handleDisconnect = useCallback(() => {
78+
if (api) {
79+
api.disconnect();
80+
setApi(null);
81+
setConnecting(false);
82+
setSelectedNetwork("");
83+
onDisconnect?.();
84+
}
85+
}, [api, setApi, setConnecting, onDisconnect]);
86+
87+
const handleNetworkChange = useCallback((value: string) => {
88+
setSelectedNetwork(value);
89+
setError(null);
90+
}, []);
91+
92+
return (
93+
<div className={cn(className, "select-none")}>
94+
<div className="font-bold">Select Network</div>
95+
<div className="flex space-x-2 mt-1">
96+
<Select
97+
value={selectedNetwork}
98+
onValueChange={handleNetworkChange}
99+
disabled={connecting || !!api}
100+
>
101+
<SelectTrigger className="flex-1">
102+
<SelectValue placeholder="Choose a network" />
103+
</SelectTrigger>
104+
<SelectContent>
105+
{NETWORKS.map((network) => (
106+
<SelectItem key={network.name} value={network.name}>
107+
{network.name}
108+
</SelectItem>
109+
))}
110+
</SelectContent>
111+
</Select>
112+
{!api ? (
113+
<Button
114+
className="min-w-[120px]"
115+
disabled={connecting || !selectedNetwork}
116+
onClick={handleConnect}
117+
>
118+
{connecting ? "Connecting..." : "Connect"}
119+
</Button>
120+
) : (
121+
<Button
122+
className="min-w-[120px]"
123+
variant="destructive"
124+
disabled={connecting}
125+
onClick={handleDisconnect}
126+
>
127+
Disconnect
128+
</Button>
129+
)}
130+
</div>
131+
{error && <p className="mt-2 text-sm text-red-400">{error.message}</p>}
132+
</div>
133+
);
134+
};

0 commit comments

Comments
 (0)