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