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