@shogun-sdk/swap 0.0.2-test.25 → 0.0.2-test.27
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 +408 -110
- package/dist/core.d.cts +4 -137
- package/dist/core.d.ts +4 -137
- package/dist/core.js +386 -86
- package/dist/index-CmsKzdEu.d.cts +377 -0
- package/dist/index-Qoc6Q9XN.d.ts +377 -0
- package/dist/index.cjs +401 -497
- package/dist/index.d.cts +4 -9
- package/dist/index.d.ts +4 -9
- package/dist/index.js +379 -475
- package/dist/react.cjs +924 -469
- package/dist/react.d.cts +258 -82
- package/dist/react.d.ts +258 -82
- package/dist/react.js +890 -443
- 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,88 +1,16 @@
|
|
|
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
|
|
88
16
|
import { zeroAddress } from "viem";
|
|
@@ -102,11 +30,25 @@ function normalizeEvmTokenAddress(address) {
|
|
|
102
30
|
}
|
|
103
31
|
|
|
104
32
|
// src/utils/chain.ts
|
|
105
|
-
import { ChainID } from "@shogun-sdk/intents-sdk";
|
|
106
|
-
var SOLANA_CHAIN_ID =
|
|
107
|
-
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 = [
|
|
108
50
|
{
|
|
109
|
-
id:
|
|
51
|
+
id: BaseChainID.Arbitrum,
|
|
110
52
|
name: "Arbitrum",
|
|
111
53
|
isEVM: true,
|
|
112
54
|
wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -115,7 +57,7 @@ var SupportedChains = [
|
|
|
115
57
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
116
58
|
},
|
|
117
59
|
{
|
|
118
|
-
id:
|
|
60
|
+
id: BaseChainID.Optimism,
|
|
119
61
|
name: "Optimism",
|
|
120
62
|
isEVM: true,
|
|
121
63
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -124,7 +66,7 @@ var SupportedChains = [
|
|
|
124
66
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
125
67
|
},
|
|
126
68
|
{
|
|
127
|
-
id:
|
|
69
|
+
id: BaseChainID.Base,
|
|
128
70
|
name: "Base",
|
|
129
71
|
isEVM: true,
|
|
130
72
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -133,7 +75,7 @@ var SupportedChains = [
|
|
|
133
75
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
134
76
|
},
|
|
135
77
|
{
|
|
136
|
-
id:
|
|
78
|
+
id: BaseChainID.Hyperliquid,
|
|
137
79
|
name: "Hyperliquid",
|
|
138
80
|
isEVM: true,
|
|
139
81
|
wrapped: "0x5555555555555555555555555555555555555555",
|
|
@@ -142,7 +84,7 @@ var SupportedChains = [
|
|
|
142
84
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
143
85
|
},
|
|
144
86
|
{
|
|
145
|
-
id:
|
|
87
|
+
id: BaseChainID.BSC,
|
|
146
88
|
name: "BSC",
|
|
147
89
|
isEVM: true,
|
|
148
90
|
wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
@@ -160,6 +102,10 @@ var SupportedChains = [
|
|
|
160
102
|
tokenAddress: NATIVE_TOKEN.SOL
|
|
161
103
|
}
|
|
162
104
|
];
|
|
105
|
+
var SupportedChains = SupportedChainsInternal.filter(
|
|
106
|
+
(c) => CURRENT_SUPPORTED.includes(c.id)
|
|
107
|
+
);
|
|
108
|
+
var isEvmChain = isEvmChainIntent;
|
|
163
109
|
|
|
164
110
|
// src/utils/viem.ts
|
|
165
111
|
function isViemWalletClient(wallet) {
|
|
@@ -187,9 +133,197 @@ function serializeBigIntsToStrings(obj) {
|
|
|
187
133
|
return obj;
|
|
188
134
|
}
|
|
189
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
|
+
|
|
190
326
|
// src/wallet-adapter/evm-wallet-adapter/adapter.ts
|
|
191
|
-
import { utils as ethersUtils } from "ethers/lib/ethers.js";
|
|
192
|
-
import { hexValue } from "ethers/lib/utils.js";
|
|
193
327
|
import {
|
|
194
328
|
custom,
|
|
195
329
|
publicActions
|
|
@@ -279,26 +413,14 @@ var adaptViemWallet = (wallet) => {
|
|
|
279
413
|
};
|
|
280
414
|
};
|
|
281
415
|
|
|
282
|
-
// src/core/
|
|
416
|
+
// src/core/execute/handleEvmExecution.ts
|
|
283
417
|
import {
|
|
284
418
|
getEVMSingleChainOrderTypedData,
|
|
285
419
|
getEVMCrossChainOrderTypedData
|
|
286
420
|
} from "@shogun-sdk/intents-sdk";
|
|
287
421
|
import { encodeFunctionData as encodeFunctionData2 } from "viem";
|
|
288
422
|
|
|
289
|
-
// src/core/
|
|
290
|
-
import { isEvmChain } from "@shogun-sdk/intents-sdk";
|
|
291
|
-
function normalizeNative(chainId, address) {
|
|
292
|
-
if (isEvmChain(chainId) && isNativeAddress(address)) {
|
|
293
|
-
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
294
|
-
if (!chain?.wrapped)
|
|
295
|
-
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
296
|
-
return chain.wrapped;
|
|
297
|
-
}
|
|
298
|
-
return address;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// src/core/executeOrder/stageMessages.ts
|
|
423
|
+
// src/core/execute/stageMessages.ts
|
|
302
424
|
var DEFAULT_STAGE_MESSAGES = {
|
|
303
425
|
processing: "Preparing transaction for execution",
|
|
304
426
|
approving: "Approving token allowance",
|
|
@@ -307,20 +429,44 @@ var DEFAULT_STAGE_MESSAGES = {
|
|
|
307
429
|
submitting: "Submitting transaction",
|
|
308
430
|
initiated: "Transaction initiated.",
|
|
309
431
|
success: "Transaction Executed successfully",
|
|
432
|
+
success_limit: "Limit order has been submitted successfully.",
|
|
310
433
|
shogun_processing: "Shogun is processing your transaction",
|
|
311
434
|
error: "Transaction failed during submission"
|
|
312
435
|
};
|
|
313
436
|
|
|
314
|
-
// src/core/
|
|
437
|
+
// src/core/execute/buildOrder.ts
|
|
315
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
|
|
316
449
|
async function buildOrder({
|
|
317
450
|
quote,
|
|
318
451
|
accountAddress,
|
|
319
452
|
destination,
|
|
320
453
|
deadline,
|
|
321
|
-
isSingleChain
|
|
454
|
+
isSingleChain,
|
|
455
|
+
orderType,
|
|
456
|
+
options
|
|
322
457
|
}) {
|
|
323
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
|
+
}
|
|
324
470
|
if (isSingleChain) {
|
|
325
471
|
return await SingleChainOrder.create({
|
|
326
472
|
user: accountAddress,
|
|
@@ -328,7 +474,7 @@ async function buildOrder({
|
|
|
328
474
|
tokenIn: tokenIn.address,
|
|
329
475
|
tokenOut: tokenOut.address,
|
|
330
476
|
amountIn: quote.amountIn,
|
|
331
|
-
amountOutMin
|
|
477
|
+
amountOutMin,
|
|
332
478
|
deadline,
|
|
333
479
|
destinationAddress: destination
|
|
334
480
|
});
|
|
@@ -342,7 +488,7 @@ async function buildOrder({
|
|
|
342
488
|
destinationTokenAddress: tokenOut.address,
|
|
343
489
|
destinationAddress: destination,
|
|
344
490
|
deadline,
|
|
345
|
-
destinationTokenMinAmount:
|
|
491
|
+
destinationTokenMinAmount: amountOutMin,
|
|
346
492
|
minStablecoinAmount: quote.minStablecoinsAmount
|
|
347
493
|
});
|
|
348
494
|
}
|
|
@@ -413,7 +559,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
|
|
|
413
559
|
});
|
|
414
560
|
}
|
|
415
561
|
|
|
416
|
-
// src/core/
|
|
562
|
+
// src/core/execute/handleOrderPollingResult.ts
|
|
417
563
|
async function handleOrderPollingResult({
|
|
418
564
|
status,
|
|
419
565
|
orderId,
|
|
@@ -451,7 +597,7 @@ async function handleOrderPollingResult({
|
|
|
451
597
|
};
|
|
452
598
|
}
|
|
453
599
|
|
|
454
|
-
// src/core/
|
|
600
|
+
// src/core/execute/ensurePermit2Allowance.ts
|
|
455
601
|
import { encodeFunctionData, erc20Abi, maxUint256 } from "viem";
|
|
456
602
|
import { PERMIT2_ADDRESS } from "@shogun-sdk/intents-sdk";
|
|
457
603
|
async function ensurePermit2Allowance({
|
|
@@ -496,7 +642,7 @@ async function ensurePermit2Allowance({
|
|
|
496
642
|
);
|
|
497
643
|
}
|
|
498
644
|
|
|
499
|
-
// src/core/
|
|
645
|
+
// src/core/execute/handleEvmExecution.ts
|
|
500
646
|
async function handleEvmExecution({
|
|
501
647
|
recipientAddress,
|
|
502
648
|
quote,
|
|
@@ -504,16 +650,19 @@ async function handleEvmExecution({
|
|
|
504
650
|
accountAddress,
|
|
505
651
|
wallet,
|
|
506
652
|
isSingleChain,
|
|
507
|
-
|
|
508
|
-
|
|
653
|
+
update,
|
|
654
|
+
orderType,
|
|
655
|
+
options
|
|
509
656
|
}) {
|
|
510
|
-
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;
|
|
511
659
|
await wallet.switchChain(chainId);
|
|
512
660
|
const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
|
|
513
661
|
quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
|
|
514
662
|
const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
|
|
515
663
|
update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
|
|
516
664
|
if (shouldWrapNative) {
|
|
665
|
+
quote.tokenIn.address === tokenIn;
|
|
517
666
|
await wallet.sendTransaction({
|
|
518
667
|
to: tokenIn,
|
|
519
668
|
data: encodeFunctionData2({
|
|
@@ -540,7 +689,9 @@ async function handleEvmExecution({
|
|
|
540
689
|
accountAddress,
|
|
541
690
|
destination,
|
|
542
691
|
deadline,
|
|
543
|
-
isSingleChain
|
|
692
|
+
isSingleChain,
|
|
693
|
+
orderType,
|
|
694
|
+
options
|
|
544
695
|
});
|
|
545
696
|
console.debug(`order`, order);
|
|
546
697
|
update("processing", messageFor("signing"));
|
|
@@ -564,17 +715,28 @@ async function handleEvmExecution({
|
|
|
564
715
|
update("initiated", messageFor("initiated"));
|
|
565
716
|
const { intentId: orderId } = res.data;
|
|
566
717
|
update("initiated", messageFor("shogun_processing"));
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
+
}
|
|
575
737
|
}
|
|
576
738
|
|
|
577
|
-
// src/core/
|
|
739
|
+
// src/core/execute/handleSolanaExecution.ts
|
|
578
740
|
import {
|
|
579
741
|
getSolanaSingleChainOrderInstructions,
|
|
580
742
|
getSolanaCrossChainOrderInstructions
|
|
@@ -587,12 +749,14 @@ async function handleSolanaExecution({
|
|
|
587
749
|
isSingleChain,
|
|
588
750
|
update,
|
|
589
751
|
accountAddress,
|
|
590
|
-
|
|
752
|
+
orderType,
|
|
753
|
+
options
|
|
591
754
|
}) {
|
|
592
755
|
if (!wallet.rpcUrl) {
|
|
593
756
|
throw new Error("Solana wallet is missing rpcUrl");
|
|
594
757
|
}
|
|
595
|
-
const
|
|
758
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
759
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
596
760
|
update("processing", messageFor("processing"));
|
|
597
761
|
const destination = recipientAddress ?? accountAddress;
|
|
598
762
|
const order = await buildOrder({
|
|
@@ -600,7 +764,9 @@ async function handleSolanaExecution({
|
|
|
600
764
|
accountAddress,
|
|
601
765
|
destination,
|
|
602
766
|
deadline,
|
|
603
|
-
isSingleChain
|
|
767
|
+
isSingleChain,
|
|
768
|
+
orderType,
|
|
769
|
+
options
|
|
604
770
|
});
|
|
605
771
|
const txData = await getSolanaOrderInstructions({
|
|
606
772
|
order,
|
|
@@ -620,16 +786,27 @@ async function handleSolanaExecution({
|
|
|
620
786
|
throw new Error("Auctioneer submission failed");
|
|
621
787
|
}
|
|
622
788
|
update("initiated", messageFor("initiated"));
|
|
623
|
-
const {
|
|
789
|
+
const { intentId: orderId } = response.data;
|
|
624
790
|
update("initiated", messageFor("shogun_processing"));
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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(accountAddress, orderId);
|
|
802
|
+
return await handleOrderPollingResult({
|
|
803
|
+
status,
|
|
804
|
+
orderId,
|
|
805
|
+
chainId: SOLANA_CHAIN_ID,
|
|
806
|
+
update,
|
|
807
|
+
messageFor
|
|
808
|
+
});
|
|
809
|
+
}
|
|
633
810
|
}
|
|
634
811
|
async function getSolanaOrderInstructions({
|
|
635
812
|
order,
|
|
@@ -662,13 +839,14 @@ async function submitToAuctioneer({
|
|
|
662
839
|
});
|
|
663
840
|
}
|
|
664
841
|
|
|
665
|
-
// src/core/
|
|
842
|
+
// src/core/execute/execute.ts
|
|
666
843
|
async function executeOrder({
|
|
667
844
|
quote,
|
|
668
845
|
accountAddress,
|
|
669
846
|
recipientAddress,
|
|
670
847
|
wallet,
|
|
671
848
|
onStatus,
|
|
849
|
+
orderType = "market" /* MARKET */,
|
|
672
850
|
options = {}
|
|
673
851
|
}) {
|
|
674
852
|
const isDev = process.env.NODE_ENV !== "production";
|
|
@@ -681,21 +859,31 @@ async function executeOrder({
|
|
|
681
859
|
onStatus?.(stage, message ?? messageFor(stage));
|
|
682
860
|
};
|
|
683
861
|
try {
|
|
684
|
-
const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
685
862
|
log("Starting execution:", {
|
|
686
863
|
accountAddress,
|
|
687
864
|
recipientAddress,
|
|
688
|
-
deadline,
|
|
689
865
|
tokenIn: quote?.tokenIn,
|
|
690
866
|
tokenOut: quote?.tokenOut
|
|
691
867
|
});
|
|
692
868
|
const adapter = normalizeWallet(wallet);
|
|
693
869
|
if (!adapter) throw new Error("No wallet provided");
|
|
694
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
|
+
}
|
|
695
883
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
696
884
|
const chainId = Number(tokenIn.chainId);
|
|
697
885
|
update("processing");
|
|
698
|
-
if (
|
|
886
|
+
if (isEvmChain3(chainId)) {
|
|
699
887
|
log("Detected EVM chain:", chainId);
|
|
700
888
|
const result = await handleEvmExecution({
|
|
701
889
|
recipientAddress,
|
|
@@ -704,13 +892,14 @@ async function executeOrder({
|
|
|
704
892
|
accountAddress,
|
|
705
893
|
wallet: adapter,
|
|
706
894
|
isSingleChain,
|
|
707
|
-
|
|
708
|
-
|
|
895
|
+
update,
|
|
896
|
+
orderType,
|
|
897
|
+
options
|
|
709
898
|
});
|
|
710
899
|
log("EVM execution result:", result);
|
|
711
900
|
return result;
|
|
712
901
|
}
|
|
713
|
-
if (chainId ===
|
|
902
|
+
if (chainId === ChainID2.Solana) {
|
|
714
903
|
log("Detected Solana chain");
|
|
715
904
|
const result = await handleSolanaExecution({
|
|
716
905
|
recipientAddress,
|
|
@@ -718,8 +907,9 @@ async function executeOrder({
|
|
|
718
907
|
accountAddress,
|
|
719
908
|
wallet: adapter,
|
|
720
909
|
isSingleChain,
|
|
721
|
-
|
|
722
|
-
|
|
910
|
+
update,
|
|
911
|
+
orderType,
|
|
912
|
+
options
|
|
723
913
|
});
|
|
724
914
|
log("Solana execution result:", result);
|
|
725
915
|
return result;
|
|
@@ -729,8 +919,13 @@ async function executeOrder({
|
|
|
729
919
|
log("Error:", unsupported);
|
|
730
920
|
return { status: false, message: unsupported, stage: "error" };
|
|
731
921
|
} catch (error) {
|
|
732
|
-
|
|
733
|
-
|
|
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
|
+
}
|
|
734
929
|
update("error", message);
|
|
735
930
|
return { status: false, message, stage: "error" };
|
|
736
931
|
}
|
|
@@ -741,353 +936,605 @@ function normalizeWallet(wallet) {
|
|
|
741
936
|
return wallet;
|
|
742
937
|
}
|
|
743
938
|
|
|
744
|
-
// src/
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
939
|
+
// src/core/orders/getOrders.ts
|
|
940
|
+
import { fetchUserOrders } from "@shogun-sdk/intents-sdk";
|
|
941
|
+
async function getOrders({
|
|
942
|
+
evmAddress,
|
|
943
|
+
solAddress
|
|
944
|
+
}) {
|
|
945
|
+
if (!evmAddress && !solAddress) {
|
|
946
|
+
throw new Error("At least one wallet address (EVM, Solana) must be provided.");
|
|
947
|
+
}
|
|
948
|
+
const orders = await fetchUserOrders(evmAddress, solAddress);
|
|
949
|
+
return orders;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/core/client.ts
|
|
953
|
+
var SwapSDK = class {
|
|
954
|
+
constructor(config) {
|
|
955
|
+
__publicField(this, "apiKey");
|
|
956
|
+
/**
|
|
957
|
+
* Fetches metadata for one or more tokens from the Shogun Token Search API.
|
|
958
|
+
*
|
|
959
|
+
* ---
|
|
960
|
+
* ### Overview
|
|
961
|
+
* `getTokensData` retrieves normalized token information — such as symbol, name,
|
|
962
|
+
* decimals, logo URI, and verified status — for a given list of token addresses.
|
|
963
|
+
*
|
|
964
|
+
* It supports both **EVM** and **SVM (Solana)** tokens, returning metadata from
|
|
965
|
+
* Shogun’s unified token registry.
|
|
966
|
+
*
|
|
967
|
+
* ---
|
|
968
|
+
* @example
|
|
969
|
+
* ```ts
|
|
970
|
+
* const tokens = await getTokensData([
|
|
971
|
+
* "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
972
|
+
* "So11111111111111111111111111111111111111112", // SOL
|
|
973
|
+
* ]);
|
|
974
|
+
*
|
|
975
|
+
* console.log(tokens);
|
|
976
|
+
* [
|
|
977
|
+
* { symbol: "USDC", name: "USD Coin", chainId: 1, decimals: 6, ... },
|
|
978
|
+
* { symbol: "SOL", name: "Solana", chainId: 101, decimals: 9, ... }
|
|
979
|
+
* ]
|
|
980
|
+
* ```
|
|
981
|
+
*
|
|
982
|
+
* @param addresses - An array of token addresses (EVM or SVM) to fetch metadata for.
|
|
983
|
+
* @returns A promise resolving to an array of {@link TokenInfo} objects.
|
|
984
|
+
*
|
|
985
|
+
* @throws Will throw an error if the network request fails or the API responds with a non-OK status.
|
|
986
|
+
*/
|
|
987
|
+
__publicField(this, "getTokensData", getTokensData.bind(this));
|
|
988
|
+
if (!config.apiKey) {
|
|
989
|
+
throw new Error("SwapSDK: Missing API key");
|
|
990
|
+
}
|
|
991
|
+
this.apiKey = config.apiKey;
|
|
992
|
+
if (this.apiKey) void this.apiKey;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Retrieves a swap quote for the given input and output tokens.
|
|
996
|
+
*
|
|
997
|
+
* @param params - Quote parameters including source/destination tokens and amount.
|
|
998
|
+
* @returns A normalized `SwapQuoteResponse` containing output amount, route, and metadata.
|
|
999
|
+
*/
|
|
1000
|
+
async getQuote(params) {
|
|
1001
|
+
return getQuote(params);
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Fetches token balances for the specified user wallet(s).
|
|
1005
|
+
*
|
|
1006
|
+
* Supports both EVM and SVM (Solana) wallet addresses.
|
|
1007
|
+
*
|
|
1008
|
+
* @param params - Wallet address and optional chain filters.
|
|
1009
|
+
* @param options - Optional abort signal for cancellation.
|
|
1010
|
+
* @returns A unified balance response with per-chain token details.
|
|
1011
|
+
*/
|
|
1012
|
+
async getBalances(params, options) {
|
|
1013
|
+
return getBalances(params, options);
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Retrieves a list of verified tokens based on search query or chain filter.
|
|
1017
|
+
*
|
|
1018
|
+
* @param params - Search parameters (query, chain ID, pagination options).
|
|
1019
|
+
* @returns Paginated `TokenSearchResponse` containing token metadata.
|
|
1020
|
+
*/
|
|
1021
|
+
async getTokenList(params) {
|
|
1022
|
+
return getTokenList(params);
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Executes a prepared swap quote using the provided wallet and configuration.
|
|
1026
|
+
*
|
|
1027
|
+
* Handles:
|
|
1028
|
+
* - Token approval (if required)
|
|
1029
|
+
* - Transaction signing and broadcasting
|
|
1030
|
+
* - Confirmation polling and stage-based status updates
|
|
1031
|
+
*
|
|
1032
|
+
* Supports both:
|
|
1033
|
+
* - Market orders (with optional deadline)
|
|
1034
|
+
* - Limit orders (requires executionPrice and deadline)
|
|
1035
|
+
*
|
|
1036
|
+
* @param quote - The swap quote to execute, containing route and metadata.
|
|
1037
|
+
* @param accountAddress - The user's wallet address executing the swap.
|
|
1038
|
+
* @param recipientAddress - Optional recipient address for the output tokens (defaults to sender).
|
|
1039
|
+
* @param wallet - Adapted wallet instance (EVM/Solana) or a standard Viem `WalletClient`.
|
|
1040
|
+
* @param onStatus - Optional callback for receiving execution stage updates and messages.
|
|
1041
|
+
* @param orderType - Defines whether this is a market or limit order.
|
|
1042
|
+
* @param options - Execution parameters (different per order type).
|
|
1043
|
+
*
|
|
1044
|
+
* @returns A finalized execution result containing transaction hash, status, and any returned data.
|
|
1045
|
+
*
|
|
1046
|
+
* @example
|
|
1047
|
+
* ```ts
|
|
1048
|
+
* * Market order
|
|
1049
|
+
* const result = await sdk.executeTransaction({
|
|
1050
|
+
* quote,
|
|
1051
|
+
* accountAddress: "0x123...",
|
|
1052
|
+
* wallet,
|
|
1053
|
+
* orderType: OrderExecutionType.MARKET,
|
|
1054
|
+
* options: { deadline: 1800 },
|
|
1055
|
+
* onStatus: (stage, msg) => console.log(stage, msg),
|
|
1056
|
+
* });
|
|
1057
|
+
*
|
|
1058
|
+
* * Limit order
|
|
1059
|
+
* const result = await sdk.executeTransaction({
|
|
1060
|
+
* quote,
|
|
1061
|
+
* accountAddress: "0x123...",
|
|
1062
|
+
* wallet,
|
|
1063
|
+
* orderType: OrderExecutionType.LIMIT,
|
|
1064
|
+
* options: { executionPrice: "0.0021", deadline: 3600 },
|
|
1065
|
+
* });
|
|
1066
|
+
* ```
|
|
1067
|
+
*/
|
|
1068
|
+
async executeTransaction({
|
|
1069
|
+
quote,
|
|
1070
|
+
accountAddress,
|
|
1071
|
+
recipientAddress,
|
|
1072
|
+
wallet,
|
|
1073
|
+
onStatus,
|
|
1074
|
+
orderType,
|
|
1075
|
+
options
|
|
1076
|
+
}) {
|
|
1077
|
+
return executeOrder({
|
|
1078
|
+
quote,
|
|
1079
|
+
wallet,
|
|
1080
|
+
accountAddress,
|
|
1081
|
+
recipientAddress,
|
|
1082
|
+
onStatus,
|
|
1083
|
+
orderType,
|
|
1084
|
+
options
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Fetches all user orders (Market, Limit, Cross-chain) for connected wallets.
|
|
1089
|
+
*
|
|
1090
|
+
* ---
|
|
1091
|
+
* ### Overview
|
|
1092
|
+
* Retrieves both **single-chain** and **cross-chain** orders from the Shogun Intents API.
|
|
1093
|
+
* Works across EVM, Solana
|
|
1094
|
+
*
|
|
1095
|
+
* ---
|
|
1096
|
+
* @example
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* const orders = await sdk.getOrders({
|
|
1099
|
+
* evmAddress: "0x123...",
|
|
1100
|
+
* solAddress: "9d12hF...abc",
|
|
1101
|
+
* });
|
|
1102
|
+
*
|
|
1103
|
+
* console.log(orders.singleChainLimitOrders);
|
|
1104
|
+
* ```
|
|
1105
|
+
*
|
|
1106
|
+
* @param params - Wallet addresses to fetch orders for (EVM, Solana).
|
|
1107
|
+
* @returns A structured {@link ApiUserOrders} object containing all user orders.
|
|
1108
|
+
*/
|
|
1109
|
+
async getOrders(params) {
|
|
1110
|
+
return getOrders(params);
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
// src/react/SwapProvider.tsx
|
|
1115
|
+
import { jsx } from "react/jsx-runtime";
|
|
1116
|
+
var SwapContext = createContext(null);
|
|
1117
|
+
var SwapProvider = ({ config, children }) => {
|
|
1118
|
+
const sdk = useMemo(() => new SwapSDK(config), [config.apiKey]);
|
|
1119
|
+
return /* @__PURE__ */ jsx(SwapContext.Provider, { value: sdk, children });
|
|
1120
|
+
};
|
|
1121
|
+
var useSwap = () => {
|
|
1122
|
+
const ctx = useContext(SwapContext);
|
|
1123
|
+
if (!ctx) {
|
|
1124
|
+
throw new Error("useSwap must be used within <SwapProvider>");
|
|
1125
|
+
}
|
|
1126
|
+
return ctx;
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
// src/react/useQuote.ts
|
|
1130
|
+
import { useCallback, useEffect, useMemo as useMemo2, useRef } from "react";
|
|
1131
|
+
import useSWR, { mutate } from "swr";
|
|
1132
|
+
var QUOTE_KEY = "quote-global";
|
|
1133
|
+
function isValidQuoteParams(p) {
|
|
1134
|
+
if (!p) return false;
|
|
1135
|
+
const { tokenIn, tokenOut, amount } = p;
|
|
1136
|
+
if (!tokenIn || !tokenOut || !amount) return false;
|
|
1137
|
+
const n = Number(amount);
|
|
1138
|
+
return Number.isFinite(n) && n > 0;
|
|
1139
|
+
}
|
|
1140
|
+
function useQuote(initialParams) {
|
|
1141
|
+
const sdk = useSwap();
|
|
1142
|
+
const abortRef = useRef(null);
|
|
1143
|
+
const paramsRef = globalThis.__QUOTE_PARAMS_REF__ ?? (globalThis.__QUOTE_PARAMS_REF__ = { current: initialParams ?? null });
|
|
1144
|
+
useEffect(() => {
|
|
1145
|
+
if (initialParams && JSON.stringify(paramsRef.current) !== JSON.stringify(initialParams)) {
|
|
1146
|
+
paramsRef.current = initialParams;
|
|
1147
|
+
void mutate(QUOTE_KEY);
|
|
1148
|
+
}
|
|
1149
|
+
}, [initialParams]);
|
|
1150
|
+
const fetcher = useCallback(async () => {
|
|
1151
|
+
const activeParams = paramsRef.current;
|
|
1152
|
+
if (!isValidQuoteParams(activeParams)) return null;
|
|
1153
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1154
|
+
const controller = new AbortController();
|
|
1155
|
+
abortRef.current = controller;
|
|
1156
|
+
try {
|
|
1157
|
+
return await sdk.getQuote(activeParams);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1160
|
+
if (err instanceof Error) throw err;
|
|
1161
|
+
throw new Error(String(err));
|
|
1162
|
+
}
|
|
1163
|
+
}, [sdk]);
|
|
1164
|
+
const {
|
|
1165
|
+
data,
|
|
1166
|
+
error,
|
|
1167
|
+
isValidating: loading,
|
|
1168
|
+
mutate: mutateQuote
|
|
1169
|
+
} = useSWR(QUOTE_KEY, fetcher, {
|
|
1170
|
+
revalidateOnFocus: false,
|
|
1171
|
+
shouldRetryOnError: false,
|
|
1172
|
+
keepPreviousData: true
|
|
1173
|
+
});
|
|
1174
|
+
const refetch = useCallback(async () => {
|
|
1175
|
+
await mutateQuote();
|
|
1176
|
+
}, [mutateQuote]);
|
|
1177
|
+
const setParams = useCallback(
|
|
1178
|
+
(next) => {
|
|
1179
|
+
paramsRef.current = next;
|
|
1180
|
+
void mutate(QUOTE_KEY);
|
|
1181
|
+
},
|
|
1182
|
+
[]
|
|
1183
|
+
);
|
|
1184
|
+
return useMemo2(
|
|
1185
|
+
() => ({
|
|
1186
|
+
data,
|
|
1187
|
+
loading,
|
|
1188
|
+
error: error instanceof Error ? error.message : null,
|
|
1189
|
+
refetch,
|
|
1190
|
+
setParams,
|
|
1191
|
+
get activeParams() {
|
|
1192
|
+
return paramsRef.current;
|
|
1193
|
+
}
|
|
1194
|
+
}),
|
|
1195
|
+
[data, loading, error, refetch, setParams]
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/react/useExecuteTransaction.ts
|
|
1200
|
+
import { useState, useCallback as useCallback3 } from "react";
|
|
1201
|
+
|
|
1202
|
+
// src/react/useBalances.ts
|
|
1203
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
1204
|
+
import useSWR2, { mutate as mutate2 } from "swr";
|
|
1205
|
+
var BALANCE_KEY = "balances-global";
|
|
1206
|
+
function useBalances(initialParams) {
|
|
1207
|
+
const sdk = useSwap();
|
|
1208
|
+
const abortRef = useRef2(null);
|
|
1209
|
+
const paramsRef = globalThis.__BALANCES_PARAMS_REF__ ?? (globalThis.__BALANCES_PARAMS_REF__ = { current: initialParams ?? null });
|
|
1210
|
+
const fetcher = useCallback2(async () => {
|
|
1211
|
+
const activeParams = paramsRef.current;
|
|
1212
|
+
if (!activeParams) return null;
|
|
1213
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1214
|
+
const controller = new AbortController();
|
|
1215
|
+
abortRef.current = controller;
|
|
1216
|
+
try {
|
|
1217
|
+
return await sdk.getBalances(activeParams, { signal: controller.signal });
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1220
|
+
if (err instanceof Error) throw err;
|
|
1221
|
+
throw new Error(String(err));
|
|
1222
|
+
}
|
|
1223
|
+
}, [sdk, paramsRef]);
|
|
1224
|
+
const {
|
|
1225
|
+
data,
|
|
1226
|
+
error,
|
|
1227
|
+
isValidating: loading,
|
|
1228
|
+
mutate: mutateBalances
|
|
1229
|
+
} = useSWR2(BALANCE_KEY, fetcher, {
|
|
1230
|
+
revalidateOnFocus: false,
|
|
1231
|
+
shouldRetryOnError: false,
|
|
1232
|
+
keepPreviousData: true
|
|
1233
|
+
});
|
|
1234
|
+
const refetch = useCallback2(async () => {
|
|
1235
|
+
await mutateBalances();
|
|
1236
|
+
}, [mutateBalances]);
|
|
1237
|
+
const setParams = useCallback2((next) => {
|
|
1238
|
+
paramsRef.current = next;
|
|
1239
|
+
void mutate2(BALANCE_KEY);
|
|
1240
|
+
}, [paramsRef]);
|
|
752
1241
|
useEffect2(() => {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1242
|
+
if (initialParams) {
|
|
1243
|
+
const prevParams = paramsRef.current;
|
|
1244
|
+
const paramsChanged = !prevParams || prevParams.addresses.svm !== initialParams.addresses.svm || prevParams.addresses.evm !== initialParams.addresses.evm;
|
|
1245
|
+
if (paramsChanged) {
|
|
1246
|
+
paramsRef.current = initialParams;
|
|
1247
|
+
void mutate2(BALANCE_KEY);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}, [initialParams, paramsRef]);
|
|
1251
|
+
return useMemo3(
|
|
1252
|
+
() => ({
|
|
1253
|
+
data,
|
|
1254
|
+
loading,
|
|
1255
|
+
error: error instanceof Error ? error : null,
|
|
1256
|
+
refetch,
|
|
1257
|
+
setParams,
|
|
1258
|
+
get activeParams() {
|
|
1259
|
+
return paramsRef.current;
|
|
1260
|
+
}
|
|
1261
|
+
}),
|
|
1262
|
+
[data, loading, error, refetch, setParams, paramsRef]
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// src/react/useExecuteTransaction.ts
|
|
1267
|
+
function useExecuteTransaction() {
|
|
1268
|
+
const sdk = useSwap();
|
|
1269
|
+
const [isLoading, setLoading] = useState(false);
|
|
1270
|
+
const [stage, setStage] = useState(null);
|
|
1271
|
+
const [message, setMessage] = useState(null);
|
|
1272
|
+
const [error, setError] = useState(null);
|
|
1273
|
+
const [result, setResult] = useState(null);
|
|
1274
|
+
const { refetch: refetchQuote } = useQuote();
|
|
1275
|
+
const { refetch: refetchBalances } = useBalances();
|
|
1276
|
+
const execute = useCallback3(
|
|
758
1277
|
async ({
|
|
759
1278
|
quote,
|
|
760
1279
|
accountAddress,
|
|
761
1280
|
recipientAddress,
|
|
762
1281
|
wallet,
|
|
763
|
-
|
|
1282
|
+
orderType,
|
|
1283
|
+
options
|
|
764
1284
|
}) => {
|
|
765
|
-
if (!quote || !wallet) {
|
|
766
|
-
throw new Error("Quote and wallet are required for order execution.");
|
|
767
|
-
}
|
|
768
1285
|
setLoading(true);
|
|
769
1286
|
setError(null);
|
|
770
|
-
|
|
771
|
-
|
|
1287
|
+
setResult(null);
|
|
1288
|
+
const onStatus = (s, msg) => {
|
|
1289
|
+
setStage(s);
|
|
1290
|
+
setMessage(msg ?? "");
|
|
1291
|
+
};
|
|
772
1292
|
try {
|
|
773
|
-
const
|
|
774
|
-
const onStatus = (stage, msg) => {
|
|
775
|
-
if (!isMounted.current) return;
|
|
776
|
-
setStatus(stage);
|
|
777
|
-
if (msg) setMessage(msg);
|
|
778
|
-
};
|
|
779
|
-
const result = await executeOrder({
|
|
1293
|
+
const res = await sdk.executeTransaction({
|
|
780
1294
|
quote,
|
|
1295
|
+
wallet,
|
|
781
1296
|
accountAddress,
|
|
782
1297
|
recipientAddress,
|
|
783
|
-
wallet,
|
|
784
1298
|
onStatus,
|
|
785
|
-
|
|
1299
|
+
orderType,
|
|
1300
|
+
options
|
|
786
1301
|
});
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
return result;
|
|
792
|
-
} catch (err) {
|
|
793
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
794
|
-
if (isMounted.current) {
|
|
795
|
-
setError(errorObj);
|
|
796
|
-
setStatus("error");
|
|
797
|
-
setMessage(errorObj.message);
|
|
798
|
-
setData({
|
|
799
|
-
status: false,
|
|
800
|
-
stage: "error",
|
|
801
|
-
message: errorObj.message
|
|
1302
|
+
if (res && typeof res === "object" && "status" in res && "stage" in res && typeof res.stage === "string") {
|
|
1303
|
+
setResult({
|
|
1304
|
+
...res,
|
|
1305
|
+
stage: res.stage
|
|
802
1306
|
});
|
|
1307
|
+
} else {
|
|
1308
|
+
setResult(res);
|
|
803
1309
|
}
|
|
804
|
-
return
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1310
|
+
return res;
|
|
1311
|
+
} catch (err) {
|
|
1312
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1313
|
+
setError(msg);
|
|
1314
|
+
throw err;
|
|
809
1315
|
} finally {
|
|
810
|
-
|
|
1316
|
+
await Promise.allSettled([refetchQuote(), refetchBalances()]);
|
|
1317
|
+
setLoading(false);
|
|
811
1318
|
}
|
|
812
1319
|
},
|
|
813
|
-
[]
|
|
1320
|
+
[sdk, refetchQuote, refetchBalances]
|
|
814
1321
|
);
|
|
815
1322
|
return {
|
|
816
|
-
/** Executes the swap order. */
|
|
817
1323
|
execute,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
/** Human-readable status message. */
|
|
1324
|
+
isLoading,
|
|
1325
|
+
stage,
|
|
821
1326
|
message,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
/** Raw SDK response data. */
|
|
825
|
-
data,
|
|
826
|
-
/** Captured error (if execution failed). */
|
|
827
|
-
error
|
|
1327
|
+
error,
|
|
1328
|
+
result
|
|
828
1329
|
};
|
|
829
1330
|
}
|
|
830
1331
|
|
|
831
|
-
// src/react/
|
|
832
|
-
import {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const
|
|
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
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1332
|
+
// src/react/useTokenList.ts
|
|
1333
|
+
import { useRef as useRef3, useState as useState2, useCallback as useCallback4 } from "react";
|
|
1334
|
+
function useTokenList() {
|
|
1335
|
+
const [tokens, setTokens] = useState2([]);
|
|
1336
|
+
const [loading, setLoading] = useState2(false);
|
|
1337
|
+
const [error, setError] = useState2(null);
|
|
1338
|
+
const [hasMore, setHasMore] = useState2(true);
|
|
1339
|
+
const [page, setPage] = useState2(1);
|
|
1340
|
+
const sdk = useSwap();
|
|
1341
|
+
const pageRef = useRef3(1);
|
|
1342
|
+
const hasMoreRef = useRef3(true);
|
|
1343
|
+
const lastQuery = useRef3({});
|
|
1344
|
+
const cache = useRef3(/* @__PURE__ */ new Map());
|
|
1345
|
+
const isLoadingRef = useRef3(false);
|
|
1346
|
+
pageRef.current = page;
|
|
1347
|
+
hasMoreRef.current = hasMore;
|
|
1348
|
+
const loadTokens = useCallback4(
|
|
1349
|
+
async (params) => {
|
|
1350
|
+
const { q, networkId, reset } = params;
|
|
1351
|
+
if (isLoadingRef.current && !reset) return;
|
|
1352
|
+
try {
|
|
1353
|
+
let currentPage = pageRef.current;
|
|
1354
|
+
if (reset) {
|
|
1355
|
+
currentPage = 1;
|
|
1356
|
+
setTokens([]);
|
|
1357
|
+
setPage(1);
|
|
1358
|
+
setHasMore(true);
|
|
1359
|
+
pageRef.current = 1;
|
|
1360
|
+
hasMoreRef.current = true;
|
|
1361
|
+
}
|
|
1362
|
+
if (!reset && !hasMoreRef.current) return;
|
|
1363
|
+
isLoadingRef.current = true;
|
|
1364
|
+
setLoading(true);
|
|
1365
|
+
setError(null);
|
|
1366
|
+
const cacheKey = JSON.stringify({
|
|
1367
|
+
q: q?.toLowerCase() ?? "",
|
|
1368
|
+
networkId: networkId ?? void 0,
|
|
1369
|
+
page: currentPage
|
|
1370
|
+
});
|
|
1371
|
+
if (cache.current.has(cacheKey)) {
|
|
1372
|
+
const cached = cache.current.get(cacheKey);
|
|
1373
|
+
setTokens((prev) => reset ? cached.results : [...prev, ...cached.results]);
|
|
1374
|
+
if (!reset && cached.results.length > 0) {
|
|
1375
|
+
const nextPage = currentPage + 1;
|
|
1376
|
+
setPage(nextPage);
|
|
1377
|
+
pageRef.current = nextPage;
|
|
1378
|
+
}
|
|
1379
|
+
isLoadingRef.current = false;
|
|
1380
|
+
setLoading(false);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const apiParams = {
|
|
1384
|
+
q,
|
|
1385
|
+
page: currentPage,
|
|
1386
|
+
limit: 20,
|
|
1387
|
+
...networkId !== void 0 ? { networkId } : {}
|
|
1388
|
+
};
|
|
1389
|
+
const res = await sdk.getTokenList(apiParams);
|
|
1390
|
+
cache.current.set(cacheKey, res);
|
|
1391
|
+
setTokens((prev) => reset ? res.results : [...prev, ...res.results]);
|
|
1392
|
+
const isLastPage = res.results.length === 0 || res.results.length < 20 || res.count && currentPage * 20 >= res.count;
|
|
1393
|
+
setHasMore(!isLastPage);
|
|
1394
|
+
hasMoreRef.current = !isLastPage;
|
|
1395
|
+
if (!reset && !isLastPage) {
|
|
1396
|
+
const nextPage = currentPage + 1;
|
|
1397
|
+
setPage(nextPage);
|
|
1398
|
+
pageRef.current = nextPage;
|
|
1399
|
+
}
|
|
1400
|
+
lastQuery.current = { q, networkId };
|
|
1401
|
+
} catch (e) {
|
|
1402
|
+
console.error("useTokenList error:", e);
|
|
1403
|
+
setError("Failed to load tokens");
|
|
1404
|
+
} finally {
|
|
1405
|
+
setLoading(false);
|
|
1406
|
+
isLoadingRef.current = false;
|
|
1407
|
+
}
|
|
891
1408
|
},
|
|
892
|
-
|
|
1409
|
+
[sdk]
|
|
1410
|
+
);
|
|
1411
|
+
const resetTokens = useCallback4(() => {
|
|
1412
|
+
setTokens([]);
|
|
1413
|
+
setPage(1);
|
|
1414
|
+
setHasMore(true);
|
|
1415
|
+
pageRef.current = 1;
|
|
1416
|
+
hasMoreRef.current = true;
|
|
1417
|
+
cache.current.clear();
|
|
1418
|
+
lastQuery.current = {};
|
|
1419
|
+
}, []);
|
|
1420
|
+
return {
|
|
1421
|
+
tokens,
|
|
1422
|
+
loading,
|
|
1423
|
+
error,
|
|
1424
|
+
hasMore,
|
|
1425
|
+
page,
|
|
1426
|
+
lastQuery: lastQuery.current,
|
|
1427
|
+
loadTokens,
|
|
1428
|
+
resetTokens
|
|
893
1429
|
};
|
|
894
1430
|
}
|
|
895
1431
|
|
|
896
|
-
// src/react/
|
|
897
|
-
|
|
898
|
-
|
|
1432
|
+
// src/react/useTokensData.ts
|
|
1433
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1434
|
+
function useTokensData(addresses) {
|
|
1435
|
+
const sdk = useSwap();
|
|
1436
|
+
const [data, setData] = useState3([]);
|
|
899
1437
|
const [loading, setLoading] = useState3(false);
|
|
900
1438
|
const [error, setError] = useState3(null);
|
|
901
|
-
const [warning, setWarning] = useState3(null);
|
|
902
|
-
const debounceMs = options?.debounceMs ?? 250;
|
|
903
|
-
const autoRefreshMs = options?.autoRefreshMs;
|
|
904
|
-
const abortRef = useRef3(null);
|
|
905
|
-
const debounceRef = useRef3(null);
|
|
906
|
-
const mounted = useRef3(false);
|
|
907
|
-
const paramsKey = useMemo2(
|
|
908
|
-
() => params ? JSON.stringify(serializeBigIntsToStrings(params)) : null,
|
|
909
|
-
[params]
|
|
910
|
-
);
|
|
911
|
-
useEffect3(() => {
|
|
912
|
-
mounted.current = true;
|
|
913
|
-
return () => {
|
|
914
|
-
mounted.current = false;
|
|
915
|
-
abortRef.current?.abort();
|
|
916
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
917
|
-
};
|
|
918
|
-
}, []);
|
|
919
|
-
const fetchQuote = useCallback2(async () => {
|
|
920
|
-
if (!params) return;
|
|
921
|
-
try {
|
|
922
|
-
setLoading(true);
|
|
923
|
-
setWarning(null);
|
|
924
|
-
const result = await getQuote(params);
|
|
925
|
-
const serializeResult = serializeBigIntsToStrings(result);
|
|
926
|
-
if (!mounted.current) return;
|
|
927
|
-
setData((prev) => {
|
|
928
|
-
if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
|
|
929
|
-
return serializeResult;
|
|
930
|
-
});
|
|
931
|
-
setWarning(result.warning ?? null);
|
|
932
|
-
setError(null);
|
|
933
|
-
} catch (err) {
|
|
934
|
-
if (err.name === "AbortError") return;
|
|
935
|
-
console.error("[useQuote] fetch error:", err);
|
|
936
|
-
if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
937
|
-
} finally {
|
|
938
|
-
if (mounted.current) setLoading(false);
|
|
939
|
-
}
|
|
940
|
-
}, [paramsKey]);
|
|
941
1439
|
useEffect3(() => {
|
|
942
|
-
if (!
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1440
|
+
if (!addresses?.length) return;
|
|
1441
|
+
let isMounted = true;
|
|
1442
|
+
setLoading(true);
|
|
1443
|
+
setError(null);
|
|
1444
|
+
sdk.getTokensData(addresses).then((res) => {
|
|
1445
|
+
if (isMounted) setData(res);
|
|
1446
|
+
}).catch((e) => {
|
|
1447
|
+
if (isMounted) setError(e instanceof Error ? e : new Error(String(e)));
|
|
1448
|
+
}).finally(() => {
|
|
1449
|
+
if (isMounted) setLoading(false);
|
|
1450
|
+
});
|
|
947
1451
|
return () => {
|
|
948
|
-
|
|
949
|
-
abortRef.current?.abort();
|
|
1452
|
+
isMounted = false;
|
|
950
1453
|
};
|
|
951
|
-
}, [
|
|
952
|
-
|
|
953
|
-
if (!autoRefreshMs || !paramsKey) return;
|
|
954
|
-
const interval = setInterval(() => fetchQuote(), autoRefreshMs);
|
|
955
|
-
return () => clearInterval(interval);
|
|
956
|
-
}, [autoRefreshMs, paramsKey, fetchQuote]);
|
|
957
|
-
return useMemo2(
|
|
958
|
-
() => ({
|
|
959
|
-
data,
|
|
960
|
-
loading,
|
|
961
|
-
error,
|
|
962
|
-
warning,
|
|
963
|
-
refetch: fetchQuote
|
|
964
|
-
}),
|
|
965
|
-
[data, loading, error, warning, fetchQuote]
|
|
966
|
-
);
|
|
1454
|
+
}, [sdk, JSON.stringify(addresses)]);
|
|
1455
|
+
return { data, loading, error };
|
|
967
1456
|
}
|
|
968
1457
|
|
|
969
|
-
// src/react/
|
|
970
|
-
import { useCallback as
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const { signal } = options ?? {};
|
|
977
|
-
if (!addresses?.evm && !addresses?.svm) {
|
|
978
|
-
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
979
|
-
}
|
|
980
|
-
const payload = JSON.stringify({
|
|
981
|
-
addresses,
|
|
982
|
-
cursorEvm,
|
|
983
|
-
cursorSvm
|
|
984
|
-
});
|
|
985
|
-
const start = performance.now();
|
|
986
|
-
const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
987
|
-
method: "POST",
|
|
988
|
-
headers: {
|
|
989
|
-
accept: "application/json",
|
|
990
|
-
"Content-Type": "application/json"
|
|
991
|
-
},
|
|
992
|
-
body: payload,
|
|
993
|
-
signal
|
|
994
|
-
}).catch((err) => {
|
|
995
|
-
if (err.name === "AbortError") {
|
|
996
|
-
throw new Error("Balance request was cancelled.");
|
|
997
|
-
}
|
|
998
|
-
throw err;
|
|
999
|
-
});
|
|
1000
|
-
if (!response.ok) {
|
|
1001
|
-
const text = await response.text().catch(() => "");
|
|
1002
|
-
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
1003
|
-
}
|
|
1004
|
-
const data = await response.json().catch(() => {
|
|
1005
|
-
throw new Error("Invalid JSON response from balances API.");
|
|
1006
|
-
});
|
|
1007
|
-
const duration = (performance.now() - start).toFixed(1);
|
|
1008
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1009
|
-
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
1010
|
-
}
|
|
1011
|
-
const evmItems = data.evm?.items ?? [];
|
|
1012
|
-
const svmItems = data.svm?.items ?? [];
|
|
1013
|
-
const combined = [...evmItems, ...svmItems];
|
|
1014
|
-
return {
|
|
1015
|
-
results: combined,
|
|
1016
|
-
nextCursorEvm: data.evm?.cursor ?? null,
|
|
1017
|
-
nextCursorSvm: data.svm?.cursor ?? null
|
|
1018
|
-
};
|
|
1458
|
+
// src/react/useOrders.ts
|
|
1459
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useMemo as useMemo4, useRef as useRef4 } from "react";
|
|
1460
|
+
import useSWR3, { mutate as mutate3 } from "swr";
|
|
1461
|
+
var ORDERS_KEY = "orders-global";
|
|
1462
|
+
function hasValidAddress(addrs) {
|
|
1463
|
+
if (!addrs) return false;
|
|
1464
|
+
return Boolean(addrs.evmAddress || addrs.solAddress || addrs.suiAddress);
|
|
1019
1465
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
function useBalances(params) {
|
|
1023
|
-
const [data, setData] = useState4(null);
|
|
1024
|
-
const [loading, setLoading] = useState4(false);
|
|
1025
|
-
const [error, setError] = useState4(null);
|
|
1466
|
+
function useOrders(initialAddresses) {
|
|
1467
|
+
const sdk = useSwap();
|
|
1026
1468
|
const abortRef = useRef4(null);
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
};
|
|
1038
|
-
}, [params?.addresses?.evm, params?.addresses?.svm, params?.cursorEvm, params?.cursorSvm]);
|
|
1039
|
-
const fetchBalances = useCallback3(async () => {
|
|
1040
|
-
if (!stableParams) return;
|
|
1469
|
+
const addrRef = globalThis.__ORDERS_ADDR_REF__ ?? (globalThis.__ORDERS_ADDR_REF__ = { current: initialAddresses ?? null });
|
|
1470
|
+
useEffect4(() => {
|
|
1471
|
+
if (initialAddresses && JSON.stringify(addrRef.current) !== JSON.stringify(initialAddresses)) {
|
|
1472
|
+
addrRef.current = initialAddresses;
|
|
1473
|
+
void mutate3(ORDERS_KEY);
|
|
1474
|
+
}
|
|
1475
|
+
}, [initialAddresses]);
|
|
1476
|
+
const fetcher = useCallback5(async () => {
|
|
1477
|
+
const active = addrRef.current;
|
|
1478
|
+
if (!hasValidAddress(active)) return null;
|
|
1041
1479
|
if (abortRef.current) abortRef.current.abort();
|
|
1042
1480
|
const controller = new AbortController();
|
|
1043
1481
|
abortRef.current = controller;
|
|
1044
|
-
setLoading(true);
|
|
1045
|
-
setError(null);
|
|
1046
1482
|
try {
|
|
1047
|
-
|
|
1048
|
-
setData((prev) => {
|
|
1049
|
-
if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
|
|
1050
|
-
return result;
|
|
1051
|
-
});
|
|
1052
|
-
return result;
|
|
1483
|
+
return await sdk.getOrders(active ?? {});
|
|
1053
1484
|
} catch (err) {
|
|
1054
|
-
if (err.name === "AbortError") return;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
throw e;
|
|
1058
|
-
} finally {
|
|
1059
|
-
setLoading(false);
|
|
1485
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1486
|
+
if (err instanceof Error) throw err;
|
|
1487
|
+
throw new Error(String(err));
|
|
1060
1488
|
}
|
|
1061
|
-
}, [
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1489
|
+
}, [sdk]);
|
|
1490
|
+
const {
|
|
1491
|
+
data,
|
|
1492
|
+
error,
|
|
1493
|
+
isValidating: loading,
|
|
1494
|
+
mutate: mutateOrders
|
|
1495
|
+
} = useSWR3(ORDERS_KEY, fetcher, {
|
|
1496
|
+
revalidateOnFocus: false,
|
|
1497
|
+
revalidateOnReconnect: false,
|
|
1498
|
+
shouldRetryOnError: false,
|
|
1499
|
+
keepPreviousData: true
|
|
1500
|
+
});
|
|
1501
|
+
const refetch = useCallback5(async () => {
|
|
1502
|
+
await mutateOrders();
|
|
1503
|
+
}, [mutateOrders]);
|
|
1504
|
+
const setAddresses = useCallback5(
|
|
1505
|
+
(next) => {
|
|
1506
|
+
if (!next) return;
|
|
1507
|
+
addrRef.current = next;
|
|
1508
|
+
void mutate3(ORDERS_KEY);
|
|
1509
|
+
},
|
|
1510
|
+
[]
|
|
1511
|
+
);
|
|
1512
|
+
return useMemo4(
|
|
1070
1513
|
() => ({
|
|
1071
|
-
/** Latest fetched balance data */
|
|
1072
1514
|
data,
|
|
1073
|
-
/** Whether the hook is currently fetching */
|
|
1074
1515
|
loading,
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1516
|
+
error: error instanceof Error ? error.message : null,
|
|
1517
|
+
refetch,
|
|
1518
|
+
setAddresses,
|
|
1519
|
+
get activeAddresses() {
|
|
1520
|
+
return addrRef.current;
|
|
1521
|
+
}
|
|
1079
1522
|
}),
|
|
1080
|
-
[data, loading, error,
|
|
1523
|
+
[data, loading, error, refetch, setAddresses]
|
|
1081
1524
|
);
|
|
1082
1525
|
}
|
|
1083
|
-
|
|
1084
|
-
// src/react/index.ts
|
|
1085
|
-
import { ChainID as ChainID4, isEvmChain as isEvmChain3 } from "@shogun-sdk/intents-sdk";
|
|
1086
1526
|
export {
|
|
1087
|
-
|
|
1088
|
-
|
|
1527
|
+
ChainId,
|
|
1528
|
+
OrderExecutionType,
|
|
1529
|
+
SupportedChains,
|
|
1530
|
+
SwapProvider,
|
|
1531
|
+
buildQuoteParams,
|
|
1532
|
+
isEvmChain,
|
|
1089
1533
|
useBalances,
|
|
1090
|
-
|
|
1534
|
+
useExecuteTransaction,
|
|
1535
|
+
useOrders,
|
|
1091
1536
|
useQuote,
|
|
1092
|
-
|
|
1537
|
+
useSwap,
|
|
1538
|
+
useTokenList,
|
|
1539
|
+
useTokensData
|
|
1093
1540
|
};
|