Skip to content

Commit bccbf1a

Browse files
author
openweb3
committed
update swap
1 parent 799a873 commit bccbf1a

File tree

11 files changed

+264
-223
lines changed

11 files changed

+264
-223
lines changed

demo/app/swap/assets/guest-swap-info-metadata.json

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,27 @@
200200
}
201201
}
202202
}
203+
},
204+
{
205+
"id": 11,
206+
"type": {
207+
"def": {
208+
"sequence": {
209+
"type": 12
210+
}
211+
}
212+
}
213+
},
214+
{
215+
"id": 12,
216+
"type": {
217+
"def": {
218+
"tuple": [
219+
10,
220+
10
221+
]
222+
}
223+
}
203224
}
204225
]
205226
},
@@ -352,17 +373,7 @@
352373
{
353374
"name": "entrypoint_list_pools",
354375
"inputs": [],
355-
"output": 7
356-
},
357-
{
358-
"name": "entrypoint_asset_info",
359-
"inputs": [
360-
{
361-
"name": "asset",
362-
"ty": 0
363-
}
364-
],
365-
"output": 9
376+
"output": 11
366377
}
367378
]
368379
}

demo/app/swap/assets/guest-swap-info.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

demo/app/swap/atoms.ts

Lines changed: 108 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
import { formatUnits, parseUnits } from "viem";
21
import { apiAtom } from "@/lib/atoms";
32
import { PvqProgram } from "@open-web3/pvq";
3+
import type { Option, u128, Vec } from "@polkadot/types";
4+
import type { ITuple } from "@polkadot/types/types";
5+
import { u8aToString } from "@polkadot/util";
46
import { atom } from "jotai";
57
import { atomWithQuery } from "jotai-tanstack-query";
8+
import { atomFamily } from "jotai/utils";
9+
import { formatUnits, parseUnits } from "viem";
610
import { guestSwapInfoProgram } from "./assets/guest-swap-info";
711
import metadata from "./assets/guest-swap-info-metadata.json";
8-
import type { Bytes, Vec, Option, u128 } from "@polkadot/types";
9-
import type { Codec, ITuple } from "@polkadot/types/types";
10-
import { compactStripLength, u8aToString } from "@polkadot/util";
11-
import { atomFamily } from "jotai/utils";
12+
13+
type AssetInfo = {
14+
assetId: string;
15+
decimals: number;
16+
name: string;
17+
symbol: string;
18+
};
1219

1320
export const lpProgramAtom = atom<PvqProgram | null>((get) => {
1421
const api = get(apiAtom);
@@ -18,122 +25,81 @@ export const lpProgramAtom = atom<PvqProgram | null>((get) => {
1825

1926
export const poolListAtom = atomWithQuery((get) => ({
2027
queryKey: ["poolList"],
28+
onError: (error: Error) => {
29+
console.error("Error fetching pool list", error.message);
30+
},
31+
throwOnError: true,
32+
retry: false,
2133
queryFn: async () => {
34+
console.log("fetching pool list");
2235
const program = get(lpProgramAtom);
2336
if (!program) throw new Error("Program not initialized");
24-
const lpList = await program.executeQuery<Vec<ITuple<[Bytes, Bytes]>>>(
37+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38+
const lpList = await program.executeQuery<Vec<ITuple<[any, any]>>>(
2539
"entrypoint_list_pools",
2640
undefined,
2741
[]
2842
);
29-
return lpList.map(
30-
([assetId1, assetId2]) => [assetId1.toHex(), assetId2.toHex()] as const
31-
);
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, ""),
50+
};
51+
};
52+
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+
});
62+
63+
console.log("pools", result);
64+
return result;
3265
},
3366
enabled: !!get(lpProgramAtom),
3467
}));
3568

3669
export const assetsInfoAtom = atomWithQuery((get) => {
37-
const { data: poolList, isFetched } = get(poolListAtom);
38-
const program = get(lpProgramAtom);
39-
const assetIds = [
40-
...new Set(
41-
poolList?.flatMap(([assetId1, assetId2]) => [assetId1, assetId2])
42-
),
43-
];
70+
const { data: poolList } = get(poolListAtom);
4471

4572
return {
46-
queryKey: ["assetsInfo", assetIds],
47-
queryFn: async (): Promise<
48-
{
49-
assetId: string;
50-
decimals: number;
51-
name: string;
52-
symbol: string;
53-
}[]
54-
> => {
55-
if (!program) throw new Error("Program not initialized");
56-
const result = await Promise.all(
57-
assetIds!
58-
.filter((assetId) => assetId !== "0x040d000000")
59-
.map(async (assetId) => {
60-
// console.log("assetId1", assetId);
61-
62-
const res = await program.executeQuery<Option<Codec>>(
63-
"entrypoint_asset_info",
64-
undefined,
65-
[assetId]
66-
);
67-
68-
if (res.isEmpty) return null;
69-
70-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
71-
const asset: any = res.unwrap();
72-
const [, name] = compactStripLength(asset.name);
73-
const [, symbol] = compactStripLength(asset.symbol);
74-
return {
75-
assetId: asset.assetId.toHex() as string,
76-
decimals: asset.decimals.toNumber() as number,
77-
name: u8aToString(name),
78-
symbol: u8aToString(symbol),
79-
};
80-
})
81-
);
73+
queryKey: ["assetsInfo"],
74+
queryFn: async () => {
75+
const assetsInfo =
76+
poolList?.flatMap((pool) => [pool.asset1, pool.asset2]) ?? [];
77+
const uniqueAssetsInfo: AssetInfo[] = [];
78+
79+
for (const asset of assetsInfo) {
80+
if (!uniqueAssetsInfo.some((a) => a.assetId === asset.assetId)) {
81+
uniqueAssetsInfo.push(asset);
82+
}
83+
}
8284

83-
return result
84-
.concat({
85-
assetId: "0x040d000000",
86-
decimals: 10,
87-
name: "lcDOT",
88-
symbol: "lcDOT",
89-
})
90-
.filter(Boolean) as {
91-
assetId: string;
92-
decimals: number;
93-
name: string;
94-
symbol: string;
95-
}[];
85+
return uniqueAssetsInfo;
9686
},
97-
enabled: isFetched && !!program,
87+
enabled: !!poolList,
9888
};
9989
});
10090

10191
export const assetsInfoFamily = atomFamily((id: string) =>
10292
atom((get) => {
10393
const { data: assetsInfo } = get(assetsInfoAtom);
104-
10594
return assetsInfo?.find((asset) => asset.assetId === id);
10695
})
10796
);
10897

109-
export const poolListDetailAtom = atomWithQuery((get) => {
110-
const { data: poolList, isFetched: isPoolListFetched } = get(poolListAtom);
111-
const { data: assetsInfo, isFetched: isAssetsInfoFetched } =
112-
get(assetsInfoAtom);
113-
114-
return {
115-
queryKey: ["poolListDetail"],
116-
queryFn: async () => {
117-
console.log("poolList", poolList, isPoolListFetched, isAssetsInfoFetched);
118-
return poolList?.map(([assetId1, assetId2]) => {
119-
const asset1 = assetsInfo?.find((asset) => asset.assetId === assetId1);
120-
const asset2 = assetsInfo?.find((asset) => asset.assetId === assetId2);
121-
return {
122-
asset1: asset1!,
123-
asset2: asset2!,
124-
key: `${assetId1}-${assetId2}`,
125-
};
126-
});
127-
},
128-
enabled: isPoolListFetched && isAssetsInfoFetched,
129-
};
130-
});
131-
13298
export const selectedPoolAtom = atom<string | null>(null);
13399
export const selectedPoolInfoAtom = atom((get) => {
134100
const selectedPool = get(selectedPoolAtom);
135-
const { data: poolListDetail } = get(poolListDetailAtom);
136-
return poolListDetail?.find((pool) => pool.key === selectedPool);
101+
const { data: poolList } = get(poolListAtom);
102+
return poolList?.find((pool) => pool.key === selectedPool);
137103
});
138104

139105
export const poolSizeAtom = atomWithQuery((get) => {
@@ -151,7 +117,16 @@ export const poolSizeAtom = atomWithQuery((get) => {
151117
undefined,
152118
[selectedPool.asset1.assetId, selectedPool.asset2.assetId]
153119
);
154-
if (poolSize.isEmpty) return;
120+
if (poolSize.isEmpty)
121+
return {
122+
size: [BigInt(0), BigInt(0)],
123+
asset1: selectedPool.asset1,
124+
asset2: selectedPool.asset2,
125+
asset1Amount: "0",
126+
asset2Amount: "0",
127+
price: 0,
128+
};
129+
155130
const [asset1Amount, asset2Amount] = poolSize.unwrap();
156131
const asset1AmountFormatted = formatUnits(
157132
asset1Amount.toBigInt(),
@@ -178,74 +153,58 @@ export const poolSizeAtom = atomWithQuery((get) => {
178153
};
179154
});
180155

181-
export const getQuoteAmountAtom = atom((get) => {
156+
export const getExactAmountOutAtom = atom((get) => {
182157
const program = get(lpProgramAtom);
183158

184-
return async (baseToken: string, quoteToken: string, baseAmount: string) => {
159+
return async (sellToken: string, buyToken: string, sellAmount: string) => {
185160
if (!program) throw new Error("Program not initialized");
186161

187-
const quoteTokenDecimals = get(assetsInfoFamily(quoteToken))?.decimals;
188-
const baseTokenDecimals = get(assetsInfoFamily(baseToken))?.decimals;
189-
if (!quoteTokenDecimals || !baseTokenDecimals)
190-
throw new Error("quoteTokenDecimals or baseTokenDecimals is not set");
162+
const sellTokenDecimals = get(assetsInfoFamily(sellToken))?.decimals;
163+
const buyTokenDecimals = get(assetsInfoFamily(buyToken))?.decimals;
164+
if (sellTokenDecimals === undefined || buyTokenDecimals === undefined)
165+
throw new Error("sellTokenDecimals or buyTokenDecimals is not set");
191166

192-
const quoteAmount = await program.executeQuery<Option<u128>>(
167+
const buyAmount = await program.executeQuery<Option<u128>>(
193168
"entrypoint_quote_price_exact_tokens_for_tokens",
194169
undefined,
195-
[baseToken, quoteToken, parseUnits(baseAmount, baseTokenDecimals)]
170+
[sellToken, buyToken, parseUnits(sellAmount, sellTokenDecimals)]
196171
);
197172

198-
console.log(
199-
`Get ${quoteToken} amount:`,
200-
baseToken,
201-
quoteToken,
202-
baseAmount,
203-
quoteAmount.toHuman()
204-
);
205-
206-
if (quoteAmount.isEmpty) return "0";
173+
if (buyAmount.isEmpty) return "0";
207174
return formatUnits(
208-
quoteAmount.unwrap().toBigInt() as bigint,
209-
quoteTokenDecimals
175+
buyAmount.unwrap().toBigInt() as bigint,
176+
buyTokenDecimals
210177
);
211178
};
212179
});
213180

214-
export const getBaseAmountAtom = atom((get) => {
181+
export const getExactAmountInAtom = atom((get) => {
215182
const program = get(lpProgramAtom);
216183

217-
return async (baseToken: string, quoteToken: string, quoteAmount: string) => {
184+
return async (sellToken: string, buyToken: string, buyAmount: string) => {
218185
if (!program) throw new Error("Program not initialized");
219186

220-
const quoteTokenDecimals = get(assetsInfoFamily(quoteToken))?.decimals;
221-
const baseTokenDecimals = get(assetsInfoFamily(baseToken))?.decimals;
222-
if (!quoteTokenDecimals || !baseTokenDecimals)
223-
throw new Error("quoteTokenDecimals or baseTokenDecimals is not set");
187+
const buyTokenDecimals = get(assetsInfoFamily(buyToken))?.decimals;
188+
const sellTokenDecimals = get(assetsInfoFamily(sellToken))?.decimals;
189+
if (buyTokenDecimals === undefined || sellTokenDecimals === undefined)
190+
throw new Error("buyTokenDecimals or sellTokenDecimals is not set");
224191

225-
const baseAmount = await program.executeQuery<Option<u128>>(
192+
const sellAmount = await program.executeQuery<Option<u128>>(
226193
"entrypoint_quote_price_tokens_for_exact_tokens",
227194
undefined,
228-
[baseToken, quoteToken, parseUnits(quoteAmount, quoteTokenDecimals)]
229-
);
230-
231-
console.log(
232-
`Get ${baseToken} amount:`,
233-
baseToken,
234-
quoteToken,
235-
quoteAmount,
236-
baseAmount.toHuman()
195+
[sellToken, buyToken, parseUnits(buyAmount, buyTokenDecimals)]
237196
);
238197

239-
if (baseAmount.isEmpty) return "0";
198+
if (sellAmount.isEmpty) return "0";
240199

241200
return formatUnits(
242-
baseAmount.unwrap().toBigInt() as bigint,
243-
baseTokenDecimals
201+
sellAmount.unwrap().toBigInt() as bigint,
202+
sellTokenDecimals
244203
);
245204
};
246205
});
247206

248-
export const getReceiveAmountAtom = atom((get) => {
207+
export const calcReceiveAmountAtom = atom((get) => {
249208
return async (
250209
sellToken: string,
251210
buyToken: string,
@@ -310,3 +269,20 @@ export const getReceiveAmountAtom = atom((get) => {
310269
}
311270
};
312271
});
272+
273+
export const getReceiveAmountAtom = atom((get) => {
274+
return async (
275+
sellToken: string,
276+
buyToken: string,
277+
amount: string,
278+
isSell: boolean
279+
) => {
280+
const getExactAmountOut = get(getExactAmountOutAtom);
281+
const getExactAmountIn = get(getExactAmountInAtom);
282+
if (isSell) {
283+
return getExactAmountOut(sellToken, buyToken, amount);
284+
} else {
285+
return getExactAmountIn(sellToken, buyToken, amount);
286+
}
287+
};
288+
});

demo/app/swap/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
33

4-
const queryClient = new QueryClient();
4+
export const queryClient = new QueryClient();
55

66
export default function SwapLayout({
77
children,

0 commit comments

Comments
 (0)