@shogun-sdk/swap 0.0.2-test.24 → 0.0.2-test.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +379 -112
- package/dist/core.d.cts +4 -136
- package/dist/core.d.ts +4 -136
- package/dist/core.js +353 -84
- package/dist/index-Czt8I0Vg.d.ts +351 -0
- package/dist/index-D62OxLwp.d.cts +351 -0
- package/dist/index.cjs +373 -500
- package/dist/index.d.cts +4 -9
- package/dist/index.d.ts +4 -9
- package/dist/index.js +347 -474
- package/dist/react.cjs +829 -476
- package/dist/react.d.cts +192 -82
- package/dist/react.d.ts +192 -82
- package/dist/react.js +799 -454
- package/dist/{wallet-BhuMJ3K_.d.cts → wallet-B9bKceyN.d.cts} +1 -2
- package/dist/{wallet-BhuMJ3K_.d.ts → wallet-B9bKceyN.d.ts} +1 -2
- package/dist/wallet-adapter.cjs +25607 -11
- package/dist/wallet-adapter.d.cts +1 -2
- package/dist/wallet-adapter.d.ts +1 -2
- package/dist/wallet-adapter.js +25626 -6
- package/package.json +44 -14
- package/dist/execute-D2qcOzkI.d.ts +0 -145
- package/dist/execute-Xvw4wXBo.d.cts +0 -145
package/dist/react.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,107 +18,47 @@ 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_viem4 = 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
|
|
61
|
+
var import_viem = require("viem");
|
|
119
62
|
var NATIVE_TOKEN = {
|
|
120
63
|
ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
|
121
64
|
SOL: "So11111111111111111111111111111111111111111",
|
|
@@ -126,13 +69,31 @@ var isNativeAddress = (tokenAddress) => {
|
|
|
126
69
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
|
127
70
|
return !!tokenAddress && NATIVE_ADDRESSES.includes(normalizedTokenAddress);
|
|
128
71
|
};
|
|
72
|
+
function normalizeEvmTokenAddress(address) {
|
|
73
|
+
const lower = address.toLowerCase();
|
|
74
|
+
return lower === NATIVE_TOKEN.ETH.toLowerCase() ? import_viem.zeroAddress : address;
|
|
75
|
+
}
|
|
129
76
|
|
|
130
77
|
// src/utils/chain.ts
|
|
131
|
-
var
|
|
132
|
-
var SOLANA_CHAIN_ID =
|
|
133
|
-
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 = [
|
|
134
95
|
{
|
|
135
|
-
id:
|
|
96
|
+
id: import_intents_sdk.ChainID.Arbitrum,
|
|
136
97
|
name: "Arbitrum",
|
|
137
98
|
isEVM: true,
|
|
138
99
|
wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -141,7 +102,7 @@ var SupportedChains = [
|
|
|
141
102
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
142
103
|
},
|
|
143
104
|
{
|
|
144
|
-
id:
|
|
105
|
+
id: import_intents_sdk.ChainID.Optimism,
|
|
145
106
|
name: "Optimism",
|
|
146
107
|
isEVM: true,
|
|
147
108
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -150,7 +111,7 @@ var SupportedChains = [
|
|
|
150
111
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
151
112
|
},
|
|
152
113
|
{
|
|
153
|
-
id:
|
|
114
|
+
id: import_intents_sdk.ChainID.Base,
|
|
154
115
|
name: "Base",
|
|
155
116
|
isEVM: true,
|
|
156
117
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -159,7 +120,7 @@ var SupportedChains = [
|
|
|
159
120
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
160
121
|
},
|
|
161
122
|
{
|
|
162
|
-
id:
|
|
123
|
+
id: import_intents_sdk.ChainID.Hyperliquid,
|
|
163
124
|
name: "Hyperliquid",
|
|
164
125
|
isEVM: true,
|
|
165
126
|
wrapped: "0x5555555555555555555555555555555555555555",
|
|
@@ -168,7 +129,7 @@ var SupportedChains = [
|
|
|
168
129
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
169
130
|
},
|
|
170
131
|
{
|
|
171
|
-
id:
|
|
132
|
+
id: import_intents_sdk.ChainID.BSC,
|
|
172
133
|
name: "BSC",
|
|
173
134
|
isEVM: true,
|
|
174
135
|
wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
@@ -186,6 +147,10 @@ var SupportedChains = [
|
|
|
186
147
|
tokenAddress: NATIVE_TOKEN.SOL
|
|
187
148
|
}
|
|
188
149
|
];
|
|
150
|
+
var SupportedChains = SupportedChainsInternal.filter(
|
|
151
|
+
(c) => CURRENT_SUPPORTED.includes(c.id)
|
|
152
|
+
);
|
|
153
|
+
var isEvmChain = import_intents_sdk.isEvmChain;
|
|
189
154
|
|
|
190
155
|
// src/utils/viem.ts
|
|
191
156
|
function isViemWalletClient(wallet) {
|
|
@@ -213,10 +178,198 @@ function serializeBigIntsToStrings(obj) {
|
|
|
213
178
|
return obj;
|
|
214
179
|
}
|
|
215
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
|
+
|
|
216
371
|
// src/wallet-adapter/evm-wallet-adapter/adapter.ts
|
|
217
|
-
var
|
|
218
|
-
var import_utils = require("ethers/lib/utils.js");
|
|
219
|
-
var import_viem = require("viem");
|
|
372
|
+
var import_viem3 = require("viem");
|
|
220
373
|
function isEVMTransaction(tx) {
|
|
221
374
|
return typeof tx.from === "string";
|
|
222
375
|
}
|
|
@@ -282,7 +435,7 @@ var adaptViemWallet = (wallet) => {
|
|
|
282
435
|
functionName,
|
|
283
436
|
args = []
|
|
284
437
|
}) => {
|
|
285
|
-
const publicClient = wallet.extend(
|
|
438
|
+
const publicClient = wallet.extend(import_viem3.publicActions);
|
|
286
439
|
return await publicClient.readContract({
|
|
287
440
|
address: address2,
|
|
288
441
|
abi,
|
|
@@ -292,7 +445,7 @@ var adaptViemWallet = (wallet) => {
|
|
|
292
445
|
};
|
|
293
446
|
return {
|
|
294
447
|
vmType: "EVM" /* EVM */,
|
|
295
|
-
transport: (0,
|
|
448
|
+
transport: (0, import_viem3.custom)(wallet.transport),
|
|
296
449
|
getChainId: async () => wallet.getChainId(),
|
|
297
450
|
address,
|
|
298
451
|
sendTransaction,
|
|
@@ -302,23 +455,11 @@ var adaptViemWallet = (wallet) => {
|
|
|
302
455
|
};
|
|
303
456
|
};
|
|
304
457
|
|
|
305
|
-
// src/core/
|
|
306
|
-
var
|
|
307
|
-
var
|
|
308
|
-
|
|
309
|
-
// src/core/executeOrder/normalizeNative.ts
|
|
310
|
-
var import_intents_sdk3 = require("@shogun-sdk/intents-sdk");
|
|
311
|
-
function normalizeNative(chainId, address) {
|
|
312
|
-
if ((0, import_intents_sdk3.isEvmChain)(chainId) && isNativeAddress(address)) {
|
|
313
|
-
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
314
|
-
if (!chain?.wrapped)
|
|
315
|
-
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
316
|
-
return chain.wrapped;
|
|
317
|
-
}
|
|
318
|
-
return address;
|
|
319
|
-
}
|
|
458
|
+
// src/core/execute/handleEvmExecution.ts
|
|
459
|
+
var import_intents_sdk10 = require("@shogun-sdk/intents-sdk");
|
|
460
|
+
var import_viem6 = require("viem");
|
|
320
461
|
|
|
321
|
-
// src/core/
|
|
462
|
+
// src/core/execute/stageMessages.ts
|
|
322
463
|
var DEFAULT_STAGE_MESSAGES = {
|
|
323
464
|
processing: "Preparing transaction for execution",
|
|
324
465
|
approving: "Approving token allowance",
|
|
@@ -327,33 +468,57 @@ var DEFAULT_STAGE_MESSAGES = {
|
|
|
327
468
|
submitting: "Submitting transaction",
|
|
328
469
|
initiated: "Transaction initiated.",
|
|
329
470
|
success: "Transaction Executed successfully",
|
|
471
|
+
success_limit: "Limit order has been submitted successfully.",
|
|
330
472
|
shogun_processing: "Shogun is processing your transaction",
|
|
331
473
|
error: "Transaction failed during submission"
|
|
332
474
|
};
|
|
333
475
|
|
|
334
|
-
// src/core/
|
|
335
|
-
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
|
|
336
488
|
async function buildOrder({
|
|
337
489
|
quote,
|
|
338
490
|
accountAddress,
|
|
339
491
|
destination,
|
|
340
492
|
deadline,
|
|
341
|
-
isSingleChain
|
|
493
|
+
isSingleChain,
|
|
494
|
+
orderType,
|
|
495
|
+
options
|
|
342
496
|
}) {
|
|
343
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
|
+
}
|
|
344
509
|
if (isSingleChain) {
|
|
345
|
-
return await
|
|
510
|
+
return await import_intents_sdk7.SingleChainOrder.create({
|
|
346
511
|
user: accountAddress,
|
|
347
512
|
chainId: tokenIn.chainId,
|
|
348
513
|
tokenIn: tokenIn.address,
|
|
349
514
|
tokenOut: tokenOut.address,
|
|
350
515
|
amountIn: quote.amountIn,
|
|
351
|
-
amountOutMin
|
|
516
|
+
amountOutMin,
|
|
352
517
|
deadline,
|
|
353
518
|
destinationAddress: destination
|
|
354
519
|
});
|
|
355
520
|
}
|
|
356
|
-
return await
|
|
521
|
+
return await import_intents_sdk7.CrossChainOrder.create({
|
|
357
522
|
user: accountAddress,
|
|
358
523
|
sourceChainId: tokenIn.chainId,
|
|
359
524
|
sourceTokenAddress: tokenIn.address,
|
|
@@ -362,13 +527,13 @@ async function buildOrder({
|
|
|
362
527
|
destinationTokenAddress: tokenOut.address,
|
|
363
528
|
destinationAddress: destination,
|
|
364
529
|
deadline,
|
|
365
|
-
destinationTokenMinAmount:
|
|
530
|
+
destinationTokenMinAmount: amountOutMin,
|
|
366
531
|
minStablecoinAmount: quote.minStablecoinsAmount
|
|
367
532
|
});
|
|
368
533
|
}
|
|
369
534
|
|
|
370
535
|
// src/utils/pollOrderStatus.ts
|
|
371
|
-
var
|
|
536
|
+
var import_intents_sdk8 = require("@shogun-sdk/intents-sdk");
|
|
372
537
|
async function pollOrderStatus(address, orderId, options = {}) {
|
|
373
538
|
const { intervalMs = 2e3, timeoutMs = 3e5 } = options;
|
|
374
539
|
const startTime = Date.now();
|
|
@@ -381,7 +546,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
|
|
|
381
546
|
else if (isSuiAddress) queryParam = `suiWallets=${address}`;
|
|
382
547
|
else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
|
|
383
548
|
else throw new Error(`Unrecognized wallet address format: ${address}`);
|
|
384
|
-
const queryUrl = `${
|
|
549
|
+
const queryUrl = `${import_intents_sdk8.AUCTIONEER_URL}/user_intent?${queryParam}`;
|
|
385
550
|
return new Promise((resolve, reject) => {
|
|
386
551
|
const pollInterval = setInterval(async () => {
|
|
387
552
|
try {
|
|
@@ -433,7 +598,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
|
|
|
433
598
|
});
|
|
434
599
|
}
|
|
435
600
|
|
|
436
|
-
// src/core/
|
|
601
|
+
// src/core/execute/handleOrderPollingResult.ts
|
|
437
602
|
async function handleOrderPollingResult({
|
|
438
603
|
status,
|
|
439
604
|
orderId,
|
|
@@ -471,9 +636,9 @@ async function handleOrderPollingResult({
|
|
|
471
636
|
};
|
|
472
637
|
}
|
|
473
638
|
|
|
474
|
-
// src/core/
|
|
475
|
-
var
|
|
476
|
-
var
|
|
639
|
+
// src/core/execute/ensurePermit2Allowance.ts
|
|
640
|
+
var import_viem5 = require("viem");
|
|
641
|
+
var import_intents_sdk9 = require("@shogun-sdk/intents-sdk");
|
|
477
642
|
async function ensurePermit2Allowance({
|
|
478
643
|
chainId,
|
|
479
644
|
tokenIn,
|
|
@@ -482,7 +647,7 @@ async function ensurePermit2Allowance({
|
|
|
482
647
|
requiredAmount,
|
|
483
648
|
increaseByDelta = false
|
|
484
649
|
}) {
|
|
485
|
-
const spender =
|
|
650
|
+
const spender = import_intents_sdk9.PERMIT2_ADDRESS[chainId];
|
|
486
651
|
let currentAllowance = 0n;
|
|
487
652
|
try {
|
|
488
653
|
if (!wallet.readContract) {
|
|
@@ -490,22 +655,22 @@ async function ensurePermit2Allowance({
|
|
|
490
655
|
}
|
|
491
656
|
currentAllowance = await wallet.readContract({
|
|
492
657
|
address: tokenIn,
|
|
493
|
-
abi:
|
|
658
|
+
abi: import_viem5.erc20Abi,
|
|
494
659
|
functionName: "allowance",
|
|
495
660
|
args: [accountAddress, spender]
|
|
496
661
|
});
|
|
497
662
|
} catch (error) {
|
|
498
663
|
console.warn(`[Permit2] Failed to read allowance for ${tokenIn}`, error);
|
|
499
664
|
}
|
|
500
|
-
const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount :
|
|
665
|
+
const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : import_viem5.maxUint256;
|
|
501
666
|
console.debug(
|
|
502
667
|
`[Permit2] Approving ${approvalAmount} for ${tokenIn} (current: ${currentAllowance}, required: ${requiredAmount})`
|
|
503
668
|
);
|
|
504
669
|
await wallet.sendTransaction({
|
|
505
670
|
to: tokenIn,
|
|
506
671
|
from: accountAddress,
|
|
507
|
-
data: (0,
|
|
508
|
-
abi:
|
|
672
|
+
data: (0, import_viem5.encodeFunctionData)({
|
|
673
|
+
abi: import_viem5.erc20Abi,
|
|
509
674
|
functionName: "approve",
|
|
510
675
|
args: [spender, approvalAmount]
|
|
511
676
|
}),
|
|
@@ -516,7 +681,7 @@ async function ensurePermit2Allowance({
|
|
|
516
681
|
);
|
|
517
682
|
}
|
|
518
683
|
|
|
519
|
-
// src/core/
|
|
684
|
+
// src/core/execute/handleEvmExecution.ts
|
|
520
685
|
async function handleEvmExecution({
|
|
521
686
|
recipientAddress,
|
|
522
687
|
quote,
|
|
@@ -524,18 +689,22 @@ async function handleEvmExecution({
|
|
|
524
689
|
accountAddress,
|
|
525
690
|
wallet,
|
|
526
691
|
isSingleChain,
|
|
527
|
-
|
|
528
|
-
|
|
692
|
+
update,
|
|
693
|
+
orderType,
|
|
694
|
+
options
|
|
529
695
|
}) {
|
|
530
|
-
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;
|
|
531
698
|
await wallet.switchChain(chainId);
|
|
532
699
|
const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
|
|
700
|
+
quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
|
|
533
701
|
const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
|
|
534
702
|
update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
|
|
535
703
|
if (shouldWrapNative) {
|
|
704
|
+
quote.tokenIn.address === tokenIn;
|
|
536
705
|
await wallet.sendTransaction({
|
|
537
706
|
to: tokenIn,
|
|
538
|
-
data: (0,
|
|
707
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
539
708
|
abi: [{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] }],
|
|
540
709
|
functionName: "deposit",
|
|
541
710
|
args: []
|
|
@@ -559,11 +728,13 @@ async function handleEvmExecution({
|
|
|
559
728
|
accountAddress,
|
|
560
729
|
destination,
|
|
561
730
|
deadline,
|
|
562
|
-
isSingleChain
|
|
731
|
+
isSingleChain,
|
|
732
|
+
orderType,
|
|
733
|
+
options
|
|
563
734
|
});
|
|
564
735
|
console.debug(`order`, order);
|
|
565
736
|
update("processing", messageFor("signing"));
|
|
566
|
-
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);
|
|
567
738
|
const typedData = serializeBigIntsToStrings(orderTypedData);
|
|
568
739
|
if (!wallet.signTypedData) {
|
|
569
740
|
throw new Error("Wallet does not support EIP-712 signing");
|
|
@@ -583,18 +754,29 @@ async function handleEvmExecution({
|
|
|
583
754
|
update("initiated", messageFor("initiated"));
|
|
584
755
|
const { intentId: orderId } = res.data;
|
|
585
756
|
update("initiated", messageFor("shogun_processing"));
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
+
}
|
|
594
776
|
}
|
|
595
777
|
|
|
596
|
-
// src/core/
|
|
597
|
-
var
|
|
778
|
+
// src/core/execute/handleSolanaExecution.ts
|
|
779
|
+
var import_intents_sdk11 = require("@shogun-sdk/intents-sdk");
|
|
598
780
|
var import_web3 = require("@solana/web3.js");
|
|
599
781
|
async function handleSolanaExecution({
|
|
600
782
|
recipientAddress,
|
|
@@ -603,12 +785,14 @@ async function handleSolanaExecution({
|
|
|
603
785
|
isSingleChain,
|
|
604
786
|
update,
|
|
605
787
|
accountAddress,
|
|
606
|
-
|
|
788
|
+
orderType,
|
|
789
|
+
options
|
|
607
790
|
}) {
|
|
608
791
|
if (!wallet.rpcUrl) {
|
|
609
792
|
throw new Error("Solana wallet is missing rpcUrl");
|
|
610
793
|
}
|
|
611
|
-
const
|
|
794
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
795
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
612
796
|
update("processing", messageFor("processing"));
|
|
613
797
|
const destination = recipientAddress ?? accountAddress;
|
|
614
798
|
const order = await buildOrder({
|
|
@@ -616,7 +800,9 @@ async function handleSolanaExecution({
|
|
|
616
800
|
accountAddress,
|
|
617
801
|
destination,
|
|
618
802
|
deadline,
|
|
619
|
-
isSingleChain
|
|
803
|
+
isSingleChain,
|
|
804
|
+
orderType,
|
|
805
|
+
options
|
|
620
806
|
});
|
|
621
807
|
const txData = await getSolanaOrderInstructions({
|
|
622
808
|
order,
|
|
@@ -638,14 +824,25 @@ async function handleSolanaExecution({
|
|
|
638
824
|
update("initiated", messageFor("initiated"));
|
|
639
825
|
const { jwt, intentId: orderId } = response.data;
|
|
640
826
|
update("initiated", messageFor("shogun_processing"));
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
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
|
+
}
|
|
649
846
|
}
|
|
650
847
|
async function getSolanaOrderInstructions({
|
|
651
848
|
order,
|
|
@@ -653,11 +850,11 @@ async function getSolanaOrderInstructions({
|
|
|
653
850
|
rpcUrl
|
|
654
851
|
}) {
|
|
655
852
|
if (isSingleChain) {
|
|
656
|
-
return await (0,
|
|
853
|
+
return await (0, import_intents_sdk11.getSolanaSingleChainOrderInstructions)(order, {
|
|
657
854
|
rpcUrl
|
|
658
855
|
});
|
|
659
856
|
}
|
|
660
|
-
return await (0,
|
|
857
|
+
return await (0, import_intents_sdk11.getSolanaCrossChainOrderInstructions)(order, {
|
|
661
858
|
rpcUrl
|
|
662
859
|
});
|
|
663
860
|
}
|
|
@@ -678,13 +875,14 @@ async function submitToAuctioneer({
|
|
|
678
875
|
});
|
|
679
876
|
}
|
|
680
877
|
|
|
681
|
-
// src/core/
|
|
878
|
+
// src/core/execute/execute.ts
|
|
682
879
|
async function executeOrder({
|
|
683
880
|
quote,
|
|
684
881
|
accountAddress,
|
|
685
882
|
recipientAddress,
|
|
686
883
|
wallet,
|
|
687
884
|
onStatus,
|
|
885
|
+
orderType = "market" /* MARKET */,
|
|
688
886
|
options = {}
|
|
689
887
|
}) {
|
|
690
888
|
const isDev = process.env.NODE_ENV !== "production";
|
|
@@ -697,21 +895,31 @@ async function executeOrder({
|
|
|
697
895
|
onStatus?.(stage, message ?? messageFor(stage));
|
|
698
896
|
};
|
|
699
897
|
try {
|
|
700
|
-
const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
701
898
|
log("Starting execution:", {
|
|
702
899
|
accountAddress,
|
|
703
900
|
recipientAddress,
|
|
704
|
-
deadline,
|
|
705
901
|
tokenIn: quote?.tokenIn,
|
|
706
902
|
tokenOut: quote?.tokenOut
|
|
707
903
|
});
|
|
708
904
|
const adapter = normalizeWallet(wallet);
|
|
709
905
|
if (!adapter) throw new Error("No wallet provided");
|
|
710
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
|
+
}
|
|
711
919
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
712
920
|
const chainId = Number(tokenIn.chainId);
|
|
713
921
|
update("processing");
|
|
714
|
-
if ((0,
|
|
922
|
+
if ((0, import_intents_sdk12.isEvmChain)(chainId)) {
|
|
715
923
|
log("Detected EVM chain:", chainId);
|
|
716
924
|
const result = await handleEvmExecution({
|
|
717
925
|
recipientAddress,
|
|
@@ -720,13 +928,14 @@ async function executeOrder({
|
|
|
720
928
|
accountAddress,
|
|
721
929
|
wallet: adapter,
|
|
722
930
|
isSingleChain,
|
|
723
|
-
|
|
724
|
-
|
|
931
|
+
update,
|
|
932
|
+
orderType,
|
|
933
|
+
options
|
|
725
934
|
});
|
|
726
935
|
log("EVM execution result:", result);
|
|
727
936
|
return result;
|
|
728
937
|
}
|
|
729
|
-
if (chainId ===
|
|
938
|
+
if (chainId === import_intents_sdk12.ChainID.Solana) {
|
|
730
939
|
log("Detected Solana chain");
|
|
731
940
|
const result = await handleSolanaExecution({
|
|
732
941
|
recipientAddress,
|
|
@@ -734,8 +943,9 @@ async function executeOrder({
|
|
|
734
943
|
accountAddress,
|
|
735
944
|
wallet: adapter,
|
|
736
945
|
isSingleChain,
|
|
737
|
-
|
|
738
|
-
|
|
946
|
+
update,
|
|
947
|
+
orderType,
|
|
948
|
+
options
|
|
739
949
|
});
|
|
740
950
|
log("Solana execution result:", result);
|
|
741
951
|
return result;
|
|
@@ -745,8 +955,13 @@ async function executeOrder({
|
|
|
745
955
|
log("Error:", unsupported);
|
|
746
956
|
return { status: false, message: unsupported, stage: "error" };
|
|
747
957
|
} catch (error) {
|
|
748
|
-
|
|
749
|
-
|
|
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
|
+
}
|
|
750
965
|
update("error", message);
|
|
751
966
|
return { status: false, message, stage: "error" };
|
|
752
967
|
}
|
|
@@ -757,345 +972,483 @@ function normalizeWallet(wallet) {
|
|
|
757
972
|
return wallet;
|
|
758
973
|
}
|
|
759
974
|
|
|
760
|
-
// src/
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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 });
|
|
768
1142
|
(0, import_react2.useEffect)(() => {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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)(
|
|
774
1275
|
async ({
|
|
775
1276
|
quote,
|
|
776
1277
|
accountAddress,
|
|
777
1278
|
recipientAddress,
|
|
778
1279
|
wallet,
|
|
779
|
-
|
|
1280
|
+
orderType,
|
|
1281
|
+
options
|
|
780
1282
|
}) => {
|
|
781
|
-
if (!quote || !wallet) {
|
|
782
|
-
throw new Error("Quote and wallet are required for order execution.");
|
|
783
|
-
}
|
|
784
1283
|
setLoading(true);
|
|
785
1284
|
setError(null);
|
|
786
|
-
|
|
787
|
-
|
|
1285
|
+
setResult(null);
|
|
1286
|
+
const onStatus = (s, msg) => {
|
|
1287
|
+
setStage(s);
|
|
1288
|
+
setMessage(msg ?? "");
|
|
1289
|
+
};
|
|
788
1290
|
try {
|
|
789
|
-
const
|
|
790
|
-
const onStatus = (stage, msg) => {
|
|
791
|
-
if (!isMounted.current) return;
|
|
792
|
-
setStatus(stage);
|
|
793
|
-
if (msg) setMessage(msg);
|
|
794
|
-
};
|
|
795
|
-
const result = await executeOrder({
|
|
1291
|
+
const res = await sdk.executeTransaction({
|
|
796
1292
|
quote,
|
|
1293
|
+
wallet,
|
|
797
1294
|
accountAddress,
|
|
798
1295
|
recipientAddress,
|
|
799
|
-
wallet,
|
|
800
1296
|
onStatus,
|
|
801
|
-
|
|
1297
|
+
orderType,
|
|
1298
|
+
options
|
|
802
1299
|
});
|
|
803
|
-
if (
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
return result;
|
|
808
|
-
} catch (err) {
|
|
809
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
810
|
-
if (isMounted.current) {
|
|
811
|
-
setError(errorObj);
|
|
812
|
-
setStatus("error");
|
|
813
|
-
setMessage(errorObj.message);
|
|
814
|
-
setData({
|
|
815
|
-
status: false,
|
|
816
|
-
stage: "error",
|
|
817
|
-
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
|
|
818
1304
|
});
|
|
1305
|
+
} else {
|
|
1306
|
+
setResult(res);
|
|
819
1307
|
}
|
|
820
|
-
return
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1308
|
+
return res;
|
|
1309
|
+
} catch (err) {
|
|
1310
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1311
|
+
setError(msg);
|
|
1312
|
+
throw err;
|
|
825
1313
|
} finally {
|
|
826
|
-
|
|
1314
|
+
await Promise.allSettled([refetchQuote(), refetchBalances()]);
|
|
1315
|
+
setLoading(false);
|
|
827
1316
|
}
|
|
828
1317
|
},
|
|
829
|
-
[]
|
|
1318
|
+
[sdk, refetchQuote, refetchBalances]
|
|
830
1319
|
);
|
|
831
1320
|
return {
|
|
832
|
-
/** Executes the swap order. */
|
|
833
1321
|
execute,
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
/** Human-readable status message. */
|
|
1322
|
+
isLoading,
|
|
1323
|
+
stage,
|
|
837
1324
|
message,
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
/** Raw SDK response data. */
|
|
841
|
-
data,
|
|
842
|
-
/** Captured error (if execution failed). */
|
|
843
|
-
error
|
|
1325
|
+
error,
|
|
1326
|
+
result
|
|
844
1327
|
};
|
|
845
1328
|
}
|
|
846
1329
|
|
|
847
|
-
// src/react/
|
|
848
|
-
var
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const
|
|
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
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
+
}
|
|
907
1406
|
},
|
|
908
|
-
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
// src/react/useQuote.ts
|
|
913
|
-
function useQuote(params, options) {
|
|
914
|
-
const [data, setData] = (0, import_react3.useState)(null);
|
|
915
|
-
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
916
|
-
const [error, setError] = (0, import_react3.useState)(null);
|
|
917
|
-
const [warning, setWarning] = (0, import_react3.useState)(null);
|
|
918
|
-
const debounceMs = options?.debounceMs ?? 250;
|
|
919
|
-
const autoRefreshMs = options?.autoRefreshMs;
|
|
920
|
-
const abortRef = (0, import_react3.useRef)(null);
|
|
921
|
-
const debounceRef = (0, import_react3.useRef)(null);
|
|
922
|
-
const mounted = (0, import_react3.useRef)(false);
|
|
923
|
-
const paramsKey = (0, import_react3.useMemo)(
|
|
924
|
-
() => params ? JSON.stringify(serializeBigIntsToStrings(params)) : null,
|
|
925
|
-
[params]
|
|
1407
|
+
[sdk]
|
|
926
1408
|
);
|
|
927
|
-
(0,
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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 = {};
|
|
934
1417
|
}, []);
|
|
935
|
-
const fetchQuote = (0, import_react3.useCallback)(async () => {
|
|
936
|
-
if (!params) return;
|
|
937
|
-
try {
|
|
938
|
-
setLoading(true);
|
|
939
|
-
setWarning(null);
|
|
940
|
-
const result = await getQuote(params);
|
|
941
|
-
const serializeResult = serializeBigIntsToStrings(result);
|
|
942
|
-
if (!mounted.current) return;
|
|
943
|
-
setData((prev) => {
|
|
944
|
-
if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
|
|
945
|
-
return serializeResult;
|
|
946
|
-
});
|
|
947
|
-
setWarning(result.warning ?? null);
|
|
948
|
-
setError(null);
|
|
949
|
-
} catch (err) {
|
|
950
|
-
if (err.name === "AbortError") return;
|
|
951
|
-
console.error("[useQuote] fetch error:", err);
|
|
952
|
-
if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
953
|
-
} finally {
|
|
954
|
-
if (mounted.current) setLoading(false);
|
|
955
|
-
}
|
|
956
|
-
}, [paramsKey]);
|
|
957
|
-
(0, import_react3.useEffect)(() => {
|
|
958
|
-
if (!paramsKey) return;
|
|
959
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
960
|
-
debounceRef.current = setTimeout(() => {
|
|
961
|
-
fetchQuote();
|
|
962
|
-
}, debounceMs);
|
|
963
|
-
return () => {
|
|
964
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
965
|
-
abortRef.current?.abort();
|
|
966
|
-
};
|
|
967
|
-
}, [paramsKey, debounceMs, fetchQuote]);
|
|
968
|
-
(0, import_react3.useEffect)(() => {
|
|
969
|
-
if (!autoRefreshMs || !paramsKey) return;
|
|
970
|
-
const interval = setInterval(() => fetchQuote(), autoRefreshMs);
|
|
971
|
-
return () => clearInterval(interval);
|
|
972
|
-
}, [autoRefreshMs, paramsKey, fetchQuote]);
|
|
973
|
-
return (0, import_react3.useMemo)(
|
|
974
|
-
() => ({
|
|
975
|
-
data,
|
|
976
|
-
loading,
|
|
977
|
-
error,
|
|
978
|
-
warning,
|
|
979
|
-
refetch: fetchQuote
|
|
980
|
-
}),
|
|
981
|
-
[data, loading, error, warning, fetchQuote]
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// src/react/useBalances.ts
|
|
986
|
-
var import_react4 = require("react");
|
|
987
|
-
|
|
988
|
-
// src/core/getBalances.ts
|
|
989
|
-
var import_intents_sdk11 = require("@shogun-sdk/intents-sdk");
|
|
990
|
-
async function getBalances(params, options) {
|
|
991
|
-
const { addresses, cursorEvm, cursorSvm } = params;
|
|
992
|
-
const { signal } = options ?? {};
|
|
993
|
-
if (!addresses?.evm && !addresses?.svm) {
|
|
994
|
-
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
995
|
-
}
|
|
996
|
-
const payload = JSON.stringify({
|
|
997
|
-
addresses,
|
|
998
|
-
cursorEvm,
|
|
999
|
-
cursorSvm
|
|
1000
|
-
});
|
|
1001
|
-
const start = performance.now();
|
|
1002
|
-
const response = await fetch(`${import_intents_sdk11.TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
1003
|
-
method: "POST",
|
|
1004
|
-
headers: {
|
|
1005
|
-
accept: "application/json",
|
|
1006
|
-
"Content-Type": "application/json"
|
|
1007
|
-
},
|
|
1008
|
-
body: payload,
|
|
1009
|
-
signal
|
|
1010
|
-
}).catch((err) => {
|
|
1011
|
-
if (err.name === "AbortError") {
|
|
1012
|
-
throw new Error("Balance request was cancelled.");
|
|
1013
|
-
}
|
|
1014
|
-
throw err;
|
|
1015
|
-
});
|
|
1016
|
-
if (!response.ok) {
|
|
1017
|
-
const text = await response.text().catch(() => "");
|
|
1018
|
-
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
1019
|
-
}
|
|
1020
|
-
const data = await response.json().catch(() => {
|
|
1021
|
-
throw new Error("Invalid JSON response from balances API.");
|
|
1022
|
-
});
|
|
1023
|
-
const duration = (performance.now() - start).toFixed(1);
|
|
1024
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1025
|
-
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
1026
|
-
}
|
|
1027
|
-
const evmItems = data.evm?.items ?? [];
|
|
1028
|
-
const svmItems = data.svm?.items ?? [];
|
|
1029
|
-
const combined = [...evmItems, ...svmItems];
|
|
1030
1418
|
return {
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1419
|
+
tokens,
|
|
1420
|
+
loading,
|
|
1421
|
+
error,
|
|
1422
|
+
hasMore,
|
|
1423
|
+
page,
|
|
1424
|
+
lastQuery: lastQuery.current,
|
|
1425
|
+
loadTokens,
|
|
1426
|
+
resetTokens
|
|
1034
1427
|
};
|
|
1035
1428
|
}
|
|
1036
1429
|
|
|
1037
|
-
// src/react/
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
const
|
|
1041
|
-
const [
|
|
1042
|
-
const
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
addresses: {
|
|
1048
|
-
evm: addresses?.evm ?? void 0,
|
|
1049
|
-
svm: addresses?.svm ?? void 0
|
|
1050
|
-
},
|
|
1051
|
-
cursorEvm,
|
|
1052
|
-
cursorSvm
|
|
1053
|
-
};
|
|
1054
|
-
}, [params?.addresses?.evm, params?.addresses?.svm, params?.cursorEvm, params?.cursorSvm]);
|
|
1055
|
-
const fetchBalances = (0, import_react4.useCallback)(async () => {
|
|
1056
|
-
if (!stableParams) return;
|
|
1057
|
-
if (abortRef.current) abortRef.current.abort();
|
|
1058
|
-
const controller = new AbortController();
|
|
1059
|
-
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;
|
|
1060
1440
|
setLoading(true);
|
|
1061
1441
|
setError(null);
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return result;
|
|
1069
|
-
} catch (err) {
|
|
1070
|
-
if (err.name === "AbortError") return;
|
|
1071
|
-
const e = err instanceof Error ? err : new Error(String(err));
|
|
1072
|
-
setError(e);
|
|
1073
|
-
throw e;
|
|
1074
|
-
} finally {
|
|
1075
|
-
setLoading(false);
|
|
1076
|
-
}
|
|
1077
|
-
}, [stableParams]);
|
|
1078
|
-
(0, import_react4.useEffect)(() => {
|
|
1079
|
-
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);
|
|
1080
1448
|
});
|
|
1081
1449
|
return () => {
|
|
1082
|
-
|
|
1450
|
+
isMounted = false;
|
|
1083
1451
|
};
|
|
1084
|
-
}, [
|
|
1085
|
-
return
|
|
1086
|
-
() => ({
|
|
1087
|
-
/** Latest fetched balance data */
|
|
1088
|
-
data,
|
|
1089
|
-
/** Whether the hook is currently fetching */
|
|
1090
|
-
loading,
|
|
1091
|
-
/** Error object if fetching failed */
|
|
1092
|
-
error,
|
|
1093
|
-
/** Manually trigger a refresh */
|
|
1094
|
-
refetch: fetchBalances
|
|
1095
|
-
}),
|
|
1096
|
-
[data, loading, error, fetchBalances]
|
|
1097
|
-
);
|
|
1452
|
+
}, [sdk, JSON.stringify(addresses)]);
|
|
1453
|
+
return { data, loading, error };
|
|
1098
1454
|
}
|
|
1099
|
-
|
|
1100
|
-
// src/react/index.ts
|
|
1101
|
-
var import_intents_sdk12 = require("@shogun-sdk/intents-sdk");
|