@shogun-sdk/swap 0.0.2-test.24 → 0.0.2-test.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +379 -112
- package/dist/core.d.cts +4 -136
- package/dist/core.d.ts +4 -136
- package/dist/core.js +353 -84
- package/dist/index-Czt8I0Vg.d.ts +351 -0
- package/dist/index-D62OxLwp.d.cts +351 -0
- package/dist/index.cjs +373 -500
- package/dist/index.d.cts +4 -9
- package/dist/index.d.ts +4 -9
- package/dist/index.js +347 -474
- package/dist/react.cjs +829 -476
- package/dist/react.d.cts +192 -82
- package/dist/react.d.ts +192 -82
- package/dist/react.js +799 -454
- package/dist/{wallet-BhuMJ3K_.d.cts → wallet-B9bKceyN.d.cts} +1 -2
- package/dist/{wallet-BhuMJ3K_.d.ts → wallet-B9bKceyN.d.ts} +1 -2
- package/dist/wallet-adapter.cjs +25607 -11
- package/dist/wallet-adapter.d.cts +1 -2
- package/dist/wallet-adapter.d.ts +1 -2
- package/dist/wallet-adapter.js +25626 -6
- package/package.json +44 -14
- package/dist/execute-D2qcOzkI.d.ts +0 -145
- package/dist/execute-Xvw4wXBo.d.cts +0 -145
package/dist/react.js
CHANGED
|
@@ -1,90 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
3
4
|
|
|
4
|
-
// src/
|
|
5
|
-
import {
|
|
6
|
-
async function getTokenList(params) {
|
|
7
|
-
return intentsGetTokenList(params);
|
|
8
|
-
}
|
|
5
|
+
// src/react/SwapProvider.tsx
|
|
6
|
+
import { createContext, useContext, useMemo } from "react";
|
|
9
7
|
|
|
10
|
-
// src/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const [data, setData] = useState(null);
|
|
14
|
-
const [loading, setLoading] = useState(false);
|
|
15
|
-
const [error, setError] = useState(null);
|
|
16
|
-
const controllerRef = useRef(null);
|
|
17
|
-
const debounceRef = useRef(null);
|
|
18
|
-
const debounceMs = params.debounceMs ?? 250;
|
|
19
|
-
const cacheKey = useMemo(() => {
|
|
20
|
-
return JSON.stringify({
|
|
21
|
-
q: params.q?.trim().toLowerCase() ?? "",
|
|
22
|
-
networkId: params.networkId ?? "all",
|
|
23
|
-
page: params.page ?? 1,
|
|
24
|
-
limit: params.limit ?? 50
|
|
25
|
-
});
|
|
26
|
-
}, [params.q, params.networkId, params.page, params.limit]);
|
|
27
|
-
async function fetchTokens(signal) {
|
|
28
|
-
if (tokenCache.has(cacheKey)) {
|
|
29
|
-
setData(tokenCache.get(cacheKey));
|
|
30
|
-
setLoading(false);
|
|
31
|
-
setError(null);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
setLoading(true);
|
|
36
|
-
const result = await getTokenList({ ...params, signal });
|
|
37
|
-
tokenCache.set(cacheKey, result);
|
|
38
|
-
setData(result);
|
|
39
|
-
setError(null);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (err.name !== "AbortError") {
|
|
42
|
-
const e = err instanceof Error ? err : new Error("Unknown error while fetching tokens");
|
|
43
|
-
setError(e);
|
|
44
|
-
}
|
|
45
|
-
} finally {
|
|
46
|
-
setLoading(false);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (!params.q && !params.networkId) return;
|
|
51
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
52
|
-
if (controllerRef.current) controllerRef.current.abort();
|
|
53
|
-
const controller = new AbortController();
|
|
54
|
-
controllerRef.current = controller;
|
|
55
|
-
debounceRef.current = setTimeout(() => {
|
|
56
|
-
fetchTokens(controller.signal);
|
|
57
|
-
}, debounceMs);
|
|
58
|
-
return () => {
|
|
59
|
-
controller.abort();
|
|
60
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
61
|
-
};
|
|
62
|
-
}, [cacheKey, debounceMs]);
|
|
63
|
-
return useMemo(
|
|
64
|
-
() => ({
|
|
65
|
-
/** Current fetched data (cached when possible) */
|
|
66
|
-
data,
|
|
67
|
-
/** Whether a request is in progress */
|
|
68
|
-
loading,
|
|
69
|
-
/** Error object if a request failed */
|
|
70
|
-
error,
|
|
71
|
-
/** Manually refetch the token list */
|
|
72
|
-
refetch: () => fetchTokens(),
|
|
73
|
-
/** Clear all cached token results (shared across hook instances) */
|
|
74
|
-
clearCache: () => tokenCache.clear()
|
|
75
|
-
}),
|
|
76
|
-
[data, loading, error]
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// src/react/useExecuteOrder.ts
|
|
81
|
-
import { useState as useState2, useCallback, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
8
|
+
// src/core/getQuote.ts
|
|
9
|
+
import { QuoteProvider } from "@shogun-sdk/intents-sdk";
|
|
10
|
+
import { parseUnits } from "viem";
|
|
82
11
|
|
|
83
|
-
// src/core/
|
|
84
|
-
import {
|
|
85
|
-
import { BaseError } from "viem";
|
|
12
|
+
// src/core/execute/normalizeNative.ts
|
|
13
|
+
import { isEvmChain as isEvmChain2 } from "@shogun-sdk/intents-sdk";
|
|
86
14
|
|
|
87
15
|
// src/utils/address.ts
|
|
16
|
+
import { zeroAddress } from "viem";
|
|
88
17
|
var NATIVE_TOKEN = {
|
|
89
18
|
ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
|
90
19
|
SOL: "So11111111111111111111111111111111111111111",
|
|
@@ -95,13 +24,31 @@ var isNativeAddress = (tokenAddress) => {
|
|
|
95
24
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
|
96
25
|
return !!tokenAddress && NATIVE_ADDRESSES.includes(normalizedTokenAddress);
|
|
97
26
|
};
|
|
27
|
+
function normalizeEvmTokenAddress(address) {
|
|
28
|
+
const lower = address.toLowerCase();
|
|
29
|
+
return lower === NATIVE_TOKEN.ETH.toLowerCase() ? zeroAddress : address;
|
|
30
|
+
}
|
|
98
31
|
|
|
99
32
|
// src/utils/chain.ts
|
|
100
|
-
import { ChainID } from "@shogun-sdk/intents-sdk";
|
|
101
|
-
var SOLANA_CHAIN_ID =
|
|
102
|
-
var
|
|
33
|
+
import { ChainID as BaseChainID, isEvmChain as isEvmChainIntent } from "@shogun-sdk/intents-sdk";
|
|
34
|
+
var SOLANA_CHAIN_ID = BaseChainID.Solana;
|
|
35
|
+
var CURRENT_SUPPORTED = [
|
|
36
|
+
BaseChainID.Solana,
|
|
37
|
+
BaseChainID.BSC,
|
|
38
|
+
BaseChainID.Base
|
|
39
|
+
];
|
|
40
|
+
var ChainId = Object.entries(BaseChainID).reduce(
|
|
41
|
+
(acc, [key, value]) => {
|
|
42
|
+
if (typeof value === "number" && CURRENT_SUPPORTED.includes(value)) {
|
|
43
|
+
acc[key] = value;
|
|
44
|
+
}
|
|
45
|
+
return acc;
|
|
46
|
+
},
|
|
47
|
+
{}
|
|
48
|
+
);
|
|
49
|
+
var SupportedChainsInternal = [
|
|
103
50
|
{
|
|
104
|
-
id:
|
|
51
|
+
id: BaseChainID.Arbitrum,
|
|
105
52
|
name: "Arbitrum",
|
|
106
53
|
isEVM: true,
|
|
107
54
|
wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -110,7 +57,7 @@ var SupportedChains = [
|
|
|
110
57
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
111
58
|
},
|
|
112
59
|
{
|
|
113
|
-
id:
|
|
60
|
+
id: BaseChainID.Optimism,
|
|
114
61
|
name: "Optimism",
|
|
115
62
|
isEVM: true,
|
|
116
63
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -119,7 +66,7 @@ var SupportedChains = [
|
|
|
119
66
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
120
67
|
},
|
|
121
68
|
{
|
|
122
|
-
id:
|
|
69
|
+
id: BaseChainID.Base,
|
|
123
70
|
name: "Base",
|
|
124
71
|
isEVM: true,
|
|
125
72
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -128,7 +75,7 @@ var SupportedChains = [
|
|
|
128
75
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
129
76
|
},
|
|
130
77
|
{
|
|
131
|
-
id:
|
|
78
|
+
id: BaseChainID.Hyperliquid,
|
|
132
79
|
name: "Hyperliquid",
|
|
133
80
|
isEVM: true,
|
|
134
81
|
wrapped: "0x5555555555555555555555555555555555555555",
|
|
@@ -137,7 +84,7 @@ var SupportedChains = [
|
|
|
137
84
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
138
85
|
},
|
|
139
86
|
{
|
|
140
|
-
id:
|
|
87
|
+
id: BaseChainID.BSC,
|
|
141
88
|
name: "BSC",
|
|
142
89
|
isEVM: true,
|
|
143
90
|
wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
@@ -155,6 +102,10 @@ var SupportedChains = [
|
|
|
155
102
|
tokenAddress: NATIVE_TOKEN.SOL
|
|
156
103
|
}
|
|
157
104
|
];
|
|
105
|
+
var SupportedChains = SupportedChainsInternal.filter(
|
|
106
|
+
(c) => CURRENT_SUPPORTED.includes(c.id)
|
|
107
|
+
);
|
|
108
|
+
var isEvmChain = isEvmChainIntent;
|
|
158
109
|
|
|
159
110
|
// src/utils/viem.ts
|
|
160
111
|
function isViemWalletClient(wallet) {
|
|
@@ -182,9 +133,197 @@ function serializeBigIntsToStrings(obj) {
|
|
|
182
133
|
return obj;
|
|
183
134
|
}
|
|
184
135
|
|
|
136
|
+
// src/core/execute/normalizeNative.ts
|
|
137
|
+
function normalizeNative(chainId, address) {
|
|
138
|
+
if (isEvmChain2(chainId) && isNativeAddress(address)) {
|
|
139
|
+
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
140
|
+
if (!chain?.wrapped)
|
|
141
|
+
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
142
|
+
return chain.wrapped;
|
|
143
|
+
}
|
|
144
|
+
return address;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/core/getQuote.ts
|
|
148
|
+
async function getQuote(params) {
|
|
149
|
+
const amount = BigInt(params.amount);
|
|
150
|
+
if (!params.tokenIn?.address || !params.tokenOut?.address) {
|
|
151
|
+
throw new Error("Both tokenIn and tokenOut must include an address.");
|
|
152
|
+
}
|
|
153
|
+
if (!params.sourceChainId || !params.destChainId) {
|
|
154
|
+
throw new Error("Both sourceChainId and destChainId are required.");
|
|
155
|
+
}
|
|
156
|
+
if (amount <= 0n) {
|
|
157
|
+
throw new Error("Amount must be greater than 0.");
|
|
158
|
+
}
|
|
159
|
+
const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
|
|
160
|
+
const data = await QuoteProvider.getQuote({
|
|
161
|
+
sourceChainId: params.sourceChainId,
|
|
162
|
+
destChainId: params.destChainId,
|
|
163
|
+
tokenIn: normalizedTokenIn,
|
|
164
|
+
tokenOut: params.tokenOut.address,
|
|
165
|
+
amount
|
|
166
|
+
});
|
|
167
|
+
const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
|
|
168
|
+
let warning;
|
|
169
|
+
if (slippagePercent > 10) {
|
|
170
|
+
warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
|
|
171
|
+
}
|
|
172
|
+
const estimatedAmountOut = BigInt(data.estimatedAmountOut);
|
|
173
|
+
const slippageBps = BigInt(Math.round(slippagePercent * 100));
|
|
174
|
+
const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
|
|
175
|
+
const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
|
|
176
|
+
const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
|
|
177
|
+
const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
|
|
178
|
+
const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
|
|
179
|
+
const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
|
|
180
|
+
return {
|
|
181
|
+
amountOut: estimatedAmountOutAfterSlippage,
|
|
182
|
+
amountOutUsd: amountOutUsdAfterSlippage,
|
|
183
|
+
amountInUsd: data.amountInUsd,
|
|
184
|
+
// Input USD stays the same
|
|
185
|
+
minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
|
|
186
|
+
tokenIn: {
|
|
187
|
+
address: params.tokenIn.address,
|
|
188
|
+
decimals: params.tokenIn.decimals ?? 18,
|
|
189
|
+
chainId: params.sourceChainId
|
|
190
|
+
},
|
|
191
|
+
tokenOut: {
|
|
192
|
+
address: params.tokenOut.address,
|
|
193
|
+
decimals: params.tokenOut.decimals ?? 18,
|
|
194
|
+
chainId: params.destChainId
|
|
195
|
+
},
|
|
196
|
+
amountIn: BigInt(params.amount),
|
|
197
|
+
pricePerInputToken,
|
|
198
|
+
slippage: slippagePercent,
|
|
199
|
+
internal: {
|
|
200
|
+
...data,
|
|
201
|
+
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
|
|
202
|
+
estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
|
|
203
|
+
},
|
|
204
|
+
warning
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function buildQuoteParams({
|
|
208
|
+
tokenIn,
|
|
209
|
+
tokenOut,
|
|
210
|
+
sourceChainId,
|
|
211
|
+
destChainId,
|
|
212
|
+
amount,
|
|
213
|
+
slippage
|
|
214
|
+
}) {
|
|
215
|
+
return {
|
|
216
|
+
tokenIn,
|
|
217
|
+
tokenOut,
|
|
218
|
+
sourceChainId,
|
|
219
|
+
destChainId,
|
|
220
|
+
amount: parseUnits(amount.toString(), tokenIn.decimals ?? 18).toString(),
|
|
221
|
+
slippage
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/core/getBalances.ts
|
|
226
|
+
import { TOKEN_SEARCH_API_BASE_URL } from "@shogun-sdk/intents-sdk";
|
|
227
|
+
async function getBalances(params, options) {
|
|
228
|
+
const { addresses, cursorEvm, cursorSvm } = params;
|
|
229
|
+
const { signal } = options ?? {};
|
|
230
|
+
if (!addresses?.evm && !addresses?.svm) {
|
|
231
|
+
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
232
|
+
}
|
|
233
|
+
const payload = JSON.stringify({
|
|
234
|
+
addresses,
|
|
235
|
+
cursorEvm,
|
|
236
|
+
cursorSvm
|
|
237
|
+
});
|
|
238
|
+
const start = performance.now();
|
|
239
|
+
const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: {
|
|
242
|
+
accept: "application/json",
|
|
243
|
+
"Content-Type": "application/json"
|
|
244
|
+
},
|
|
245
|
+
body: payload,
|
|
246
|
+
signal
|
|
247
|
+
}).catch((err) => {
|
|
248
|
+
if (err.name === "AbortError") {
|
|
249
|
+
throw new Error("Balance request was cancelled.");
|
|
250
|
+
}
|
|
251
|
+
throw err;
|
|
252
|
+
});
|
|
253
|
+
if (!response.ok) {
|
|
254
|
+
const text = await response.text().catch(() => "");
|
|
255
|
+
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
256
|
+
}
|
|
257
|
+
const data = await response.json().catch(() => {
|
|
258
|
+
throw new Error("Invalid JSON response from balances API.");
|
|
259
|
+
});
|
|
260
|
+
const duration = (performance.now() - start).toFixed(1);
|
|
261
|
+
if (process.env.NODE_ENV !== "production") {
|
|
262
|
+
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
263
|
+
}
|
|
264
|
+
const evmItems = data.evm?.items ?? [];
|
|
265
|
+
const svmItems = data.svm?.items ?? [];
|
|
266
|
+
const combined = [...evmItems, ...svmItems];
|
|
267
|
+
const filtered = combined.filter(
|
|
268
|
+
(b) => CURRENT_SUPPORTED.includes(b.chainId)
|
|
269
|
+
);
|
|
270
|
+
return {
|
|
271
|
+
results: filtered,
|
|
272
|
+
nextCursorEvm: data.evm?.cursor ?? null,
|
|
273
|
+
nextCursorSvm: data.svm?.cursor ?? null
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/core/token-list.ts
|
|
278
|
+
import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL2 } from "@shogun-sdk/intents-sdk";
|
|
279
|
+
async function getTokenList(params) {
|
|
280
|
+
const url = new URL(`${TOKEN_SEARCH_API_BASE_URL2}/tokens/search`);
|
|
281
|
+
if (params.q) url.searchParams.append("q", params.q);
|
|
282
|
+
if (params.networkId) url.searchParams.append("networkId", String(params.networkId));
|
|
283
|
+
if (params.page) url.searchParams.append("page", String(params.page));
|
|
284
|
+
if (params.limit) url.searchParams.append("limit", String(params.limit));
|
|
285
|
+
const res = await fetch(url.toString(), {
|
|
286
|
+
signal: params.signal
|
|
287
|
+
});
|
|
288
|
+
if (!res.ok) {
|
|
289
|
+
throw new Error(`Failed to fetch tokens: ${res.status} ${res.statusText}`);
|
|
290
|
+
}
|
|
291
|
+
const data = await res.json();
|
|
292
|
+
const filteredResults = data.results.filter(
|
|
293
|
+
(token) => CURRENT_SUPPORTED.includes(token.chainId)
|
|
294
|
+
);
|
|
295
|
+
return {
|
|
296
|
+
...data,
|
|
297
|
+
results: filteredResults,
|
|
298
|
+
count: filteredResults.length
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/core/token.ts
|
|
303
|
+
import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL3 } from "@shogun-sdk/intents-sdk";
|
|
304
|
+
async function getTokensData(addresses) {
|
|
305
|
+
if (!addresses?.length) return [];
|
|
306
|
+
const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL3}/tokens/tokens`, {
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers: {
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
accept: "*/*"
|
|
311
|
+
},
|
|
312
|
+
body: JSON.stringify({ addresses })
|
|
313
|
+
});
|
|
314
|
+
if (!response.ok) {
|
|
315
|
+
throw new Error(`Failed to fetch token data: ${response.statusText}`);
|
|
316
|
+
}
|
|
317
|
+
const data = await response.json();
|
|
318
|
+
const filtered = data.filter((t) => CURRENT_SUPPORTED.includes(Number(t.chainId)));
|
|
319
|
+
return filtered;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/core/execute/execute.ts
|
|
323
|
+
import { ChainID as ChainID2, isEvmChain as isEvmChain3 } from "@shogun-sdk/intents-sdk";
|
|
324
|
+
import "viem";
|
|
325
|
+
|
|
185
326
|
// src/wallet-adapter/evm-wallet-adapter/adapter.ts
|
|
186
|
-
import { utils as ethersUtils } from "ethers/lib/ethers.js";
|
|
187
|
-
import { hexValue } from "ethers/lib/utils.js";
|
|
188
327
|
import {
|
|
189
328
|
custom,
|
|
190
329
|
publicActions
|
|
@@ -274,26 +413,14 @@ var adaptViemWallet = (wallet) => {
|
|
|
274
413
|
};
|
|
275
414
|
};
|
|
276
415
|
|
|
277
|
-
// src/core/
|
|
416
|
+
// src/core/execute/handleEvmExecution.ts
|
|
278
417
|
import {
|
|
279
418
|
getEVMSingleChainOrderTypedData,
|
|
280
419
|
getEVMCrossChainOrderTypedData
|
|
281
420
|
} from "@shogun-sdk/intents-sdk";
|
|
282
421
|
import { encodeFunctionData as encodeFunctionData2 } from "viem";
|
|
283
422
|
|
|
284
|
-
// src/core/
|
|
285
|
-
import { isEvmChain } from "@shogun-sdk/intents-sdk";
|
|
286
|
-
function normalizeNative(chainId, address) {
|
|
287
|
-
if (isEvmChain(chainId) && isNativeAddress(address)) {
|
|
288
|
-
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
289
|
-
if (!chain?.wrapped)
|
|
290
|
-
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
291
|
-
return chain.wrapped;
|
|
292
|
-
}
|
|
293
|
-
return address;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// src/core/executeOrder/stageMessages.ts
|
|
423
|
+
// src/core/execute/stageMessages.ts
|
|
297
424
|
var DEFAULT_STAGE_MESSAGES = {
|
|
298
425
|
processing: "Preparing transaction for execution",
|
|
299
426
|
approving: "Approving token allowance",
|
|
@@ -302,20 +429,44 @@ var DEFAULT_STAGE_MESSAGES = {
|
|
|
302
429
|
submitting: "Submitting transaction",
|
|
303
430
|
initiated: "Transaction initiated.",
|
|
304
431
|
success: "Transaction Executed successfully",
|
|
432
|
+
success_limit: "Limit order has been submitted successfully.",
|
|
305
433
|
shogun_processing: "Shogun is processing your transaction",
|
|
306
434
|
error: "Transaction failed during submission"
|
|
307
435
|
};
|
|
308
436
|
|
|
309
|
-
// src/core/
|
|
437
|
+
// src/core/execute/buildOrder.ts
|
|
310
438
|
import { CrossChainOrder, SingleChainOrder } from "@shogun-sdk/intents-sdk";
|
|
439
|
+
import { formatUnits, parseUnits as parseUnits2 } from "viem";
|
|
440
|
+
|
|
441
|
+
// src/utils/order.ts
|
|
442
|
+
var OrderExecutionType = /* @__PURE__ */ ((OrderExecutionType2) => {
|
|
443
|
+
OrderExecutionType2["LIMIT"] = "limit";
|
|
444
|
+
OrderExecutionType2["MARKET"] = "market";
|
|
445
|
+
return OrderExecutionType2;
|
|
446
|
+
})(OrderExecutionType || {});
|
|
447
|
+
|
|
448
|
+
// src/core/execute/buildOrder.ts
|
|
311
449
|
async function buildOrder({
|
|
312
450
|
quote,
|
|
313
451
|
accountAddress,
|
|
314
452
|
destination,
|
|
315
453
|
deadline,
|
|
316
|
-
isSingleChain
|
|
454
|
+
isSingleChain,
|
|
455
|
+
orderType,
|
|
456
|
+
options
|
|
317
457
|
}) {
|
|
318
458
|
const { tokenIn, tokenOut } = quote;
|
|
459
|
+
let amountOutMin = BigInt(quote.internal.estimatedAmountOutReduced);
|
|
460
|
+
if (orderType === "limit" /* LIMIT */ && options && "executionPrice" in options) {
|
|
461
|
+
const executionPrice = Number(options.executionPrice);
|
|
462
|
+
if (Number.isFinite(executionPrice) && executionPrice > 0) {
|
|
463
|
+
const decimalsIn = tokenIn.decimals ?? 18;
|
|
464
|
+
const decimalsOut = tokenOut.decimals ?? 18;
|
|
465
|
+
const formattedAmountIn = Number(formatUnits(BigInt(quote.amountIn.toString()), decimalsIn));
|
|
466
|
+
const rawAmountOut = formattedAmountIn * executionPrice;
|
|
467
|
+
amountOutMin = parseUnits2(rawAmountOut.toString(), decimalsOut);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
319
470
|
if (isSingleChain) {
|
|
320
471
|
return await SingleChainOrder.create({
|
|
321
472
|
user: accountAddress,
|
|
@@ -323,7 +474,7 @@ async function buildOrder({
|
|
|
323
474
|
tokenIn: tokenIn.address,
|
|
324
475
|
tokenOut: tokenOut.address,
|
|
325
476
|
amountIn: quote.amountIn,
|
|
326
|
-
amountOutMin
|
|
477
|
+
amountOutMin,
|
|
327
478
|
deadline,
|
|
328
479
|
destinationAddress: destination
|
|
329
480
|
});
|
|
@@ -337,7 +488,7 @@ async function buildOrder({
|
|
|
337
488
|
destinationTokenAddress: tokenOut.address,
|
|
338
489
|
destinationAddress: destination,
|
|
339
490
|
deadline,
|
|
340
|
-
destinationTokenMinAmount:
|
|
491
|
+
destinationTokenMinAmount: amountOutMin,
|
|
341
492
|
minStablecoinAmount: quote.minStablecoinsAmount
|
|
342
493
|
});
|
|
343
494
|
}
|
|
@@ -408,7 +559,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
|
|
|
408
559
|
});
|
|
409
560
|
}
|
|
410
561
|
|
|
411
|
-
// src/core/
|
|
562
|
+
// src/core/execute/handleOrderPollingResult.ts
|
|
412
563
|
async function handleOrderPollingResult({
|
|
413
564
|
status,
|
|
414
565
|
orderId,
|
|
@@ -446,7 +597,7 @@ async function handleOrderPollingResult({
|
|
|
446
597
|
};
|
|
447
598
|
}
|
|
448
599
|
|
|
449
|
-
// src/core/
|
|
600
|
+
// src/core/execute/ensurePermit2Allowance.ts
|
|
450
601
|
import { encodeFunctionData, erc20Abi, maxUint256 } from "viem";
|
|
451
602
|
import { PERMIT2_ADDRESS } from "@shogun-sdk/intents-sdk";
|
|
452
603
|
async function ensurePermit2Allowance({
|
|
@@ -491,7 +642,7 @@ async function ensurePermit2Allowance({
|
|
|
491
642
|
);
|
|
492
643
|
}
|
|
493
644
|
|
|
494
|
-
// src/core/
|
|
645
|
+
// src/core/execute/handleEvmExecution.ts
|
|
495
646
|
async function handleEvmExecution({
|
|
496
647
|
recipientAddress,
|
|
497
648
|
quote,
|
|
@@ -499,15 +650,19 @@ async function handleEvmExecution({
|
|
|
499
650
|
accountAddress,
|
|
500
651
|
wallet,
|
|
501
652
|
isSingleChain,
|
|
502
|
-
|
|
503
|
-
|
|
653
|
+
update,
|
|
654
|
+
orderType,
|
|
655
|
+
options
|
|
504
656
|
}) {
|
|
505
|
-
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
657
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
658
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
506
659
|
await wallet.switchChain(chainId);
|
|
507
660
|
const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
|
|
661
|
+
quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
|
|
508
662
|
const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
|
|
509
663
|
update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
|
|
510
664
|
if (shouldWrapNative) {
|
|
665
|
+
quote.tokenIn.address === tokenIn;
|
|
511
666
|
await wallet.sendTransaction({
|
|
512
667
|
to: tokenIn,
|
|
513
668
|
data: encodeFunctionData2({
|
|
@@ -534,7 +689,9 @@ async function handleEvmExecution({
|
|
|
534
689
|
accountAddress,
|
|
535
690
|
destination,
|
|
536
691
|
deadline,
|
|
537
|
-
isSingleChain
|
|
692
|
+
isSingleChain,
|
|
693
|
+
orderType,
|
|
694
|
+
options
|
|
538
695
|
});
|
|
539
696
|
console.debug(`order`, order);
|
|
540
697
|
update("processing", messageFor("signing"));
|
|
@@ -558,17 +715,28 @@ async function handleEvmExecution({
|
|
|
558
715
|
update("initiated", messageFor("initiated"));
|
|
559
716
|
const { intentId: orderId } = res.data;
|
|
560
717
|
update("initiated", messageFor("shogun_processing"));
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
718
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
719
|
+
update("success", messageFor("success_limit"));
|
|
720
|
+
return {
|
|
721
|
+
status: true,
|
|
722
|
+
orderId,
|
|
723
|
+
chainId,
|
|
724
|
+
finalStatus: "OrderPlaced",
|
|
725
|
+
stage: "success"
|
|
726
|
+
};
|
|
727
|
+
} else {
|
|
728
|
+
const status = await pollOrderStatus(accountAddress, orderId);
|
|
729
|
+
return await handleOrderPollingResult({
|
|
730
|
+
status,
|
|
731
|
+
orderId,
|
|
732
|
+
chainId,
|
|
733
|
+
update,
|
|
734
|
+
messageFor
|
|
735
|
+
});
|
|
736
|
+
}
|
|
569
737
|
}
|
|
570
738
|
|
|
571
|
-
// src/core/
|
|
739
|
+
// src/core/execute/handleSolanaExecution.ts
|
|
572
740
|
import {
|
|
573
741
|
getSolanaSingleChainOrderInstructions,
|
|
574
742
|
getSolanaCrossChainOrderInstructions
|
|
@@ -581,12 +749,14 @@ async function handleSolanaExecution({
|
|
|
581
749
|
isSingleChain,
|
|
582
750
|
update,
|
|
583
751
|
accountAddress,
|
|
584
|
-
|
|
752
|
+
orderType,
|
|
753
|
+
options
|
|
585
754
|
}) {
|
|
586
755
|
if (!wallet.rpcUrl) {
|
|
587
756
|
throw new Error("Solana wallet is missing rpcUrl");
|
|
588
757
|
}
|
|
589
|
-
const
|
|
758
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
759
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
590
760
|
update("processing", messageFor("processing"));
|
|
591
761
|
const destination = recipientAddress ?? accountAddress;
|
|
592
762
|
const order = await buildOrder({
|
|
@@ -594,7 +764,9 @@ async function handleSolanaExecution({
|
|
|
594
764
|
accountAddress,
|
|
595
765
|
destination,
|
|
596
766
|
deadline,
|
|
597
|
-
isSingleChain
|
|
767
|
+
isSingleChain,
|
|
768
|
+
orderType,
|
|
769
|
+
options
|
|
598
770
|
});
|
|
599
771
|
const txData = await getSolanaOrderInstructions({
|
|
600
772
|
order,
|
|
@@ -616,14 +788,25 @@ async function handleSolanaExecution({
|
|
|
616
788
|
update("initiated", messageFor("initiated"));
|
|
617
789
|
const { jwt, intentId: orderId } = response.data;
|
|
618
790
|
update("initiated", messageFor("shogun_processing"));
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
791
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
792
|
+
update("success", messageFor("success_limit"));
|
|
793
|
+
return {
|
|
794
|
+
status: true,
|
|
795
|
+
orderId,
|
|
796
|
+
chainId: SOLANA_CHAIN_ID,
|
|
797
|
+
finalStatus: "OrderPlaced",
|
|
798
|
+
stage: "success"
|
|
799
|
+
};
|
|
800
|
+
} else {
|
|
801
|
+
const status = await pollOrderStatus(jwt, orderId);
|
|
802
|
+
return await handleOrderPollingResult({
|
|
803
|
+
status,
|
|
804
|
+
orderId,
|
|
805
|
+
chainId: SOLANA_CHAIN_ID,
|
|
806
|
+
update,
|
|
807
|
+
messageFor
|
|
808
|
+
});
|
|
809
|
+
}
|
|
627
810
|
}
|
|
628
811
|
async function getSolanaOrderInstructions({
|
|
629
812
|
order,
|
|
@@ -656,13 +839,14 @@ async function submitToAuctioneer({
|
|
|
656
839
|
});
|
|
657
840
|
}
|
|
658
841
|
|
|
659
|
-
// src/core/
|
|
842
|
+
// src/core/execute/execute.ts
|
|
660
843
|
async function executeOrder({
|
|
661
844
|
quote,
|
|
662
845
|
accountAddress,
|
|
663
846
|
recipientAddress,
|
|
664
847
|
wallet,
|
|
665
848
|
onStatus,
|
|
849
|
+
orderType = "market" /* MARKET */,
|
|
666
850
|
options = {}
|
|
667
851
|
}) {
|
|
668
852
|
const isDev = process.env.NODE_ENV !== "production";
|
|
@@ -675,21 +859,31 @@ async function executeOrder({
|
|
|
675
859
|
onStatus?.(stage, message ?? messageFor(stage));
|
|
676
860
|
};
|
|
677
861
|
try {
|
|
678
|
-
const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
679
862
|
log("Starting execution:", {
|
|
680
863
|
accountAddress,
|
|
681
864
|
recipientAddress,
|
|
682
|
-
deadline,
|
|
683
865
|
tokenIn: quote?.tokenIn,
|
|
684
866
|
tokenOut: quote?.tokenOut
|
|
685
867
|
});
|
|
686
868
|
const adapter = normalizeWallet(wallet);
|
|
687
869
|
if (!adapter) throw new Error("No wallet provided");
|
|
688
870
|
const { tokenIn, tokenOut } = quote;
|
|
871
|
+
const srcChain = Number(tokenIn.chainId);
|
|
872
|
+
const destChain = Number(tokenOut.chainId);
|
|
873
|
+
if (!CURRENT_SUPPORTED.includes(srcChain) || !CURRENT_SUPPORTED.includes(destChain)) {
|
|
874
|
+
const unsupportedChains = [
|
|
875
|
+
!CURRENT_SUPPORTED.includes(srcChain) ? srcChain : null,
|
|
876
|
+
!CURRENT_SUPPORTED.includes(destChain) ? destChain : null
|
|
877
|
+
].filter(Boolean).join(", ");
|
|
878
|
+
const errorMsg = `Unsupported chain(s): ${unsupportedChains}`;
|
|
879
|
+
update("error", errorMsg);
|
|
880
|
+
log("Error:", errorMsg);
|
|
881
|
+
throw new Error(errorMsg);
|
|
882
|
+
}
|
|
689
883
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
690
884
|
const chainId = Number(tokenIn.chainId);
|
|
691
885
|
update("processing");
|
|
692
|
-
if (
|
|
886
|
+
if (isEvmChain3(chainId)) {
|
|
693
887
|
log("Detected EVM chain:", chainId);
|
|
694
888
|
const result = await handleEvmExecution({
|
|
695
889
|
recipientAddress,
|
|
@@ -698,13 +892,14 @@ async function executeOrder({
|
|
|
698
892
|
accountAddress,
|
|
699
893
|
wallet: adapter,
|
|
700
894
|
isSingleChain,
|
|
701
|
-
|
|
702
|
-
|
|
895
|
+
update,
|
|
896
|
+
orderType,
|
|
897
|
+
options
|
|
703
898
|
});
|
|
704
899
|
log("EVM execution result:", result);
|
|
705
900
|
return result;
|
|
706
901
|
}
|
|
707
|
-
if (chainId ===
|
|
902
|
+
if (chainId === ChainID2.Solana) {
|
|
708
903
|
log("Detected Solana chain");
|
|
709
904
|
const result = await handleSolanaExecution({
|
|
710
905
|
recipientAddress,
|
|
@@ -712,8 +907,9 @@ async function executeOrder({
|
|
|
712
907
|
accountAddress,
|
|
713
908
|
wallet: adapter,
|
|
714
909
|
isSingleChain,
|
|
715
|
-
|
|
716
|
-
|
|
910
|
+
update,
|
|
911
|
+
orderType,
|
|
912
|
+
options
|
|
717
913
|
});
|
|
718
914
|
log("Solana execution result:", result);
|
|
719
915
|
return result;
|
|
@@ -723,8 +919,13 @@ async function executeOrder({
|
|
|
723
919
|
log("Error:", unsupported);
|
|
724
920
|
return { status: false, message: unsupported, stage: "error" };
|
|
725
921
|
} catch (error) {
|
|
726
|
-
|
|
727
|
-
|
|
922
|
+
let message = "An unknown error occurred";
|
|
923
|
+
if (error && typeof error === "object") {
|
|
924
|
+
const err = error;
|
|
925
|
+
message = err.details ?? err.message ?? message;
|
|
926
|
+
} else if (typeof error === "string") {
|
|
927
|
+
message = error;
|
|
928
|
+
}
|
|
728
929
|
update("error", message);
|
|
729
930
|
return { status: false, message, stage: "error" };
|
|
730
931
|
}
|
|
@@ -735,353 +936,497 @@ function normalizeWallet(wallet) {
|
|
|
735
936
|
return wallet;
|
|
736
937
|
}
|
|
737
938
|
|
|
738
|
-
// src/
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
939
|
+
// src/core/client.ts
|
|
940
|
+
var SwapSDK = class {
|
|
941
|
+
constructor(config) {
|
|
942
|
+
__publicField(this, "apiKey");
|
|
943
|
+
/**
|
|
944
|
+
* Fetches metadata for one or more tokens from the Shogun Token Search API.
|
|
945
|
+
*
|
|
946
|
+
* ---
|
|
947
|
+
* ### Overview
|
|
948
|
+
* `getTokensData` retrieves normalized token information — such as symbol, name,
|
|
949
|
+
* decimals, logo URI, and verified status — for a given list of token addresses.
|
|
950
|
+
*
|
|
951
|
+
* It supports both **EVM** and **SVM (Solana)** tokens, returning metadata from
|
|
952
|
+
* Shogun’s unified token registry.
|
|
953
|
+
*
|
|
954
|
+
* ---
|
|
955
|
+
* @example
|
|
956
|
+
* ```ts
|
|
957
|
+
* const tokens = await getTokensData([
|
|
958
|
+
* "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
959
|
+
* "So11111111111111111111111111111111111111112", // SOL
|
|
960
|
+
* ]);
|
|
961
|
+
*
|
|
962
|
+
* console.log(tokens);
|
|
963
|
+
* [
|
|
964
|
+
* { symbol: "USDC", name: "USD Coin", chainId: 1, decimals: 6, ... },
|
|
965
|
+
* { symbol: "SOL", name: "Solana", chainId: 101, decimals: 9, ... }
|
|
966
|
+
* ]
|
|
967
|
+
* ```
|
|
968
|
+
*
|
|
969
|
+
* @param addresses - An array of token addresses (EVM or SVM) to fetch metadata for.
|
|
970
|
+
* @returns A promise resolving to an array of {@link TokenInfo} objects.
|
|
971
|
+
*
|
|
972
|
+
* @throws Will throw an error if the network request fails or the API responds with a non-OK status.
|
|
973
|
+
*/
|
|
974
|
+
__publicField(this, "getTokensData", getTokensData.bind(this));
|
|
975
|
+
if (!config.apiKey) {
|
|
976
|
+
throw new Error("SwapSDK: Missing API key");
|
|
977
|
+
}
|
|
978
|
+
this.apiKey = config.apiKey;
|
|
979
|
+
if (this.apiKey) void this.apiKey;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Retrieves a swap quote for the given input and output tokens.
|
|
983
|
+
*
|
|
984
|
+
* @param params - Quote parameters including source/destination tokens and amount.
|
|
985
|
+
* @returns A normalized `SwapQuoteResponse` containing output amount, route, and metadata.
|
|
986
|
+
*/
|
|
987
|
+
async getQuote(params) {
|
|
988
|
+
return getQuote(params);
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Fetches token balances for the specified user wallet(s).
|
|
992
|
+
*
|
|
993
|
+
* Supports both EVM and SVM (Solana) wallet addresses.
|
|
994
|
+
*
|
|
995
|
+
* @param params - Wallet address and optional chain filters.
|
|
996
|
+
* @param options - Optional abort signal for cancellation.
|
|
997
|
+
* @returns A unified balance response with per-chain token details.
|
|
998
|
+
*/
|
|
999
|
+
async getBalances(params, options) {
|
|
1000
|
+
return getBalances(params, options);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Retrieves a list of verified tokens based on search query or chain filter.
|
|
1004
|
+
*
|
|
1005
|
+
* @param params - Search parameters (query, chain ID, pagination options).
|
|
1006
|
+
* @returns Paginated `TokenSearchResponse` containing token metadata.
|
|
1007
|
+
*/
|
|
1008
|
+
async getTokenList(params) {
|
|
1009
|
+
return getTokenList(params);
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Executes a prepared swap quote using the provided wallet and configuration.
|
|
1013
|
+
*
|
|
1014
|
+
* Handles:
|
|
1015
|
+
* - Token approval (if required)
|
|
1016
|
+
* - Transaction signing and broadcasting
|
|
1017
|
+
* - Confirmation polling and stage-based status updates
|
|
1018
|
+
*
|
|
1019
|
+
* Supports both:
|
|
1020
|
+
* - Market orders (with optional deadline)
|
|
1021
|
+
* - Limit orders (requires executionPrice and deadline)
|
|
1022
|
+
*
|
|
1023
|
+
* @param quote - The swap quote to execute, containing route and metadata.
|
|
1024
|
+
* @param accountAddress - The user's wallet address executing the swap.
|
|
1025
|
+
* @param recipientAddress - Optional recipient address for the output tokens (defaults to sender).
|
|
1026
|
+
* @param wallet - Adapted wallet instance (EVM/Solana) or a standard Viem `WalletClient`.
|
|
1027
|
+
* @param onStatus - Optional callback for receiving execution stage updates and messages.
|
|
1028
|
+
* @param orderType - Defines whether this is a market or limit order.
|
|
1029
|
+
* @param options - Execution parameters (different per order type).
|
|
1030
|
+
*
|
|
1031
|
+
* @returns A finalized execution result containing transaction hash, status, and any returned data.
|
|
1032
|
+
*
|
|
1033
|
+
* @example
|
|
1034
|
+
* ```ts
|
|
1035
|
+
* * Market order
|
|
1036
|
+
* const result = await sdk.executeTransaction({
|
|
1037
|
+
* quote,
|
|
1038
|
+
* accountAddress: "0x123...",
|
|
1039
|
+
* wallet,
|
|
1040
|
+
* orderType: OrderExecutionType.MARKET,
|
|
1041
|
+
* options: { deadline: 1800 },
|
|
1042
|
+
* onStatus: (stage, msg) => console.log(stage, msg),
|
|
1043
|
+
* });
|
|
1044
|
+
*
|
|
1045
|
+
* * Limit order
|
|
1046
|
+
* const result = await sdk.executeTransaction({
|
|
1047
|
+
* quote,
|
|
1048
|
+
* accountAddress: "0x123...",
|
|
1049
|
+
* wallet,
|
|
1050
|
+
* orderType: OrderExecutionType.LIMIT,
|
|
1051
|
+
* options: { executionPrice: "0.0021", deadline: 3600 },
|
|
1052
|
+
* });
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
async executeTransaction({
|
|
1056
|
+
quote,
|
|
1057
|
+
accountAddress,
|
|
1058
|
+
recipientAddress,
|
|
1059
|
+
wallet,
|
|
1060
|
+
onStatus,
|
|
1061
|
+
orderType,
|
|
1062
|
+
options
|
|
1063
|
+
}) {
|
|
1064
|
+
return executeOrder({
|
|
1065
|
+
quote,
|
|
1066
|
+
wallet,
|
|
1067
|
+
accountAddress,
|
|
1068
|
+
recipientAddress,
|
|
1069
|
+
onStatus,
|
|
1070
|
+
orderType,
|
|
1071
|
+
options
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
// src/react/SwapProvider.tsx
|
|
1077
|
+
import { jsx } from "react/jsx-runtime";
|
|
1078
|
+
var SwapContext = createContext(null);
|
|
1079
|
+
var SwapProvider = ({ config, children }) => {
|
|
1080
|
+
const sdk = useMemo(() => new SwapSDK(config), [config.apiKey]);
|
|
1081
|
+
return /* @__PURE__ */ jsx(SwapContext.Provider, { value: sdk, children });
|
|
1082
|
+
};
|
|
1083
|
+
var useSwap = () => {
|
|
1084
|
+
const ctx = useContext(SwapContext);
|
|
1085
|
+
if (!ctx) {
|
|
1086
|
+
throw new Error("useSwap must be used within <SwapProvider>");
|
|
1087
|
+
}
|
|
1088
|
+
return ctx;
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
// src/react/useQuote.ts
|
|
1092
|
+
import { useCallback, useEffect, useMemo as useMemo2, useRef } from "react";
|
|
1093
|
+
import useSWR, { mutate } from "swr";
|
|
1094
|
+
var QUOTE_KEY = "quote-global";
|
|
1095
|
+
function isValidQuoteParams(p) {
|
|
1096
|
+
if (!p) return false;
|
|
1097
|
+
const { tokenIn, tokenOut, amount } = p;
|
|
1098
|
+
if (!tokenIn || !tokenOut || !amount) return false;
|
|
1099
|
+
const n = Number(amount);
|
|
1100
|
+
return Number.isFinite(n) && n > 0;
|
|
1101
|
+
}
|
|
1102
|
+
function useQuote(initialParams) {
|
|
1103
|
+
const sdk = useSwap();
|
|
1104
|
+
const abortRef = useRef(null);
|
|
1105
|
+
const paramsRef = globalThis.__QUOTE_PARAMS_REF__ ?? (globalThis.__QUOTE_PARAMS_REF__ = { current: initialParams ?? null });
|
|
1106
|
+
useEffect(() => {
|
|
1107
|
+
if (initialParams && JSON.stringify(paramsRef.current) !== JSON.stringify(initialParams)) {
|
|
1108
|
+
paramsRef.current = initialParams;
|
|
1109
|
+
void mutate(QUOTE_KEY);
|
|
1110
|
+
}
|
|
1111
|
+
}, [initialParams]);
|
|
1112
|
+
const fetcher = useCallback(async () => {
|
|
1113
|
+
const activeParams = paramsRef.current;
|
|
1114
|
+
if (!isValidQuoteParams(activeParams)) return null;
|
|
1115
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1116
|
+
const controller = new AbortController();
|
|
1117
|
+
abortRef.current = controller;
|
|
1118
|
+
try {
|
|
1119
|
+
return await sdk.getQuote(activeParams);
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1122
|
+
if (err instanceof Error) throw err;
|
|
1123
|
+
throw new Error(String(err));
|
|
1124
|
+
}
|
|
1125
|
+
}, [sdk]);
|
|
1126
|
+
const {
|
|
1127
|
+
data,
|
|
1128
|
+
error,
|
|
1129
|
+
isValidating: loading,
|
|
1130
|
+
mutate: mutateQuote
|
|
1131
|
+
} = useSWR(QUOTE_KEY, fetcher, {
|
|
1132
|
+
revalidateOnFocus: false,
|
|
1133
|
+
shouldRetryOnError: false,
|
|
1134
|
+
keepPreviousData: true
|
|
1135
|
+
});
|
|
1136
|
+
const refetch = useCallback(async () => {
|
|
1137
|
+
await mutateQuote();
|
|
1138
|
+
}, [mutateQuote]);
|
|
1139
|
+
const setParams = useCallback(
|
|
1140
|
+
(next) => {
|
|
1141
|
+
paramsRef.current = next;
|
|
1142
|
+
void mutate(QUOTE_KEY);
|
|
1143
|
+
},
|
|
1144
|
+
[]
|
|
1145
|
+
);
|
|
1146
|
+
return useMemo2(
|
|
1147
|
+
() => ({
|
|
1148
|
+
data,
|
|
1149
|
+
loading,
|
|
1150
|
+
error: error instanceof Error ? error.message : null,
|
|
1151
|
+
refetch,
|
|
1152
|
+
setParams,
|
|
1153
|
+
get activeParams() {
|
|
1154
|
+
return paramsRef.current;
|
|
1155
|
+
}
|
|
1156
|
+
}),
|
|
1157
|
+
[data, loading, error, refetch, setParams]
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// src/react/useExecuteTransaction.ts
|
|
1162
|
+
import { useState, useCallback as useCallback3 } from "react";
|
|
1163
|
+
|
|
1164
|
+
// src/react/useBalances.ts
|
|
1165
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
1166
|
+
import useSWR2, { mutate as mutate2 } from "swr";
|
|
1167
|
+
var BALANCE_KEY = "balances-global";
|
|
1168
|
+
function useBalances(initialParams) {
|
|
1169
|
+
const sdk = useSwap();
|
|
1170
|
+
const abortRef = useRef2(null);
|
|
1171
|
+
const paramsRef = globalThis.__BALANCES_PARAMS_REF__ ?? (globalThis.__BALANCES_PARAMS_REF__ = { current: initialParams ?? null });
|
|
1172
|
+
const fetcher = useCallback2(async () => {
|
|
1173
|
+
const activeParams = paramsRef.current;
|
|
1174
|
+
if (!activeParams) return null;
|
|
1175
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1176
|
+
const controller = new AbortController();
|
|
1177
|
+
abortRef.current = controller;
|
|
1178
|
+
try {
|
|
1179
|
+
return await sdk.getBalances(activeParams, { signal: controller.signal });
|
|
1180
|
+
} catch (err) {
|
|
1181
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1182
|
+
if (err instanceof Error) throw err;
|
|
1183
|
+
throw new Error(String(err));
|
|
1184
|
+
}
|
|
1185
|
+
}, [sdk, paramsRef]);
|
|
1186
|
+
const {
|
|
1187
|
+
data,
|
|
1188
|
+
error,
|
|
1189
|
+
isValidating: loading,
|
|
1190
|
+
mutate: mutateBalances
|
|
1191
|
+
} = useSWR2(BALANCE_KEY, fetcher, {
|
|
1192
|
+
revalidateOnFocus: false,
|
|
1193
|
+
shouldRetryOnError: false,
|
|
1194
|
+
keepPreviousData: true
|
|
1195
|
+
});
|
|
1196
|
+
const refetch = useCallback2(async () => {
|
|
1197
|
+
await mutateBalances();
|
|
1198
|
+
}, [mutateBalances]);
|
|
1199
|
+
const setParams = useCallback2((next) => {
|
|
1200
|
+
paramsRef.current = next;
|
|
1201
|
+
void mutate2(BALANCE_KEY);
|
|
1202
|
+
}, [paramsRef]);
|
|
746
1203
|
useEffect2(() => {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1204
|
+
if (initialParams) {
|
|
1205
|
+
const prevParams = paramsRef.current;
|
|
1206
|
+
const paramsChanged = !prevParams || prevParams.addresses.svm !== initialParams.addresses.svm || prevParams.addresses.evm !== initialParams.addresses.evm;
|
|
1207
|
+
if (paramsChanged) {
|
|
1208
|
+
paramsRef.current = initialParams;
|
|
1209
|
+
void mutate2(BALANCE_KEY);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}, [initialParams, paramsRef]);
|
|
1213
|
+
return useMemo3(
|
|
1214
|
+
() => ({
|
|
1215
|
+
data,
|
|
1216
|
+
loading,
|
|
1217
|
+
error: error instanceof Error ? error : null,
|
|
1218
|
+
refetch,
|
|
1219
|
+
setParams,
|
|
1220
|
+
get activeParams() {
|
|
1221
|
+
return paramsRef.current;
|
|
1222
|
+
}
|
|
1223
|
+
}),
|
|
1224
|
+
[data, loading, error, refetch, setParams, paramsRef]
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// src/react/useExecuteTransaction.ts
|
|
1229
|
+
function useExecuteTransaction() {
|
|
1230
|
+
const sdk = useSwap();
|
|
1231
|
+
const [isLoading, setLoading] = useState(false);
|
|
1232
|
+
const [stage, setStage] = useState(null);
|
|
1233
|
+
const [message, setMessage] = useState(null);
|
|
1234
|
+
const [error, setError] = useState(null);
|
|
1235
|
+
const [result, setResult] = useState(null);
|
|
1236
|
+
const { refetch: refetchQuote } = useQuote();
|
|
1237
|
+
const { refetch: refetchBalances } = useBalances();
|
|
1238
|
+
const execute = useCallback3(
|
|
752
1239
|
async ({
|
|
753
1240
|
quote,
|
|
754
1241
|
accountAddress,
|
|
755
1242
|
recipientAddress,
|
|
756
1243
|
wallet,
|
|
757
|
-
|
|
1244
|
+
orderType,
|
|
1245
|
+
options
|
|
758
1246
|
}) => {
|
|
759
|
-
if (!quote || !wallet) {
|
|
760
|
-
throw new Error("Quote and wallet are required for order execution.");
|
|
761
|
-
}
|
|
762
1247
|
setLoading(true);
|
|
763
1248
|
setError(null);
|
|
764
|
-
|
|
765
|
-
|
|
1249
|
+
setResult(null);
|
|
1250
|
+
const onStatus = (s, msg) => {
|
|
1251
|
+
setStage(s);
|
|
1252
|
+
setMessage(msg ?? "");
|
|
1253
|
+
};
|
|
766
1254
|
try {
|
|
767
|
-
const
|
|
768
|
-
const onStatus = (stage, msg) => {
|
|
769
|
-
if (!isMounted.current) return;
|
|
770
|
-
setStatus(stage);
|
|
771
|
-
if (msg) setMessage(msg);
|
|
772
|
-
};
|
|
773
|
-
const result = await executeOrder({
|
|
1255
|
+
const res = await sdk.executeTransaction({
|
|
774
1256
|
quote,
|
|
1257
|
+
wallet,
|
|
775
1258
|
accountAddress,
|
|
776
1259
|
recipientAddress,
|
|
777
|
-
wallet,
|
|
778
1260
|
onStatus,
|
|
779
|
-
|
|
1261
|
+
orderType,
|
|
1262
|
+
options
|
|
780
1263
|
});
|
|
781
|
-
if (
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
return result;
|
|
786
|
-
} catch (err) {
|
|
787
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
788
|
-
if (isMounted.current) {
|
|
789
|
-
setError(errorObj);
|
|
790
|
-
setStatus("error");
|
|
791
|
-
setMessage(errorObj.message);
|
|
792
|
-
setData({
|
|
793
|
-
status: false,
|
|
794
|
-
stage: "error",
|
|
795
|
-
message: errorObj.message
|
|
1264
|
+
if (res && typeof res === "object" && "status" in res && "stage" in res && typeof res.stage === "string") {
|
|
1265
|
+
setResult({
|
|
1266
|
+
...res,
|
|
1267
|
+
stage: res.stage
|
|
796
1268
|
});
|
|
1269
|
+
} else {
|
|
1270
|
+
setResult(res);
|
|
797
1271
|
}
|
|
798
|
-
return
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1272
|
+
return res;
|
|
1273
|
+
} catch (err) {
|
|
1274
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1275
|
+
setError(msg);
|
|
1276
|
+
throw err;
|
|
803
1277
|
} finally {
|
|
804
|
-
|
|
1278
|
+
await Promise.allSettled([refetchQuote(), refetchBalances()]);
|
|
1279
|
+
setLoading(false);
|
|
805
1280
|
}
|
|
806
1281
|
},
|
|
807
|
-
[]
|
|
1282
|
+
[sdk, refetchQuote, refetchBalances]
|
|
808
1283
|
);
|
|
809
1284
|
return {
|
|
810
|
-
/** Executes the swap order. */
|
|
811
1285
|
execute,
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
/** Human-readable status message. */
|
|
1286
|
+
isLoading,
|
|
1287
|
+
stage,
|
|
815
1288
|
message,
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
/** Raw SDK response data. */
|
|
819
|
-
data,
|
|
820
|
-
/** Captured error (if execution failed). */
|
|
821
|
-
error
|
|
1289
|
+
error,
|
|
1290
|
+
result
|
|
822
1291
|
};
|
|
823
1292
|
}
|
|
824
1293
|
|
|
825
|
-
// src/react/
|
|
826
|
-
import {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1294
|
+
// src/react/useTokenList.ts
|
|
1295
|
+
import { useRef as useRef3, useState as useState2, useCallback as useCallback4 } from "react";
|
|
1296
|
+
function useTokenList() {
|
|
1297
|
+
const [tokens, setTokens] = useState2([]);
|
|
1298
|
+
const [loading, setLoading] = useState2(false);
|
|
1299
|
+
const [error, setError] = useState2(null);
|
|
1300
|
+
const [hasMore, setHasMore] = useState2(true);
|
|
1301
|
+
const [page, setPage] = useState2(1);
|
|
1302
|
+
const sdk = useSwap();
|
|
1303
|
+
const pageRef = useRef3(1);
|
|
1304
|
+
const hasMoreRef = useRef3(true);
|
|
1305
|
+
const lastQuery = useRef3({});
|
|
1306
|
+
const cache = useRef3(/* @__PURE__ */ new Map());
|
|
1307
|
+
const isLoadingRef = useRef3(false);
|
|
1308
|
+
pageRef.current = page;
|
|
1309
|
+
hasMoreRef.current = hasMore;
|
|
1310
|
+
const loadTokens = useCallback4(
|
|
1311
|
+
async (params) => {
|
|
1312
|
+
const { q, networkId, reset } = params;
|
|
1313
|
+
if (isLoadingRef.current && !reset) return;
|
|
1314
|
+
try {
|
|
1315
|
+
let currentPage = pageRef.current;
|
|
1316
|
+
if (reset) {
|
|
1317
|
+
currentPage = 1;
|
|
1318
|
+
setTokens([]);
|
|
1319
|
+
setPage(1);
|
|
1320
|
+
setHasMore(true);
|
|
1321
|
+
pageRef.current = 1;
|
|
1322
|
+
hasMoreRef.current = true;
|
|
1323
|
+
}
|
|
1324
|
+
if (!reset && !hasMoreRef.current) return;
|
|
1325
|
+
isLoadingRef.current = true;
|
|
1326
|
+
setLoading(true);
|
|
1327
|
+
setError(null);
|
|
1328
|
+
const cacheKey = JSON.stringify({
|
|
1329
|
+
q: q?.toLowerCase() ?? "",
|
|
1330
|
+
networkId: networkId ?? void 0,
|
|
1331
|
+
page: currentPage
|
|
1332
|
+
});
|
|
1333
|
+
if (cache.current.has(cacheKey)) {
|
|
1334
|
+
const cached = cache.current.get(cacheKey);
|
|
1335
|
+
setTokens((prev) => reset ? cached.results : [...prev, ...cached.results]);
|
|
1336
|
+
if (!reset && cached.results.length > 0) {
|
|
1337
|
+
const nextPage = currentPage + 1;
|
|
1338
|
+
setPage(nextPage);
|
|
1339
|
+
pageRef.current = nextPage;
|
|
1340
|
+
}
|
|
1341
|
+
isLoadingRef.current = false;
|
|
1342
|
+
setLoading(false);
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
const apiParams = {
|
|
1346
|
+
q,
|
|
1347
|
+
page: currentPage,
|
|
1348
|
+
limit: 20,
|
|
1349
|
+
...networkId !== void 0 ? { networkId } : {}
|
|
1350
|
+
};
|
|
1351
|
+
const res = await sdk.getTokenList(apiParams);
|
|
1352
|
+
cache.current.set(cacheKey, res);
|
|
1353
|
+
setTokens((prev) => reset ? res.results : [...prev, ...res.results]);
|
|
1354
|
+
const isLastPage = res.results.length === 0 || res.results.length < 20 || res.count && currentPage * 20 >= res.count;
|
|
1355
|
+
setHasMore(!isLastPage);
|
|
1356
|
+
hasMoreRef.current = !isLastPage;
|
|
1357
|
+
if (!reset && !isLastPage) {
|
|
1358
|
+
const nextPage = currentPage + 1;
|
|
1359
|
+
setPage(nextPage);
|
|
1360
|
+
pageRef.current = nextPage;
|
|
1361
|
+
}
|
|
1362
|
+
lastQuery.current = { q, networkId };
|
|
1363
|
+
} catch (e) {
|
|
1364
|
+
console.error("useTokenList error:", e);
|
|
1365
|
+
setError("Failed to load tokens");
|
|
1366
|
+
} finally {
|
|
1367
|
+
setLoading(false);
|
|
1368
|
+
isLoadingRef.current = false;
|
|
1369
|
+
}
|
|
885
1370
|
},
|
|
886
|
-
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// src/react/useQuote.ts
|
|
891
|
-
function useQuote(params, options) {
|
|
892
|
-
const [data, setData] = useState3(null);
|
|
893
|
-
const [loading, setLoading] = useState3(false);
|
|
894
|
-
const [error, setError] = useState3(null);
|
|
895
|
-
const [warning, setWarning] = useState3(null);
|
|
896
|
-
const debounceMs = options?.debounceMs ?? 250;
|
|
897
|
-
const autoRefreshMs = options?.autoRefreshMs;
|
|
898
|
-
const abortRef = useRef3(null);
|
|
899
|
-
const debounceRef = useRef3(null);
|
|
900
|
-
const mounted = useRef3(false);
|
|
901
|
-
const paramsKey = useMemo2(
|
|
902
|
-
() => params ? JSON.stringify(serializeBigIntsToStrings(params)) : null,
|
|
903
|
-
[params]
|
|
1371
|
+
[sdk]
|
|
904
1372
|
);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1373
|
+
const resetTokens = useCallback4(() => {
|
|
1374
|
+
setTokens([]);
|
|
1375
|
+
setPage(1);
|
|
1376
|
+
setHasMore(true);
|
|
1377
|
+
pageRef.current = 1;
|
|
1378
|
+
hasMoreRef.current = true;
|
|
1379
|
+
cache.current.clear();
|
|
1380
|
+
lastQuery.current = {};
|
|
912
1381
|
}, []);
|
|
913
|
-
const fetchQuote = useCallback2(async () => {
|
|
914
|
-
if (!params) return;
|
|
915
|
-
try {
|
|
916
|
-
setLoading(true);
|
|
917
|
-
setWarning(null);
|
|
918
|
-
const result = await getQuote(params);
|
|
919
|
-
const serializeResult = serializeBigIntsToStrings(result);
|
|
920
|
-
if (!mounted.current) return;
|
|
921
|
-
setData((prev) => {
|
|
922
|
-
if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
|
|
923
|
-
return serializeResult;
|
|
924
|
-
});
|
|
925
|
-
setWarning(result.warning ?? null);
|
|
926
|
-
setError(null);
|
|
927
|
-
} catch (err) {
|
|
928
|
-
if (err.name === "AbortError") return;
|
|
929
|
-
console.error("[useQuote] fetch error:", err);
|
|
930
|
-
if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
931
|
-
} finally {
|
|
932
|
-
if (mounted.current) setLoading(false);
|
|
933
|
-
}
|
|
934
|
-
}, [paramsKey]);
|
|
935
|
-
useEffect3(() => {
|
|
936
|
-
if (!paramsKey) return;
|
|
937
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
938
|
-
debounceRef.current = setTimeout(() => {
|
|
939
|
-
fetchQuote();
|
|
940
|
-
}, debounceMs);
|
|
941
|
-
return () => {
|
|
942
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
943
|
-
abortRef.current?.abort();
|
|
944
|
-
};
|
|
945
|
-
}, [paramsKey, debounceMs, fetchQuote]);
|
|
946
|
-
useEffect3(() => {
|
|
947
|
-
if (!autoRefreshMs || !paramsKey) return;
|
|
948
|
-
const interval = setInterval(() => fetchQuote(), autoRefreshMs);
|
|
949
|
-
return () => clearInterval(interval);
|
|
950
|
-
}, [autoRefreshMs, paramsKey, fetchQuote]);
|
|
951
|
-
return useMemo2(
|
|
952
|
-
() => ({
|
|
953
|
-
data,
|
|
954
|
-
loading,
|
|
955
|
-
error,
|
|
956
|
-
warning,
|
|
957
|
-
refetch: fetchQuote
|
|
958
|
-
}),
|
|
959
|
-
[data, loading, error, warning, fetchQuote]
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
// src/react/useBalances.ts
|
|
964
|
-
import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
965
|
-
|
|
966
|
-
// src/core/getBalances.ts
|
|
967
|
-
import { TOKEN_SEARCH_API_BASE_URL } from "@shogun-sdk/intents-sdk";
|
|
968
|
-
async function getBalances(params, options) {
|
|
969
|
-
const { addresses, cursorEvm, cursorSvm } = params;
|
|
970
|
-
const { signal } = options ?? {};
|
|
971
|
-
if (!addresses?.evm && !addresses?.svm) {
|
|
972
|
-
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
973
|
-
}
|
|
974
|
-
const payload = JSON.stringify({
|
|
975
|
-
addresses,
|
|
976
|
-
cursorEvm,
|
|
977
|
-
cursorSvm
|
|
978
|
-
});
|
|
979
|
-
const start = performance.now();
|
|
980
|
-
const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
981
|
-
method: "POST",
|
|
982
|
-
headers: {
|
|
983
|
-
accept: "application/json",
|
|
984
|
-
"Content-Type": "application/json"
|
|
985
|
-
},
|
|
986
|
-
body: payload,
|
|
987
|
-
signal
|
|
988
|
-
}).catch((err) => {
|
|
989
|
-
if (err.name === "AbortError") {
|
|
990
|
-
throw new Error("Balance request was cancelled.");
|
|
991
|
-
}
|
|
992
|
-
throw err;
|
|
993
|
-
});
|
|
994
|
-
if (!response.ok) {
|
|
995
|
-
const text = await response.text().catch(() => "");
|
|
996
|
-
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
997
|
-
}
|
|
998
|
-
const data = await response.json().catch(() => {
|
|
999
|
-
throw new Error("Invalid JSON response from balances API.");
|
|
1000
|
-
});
|
|
1001
|
-
const duration = (performance.now() - start).toFixed(1);
|
|
1002
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1003
|
-
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
1004
|
-
}
|
|
1005
|
-
const evmItems = data.evm?.items ?? [];
|
|
1006
|
-
const svmItems = data.svm?.items ?? [];
|
|
1007
|
-
const combined = [...evmItems, ...svmItems];
|
|
1008
1382
|
return {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1383
|
+
tokens,
|
|
1384
|
+
loading,
|
|
1385
|
+
error,
|
|
1386
|
+
hasMore,
|
|
1387
|
+
page,
|
|
1388
|
+
lastQuery: lastQuery.current,
|
|
1389
|
+
loadTokens,
|
|
1390
|
+
resetTokens
|
|
1012
1391
|
};
|
|
1013
1392
|
}
|
|
1014
1393
|
|
|
1015
|
-
// src/react/
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
const
|
|
1019
|
-
const [
|
|
1020
|
-
const
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
addresses: {
|
|
1026
|
-
evm: addresses?.evm ?? void 0,
|
|
1027
|
-
svm: addresses?.svm ?? void 0
|
|
1028
|
-
},
|
|
1029
|
-
cursorEvm,
|
|
1030
|
-
cursorSvm
|
|
1031
|
-
};
|
|
1032
|
-
}, [params?.addresses?.evm, params?.addresses?.svm, params?.cursorEvm, params?.cursorSvm]);
|
|
1033
|
-
const fetchBalances = useCallback3(async () => {
|
|
1034
|
-
if (!stableParams) return;
|
|
1035
|
-
if (abortRef.current) abortRef.current.abort();
|
|
1036
|
-
const controller = new AbortController();
|
|
1037
|
-
abortRef.current = controller;
|
|
1394
|
+
// src/react/useTokensData.ts
|
|
1395
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1396
|
+
function useTokensData(addresses) {
|
|
1397
|
+
const sdk = useSwap();
|
|
1398
|
+
const [data, setData] = useState3([]);
|
|
1399
|
+
const [loading, setLoading] = useState3(false);
|
|
1400
|
+
const [error, setError] = useState3(null);
|
|
1401
|
+
useEffect3(() => {
|
|
1402
|
+
if (!addresses?.length) return;
|
|
1403
|
+
let isMounted = true;
|
|
1038
1404
|
setLoading(true);
|
|
1039
1405
|
setError(null);
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
return result;
|
|
1047
|
-
} catch (err) {
|
|
1048
|
-
if (err.name === "AbortError") return;
|
|
1049
|
-
const e = err instanceof Error ? err : new Error(String(err));
|
|
1050
|
-
setError(e);
|
|
1051
|
-
throw e;
|
|
1052
|
-
} finally {
|
|
1053
|
-
setLoading(false);
|
|
1054
|
-
}
|
|
1055
|
-
}, [stableParams]);
|
|
1056
|
-
useEffect4(() => {
|
|
1057
|
-
if (stableParams) fetchBalances().catch(() => {
|
|
1406
|
+
sdk.getTokensData(addresses).then((res) => {
|
|
1407
|
+
if (isMounted) setData(res);
|
|
1408
|
+
}).catch((e) => {
|
|
1409
|
+
if (isMounted) setError(e instanceof Error ? e : new Error(String(e)));
|
|
1410
|
+
}).finally(() => {
|
|
1411
|
+
if (isMounted) setLoading(false);
|
|
1058
1412
|
});
|
|
1059
1413
|
return () => {
|
|
1060
|
-
|
|
1414
|
+
isMounted = false;
|
|
1061
1415
|
};
|
|
1062
|
-
}, [
|
|
1063
|
-
return
|
|
1064
|
-
() => ({
|
|
1065
|
-
/** Latest fetched balance data */
|
|
1066
|
-
data,
|
|
1067
|
-
/** Whether the hook is currently fetching */
|
|
1068
|
-
loading,
|
|
1069
|
-
/** Error object if fetching failed */
|
|
1070
|
-
error,
|
|
1071
|
-
/** Manually trigger a refresh */
|
|
1072
|
-
refetch: fetchBalances
|
|
1073
|
-
}),
|
|
1074
|
-
[data, loading, error, fetchBalances]
|
|
1075
|
-
);
|
|
1416
|
+
}, [sdk, JSON.stringify(addresses)]);
|
|
1417
|
+
return { data, loading, error };
|
|
1076
1418
|
}
|
|
1077
|
-
|
|
1078
|
-
// src/react/index.ts
|
|
1079
|
-
import { ChainID as ChainID4, isEvmChain as isEvmChain3 } from "@shogun-sdk/intents-sdk";
|
|
1080
1419
|
export {
|
|
1081
|
-
|
|
1082
|
-
|
|
1420
|
+
ChainId,
|
|
1421
|
+
OrderExecutionType,
|
|
1422
|
+
SupportedChains,
|
|
1423
|
+
SwapProvider,
|
|
1424
|
+
buildQuoteParams,
|
|
1425
|
+
isEvmChain,
|
|
1083
1426
|
useBalances,
|
|
1084
|
-
|
|
1427
|
+
useExecuteTransaction,
|
|
1085
1428
|
useQuote,
|
|
1086
|
-
|
|
1429
|
+
useSwap,
|
|
1430
|
+
useTokenList,
|
|
1431
|
+
useTokensData
|
|
1087
1432
|
};
|