@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/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
- ChainID: () => import_intents_sdk12.ChainID,
24
- isEvmChain: () => import_intents_sdk12.isEvmChain,
35
+ ChainId: () => ChainId,
36
+ OrderExecutionType: () => OrderExecutionType,
37
+ SupportedChains: () => SupportedChains,
38
+ SwapProvider: () => SwapProvider,
39
+ buildQuoteParams: () => buildQuoteParams,
40
+ isEvmChain: () => isEvmChain,
25
41
  useBalances: () => useBalances,
26
- useExecuteOrder: () => useExecuteOrder,
42
+ useExecuteTransaction: () => useExecuteTransaction,
27
43
  useQuote: () => useQuote,
28
- useTokenList: () => useTokenList
44
+ useSwap: () => useSwap,
45
+ useTokenList: () => useTokenList,
46
+ useTokensData: () => useTokensData
29
47
  });
30
48
  module.exports = __toCommonJS(react_exports);
31
49
 
32
- // src/react/useTokenList.ts
50
+ // src/react/SwapProvider.tsx
33
51
  var import_react = require("react");
34
52
 
35
- // src/core/token-list.ts
36
- var import_intents_sdk = require("@shogun-sdk/intents-sdk");
37
- async function getTokenList(params) {
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/executeOrder/execute.ts
115
- var import_intents_sdk9 = require("@shogun-sdk/intents-sdk");
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 import_intents_sdk2 = require("@shogun-sdk/intents-sdk");
132
- var SOLANA_CHAIN_ID = import_intents_sdk2.ChainID.Solana;
133
- var SupportedChains = [
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: import_intents_sdk2.ChainID.Arbitrum,
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: import_intents_sdk2.ChainID.Optimism,
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: import_intents_sdk2.ChainID.Base,
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: import_intents_sdk2.ChainID.Hyperliquid,
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: import_intents_sdk2.ChainID.BSC,
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 import_ethers = require("ethers/lib/ethers.js");
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(import_viem.publicActions);
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, import_viem.custom)(wallet.transport),
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/executeOrder/handleEvmExecution.ts
306
- var import_intents_sdk7 = require("@shogun-sdk/intents-sdk");
307
- var import_viem3 = require("viem");
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/executeOrder/stageMessages.ts
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/executeOrder/buildOrder.ts
335
- var import_intents_sdk4 = require("@shogun-sdk/intents-sdk");
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 import_intents_sdk4.SingleChainOrder.create({
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: quote.internal.estimatedAmountOutReduced,
516
+ amountOutMin,
352
517
  deadline,
353
518
  destinationAddress: destination
354
519
  });
355
520
  }
356
- return await import_intents_sdk4.CrossChainOrder.create({
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: quote.internal.estimatedAmountOutReduced,
530
+ destinationTokenMinAmount: amountOutMin,
366
531
  minStablecoinAmount: quote.minStablecoinsAmount
367
532
  });
368
533
  }
369
534
 
370
535
  // src/utils/pollOrderStatus.ts
371
- var import_intents_sdk5 = require("@shogun-sdk/intents-sdk");
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 = `${import_intents_sdk5.AUCTIONEER_URL}/user_intent?${queryParam}`;
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/executeOrder/handleOrderPollingResult.ts
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/executeOrder/ensurePermit2Allowance.ts
475
- var import_viem2 = require("viem");
476
- var import_intents_sdk6 = require("@shogun-sdk/intents-sdk");
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 = import_intents_sdk6.PERMIT2_ADDRESS[chainId];
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: import_viem2.erc20Abi,
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 : import_viem2.maxUint256;
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, import_viem2.encodeFunctionData)({
508
- abi: import_viem2.erc20Abi,
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/executeOrder/handleEvmExecution.ts
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
- deadline,
528
- update
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, import_viem3.encodeFunctionData)({
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, import_intents_sdk7.getEVMSingleChainOrderTypedData)(order) : await (0, import_intents_sdk7.getEVMCrossChainOrderTypedData)(order);
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
- const status = await pollOrderStatus(accountAddress, orderId);
587
- return await handleOrderPollingResult({
588
- status,
589
- orderId,
590
- chainId,
591
- update,
592
- messageFor
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/executeOrder/handleSolanaExecution.ts
597
- var import_intents_sdk8 = require("@shogun-sdk/intents-sdk");
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
- deadline
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 messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
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
- const status = await pollOrderStatus(jwt, orderId);
642
- return await handleOrderPollingResult({
643
- status,
644
- orderId,
645
- chainId: SOLANA_CHAIN_ID,
646
- update,
647
- messageFor
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, import_intents_sdk8.getSolanaSingleChainOrderInstructions)(order, {
853
+ return await (0, import_intents_sdk11.getSolanaSingleChainOrderInstructions)(order, {
657
854
  rpcUrl
658
855
  });
659
856
  }
660
- return await (0, import_intents_sdk8.getSolanaCrossChainOrderInstructions)(order, {
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/executeOrder/execute.ts
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, import_intents_sdk9.isEvmChain)(chainId)) {
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
- deadline,
724
- update
931
+ update,
932
+ orderType,
933
+ options
725
934
  });
726
935
  log("EVM execution result:", result);
727
936
  return result;
728
937
  }
729
- if (chainId === import_intents_sdk9.ChainID.Solana) {
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
- deadline,
738
- update
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
- const message = error instanceof import_viem4.BaseError ? error.shortMessage : error instanceof Error ? error.message : String(error);
749
- log("Execution failed:", { message, error });
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/react/useExecuteOrder.ts
761
- function useExecuteOrder() {
762
- const [status, setStatus] = (0, import_react2.useState)("processing");
763
- const [message, setMessage] = (0, import_react2.useState)(null);
764
- const [loading, setLoading] = (0, import_react2.useState)(false);
765
- const [data, setData] = (0, import_react2.useState)(null);
766
- const [error, setError] = (0, import_react2.useState)(null);
767
- const isMounted = (0, import_react2.useRef)(true);
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
- return () => {
770
- isMounted.current = false;
771
- };
772
- }, []);
773
- const execute = (0, import_react2.useCallback)(
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
- deadline
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
- setData(null);
787
- setMessage(null);
1285
+ setResult(null);
1286
+ const onStatus = (s, msg) => {
1287
+ setStage(s);
1288
+ setMessage(msg ?? "");
1289
+ };
788
1290
  try {
789
- const effectiveDeadline = deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
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
- options: { deadline: effectiveDeadline }
1297
+ orderType,
1298
+ options
802
1299
  });
803
- if (!isMounted.current) return result;
804
- setData(result);
805
- setStatus(result.stage);
806
- setMessage("Order executed successfully");
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
- status: false,
822
- stage: "error",
823
- message: errorObj.message
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
- if (isMounted.current) setLoading(false);
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
- /** Current execution stage. */
835
- status,
836
- /** Human-readable status message. */
1322
+ isLoading,
1323
+ stage,
837
1324
  message,
838
- /** Whether execution is ongoing. */
839
- loading,
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/useQuote.ts
848
- var import_react3 = require("react");
849
-
850
- // src/core/getQuote.ts
851
- var import_intents_sdk10 = require("@shogun-sdk/intents-sdk");
852
- var import_viem5 = require("viem");
853
- async function getQuote(params) {
854
- if (!params.tokenIn?.address || !params.tokenOut?.address) {
855
- throw new Error("Both tokenIn and tokenOut must include an address.");
856
- }
857
- if (!params.sourceChainId || !params.destChainId) {
858
- throw new Error("Both sourceChainId and destChainId are required.");
859
- }
860
- if (params.amount <= 0n) {
861
- throw new Error("Amount must be greater than 0.");
862
- }
863
- const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
864
- const data = await import_intents_sdk10.QuoteProvider.getQuote({
865
- sourceChainId: params.sourceChainId,
866
- destChainId: params.destChainId,
867
- tokenIn: normalizedTokenIn,
868
- tokenOut: params.tokenOut.address,
869
- amount: params.amount
870
- });
871
- const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
872
- let warning;
873
- if (slippagePercent > 10) {
874
- warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
875
- }
876
- const estimatedAmountOut = BigInt(data.estimatedAmountOut);
877
- const slippageBps = BigInt(Math.round(slippagePercent * 100));
878
- const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
879
- const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
880
- const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
881
- const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
882
- const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
883
- const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
884
- return {
885
- amountOut: estimatedAmountOutAfterSlippage,
886
- amountOutUsd: amountOutUsdAfterSlippage,
887
- amountInUsd: data.amountInUsd,
888
- // Input USD stays the same
889
- minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
890
- tokenIn: {
891
- address: params.tokenIn.address,
892
- decimals: params.tokenIn.decimals ?? 18,
893
- chainId: params.sourceChainId
894
- },
895
- tokenOut: {
896
- address: params.tokenOut.address,
897
- decimals: params.tokenOut.decimals ?? 18,
898
- chainId: params.destChainId
899
- },
900
- amountIn: params.amount,
901
- pricePerInputToken,
902
- slippage: slippagePercent,
903
- internal: {
904
- ...data,
905
- estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
906
- estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
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
- warning
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, import_react3.useEffect)(() => {
928
- mounted.current = true;
929
- return () => {
930
- mounted.current = false;
931
- abortRef.current?.abort();
932
- if (debounceRef.current) clearTimeout(debounceRef.current);
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
- results: combined,
1032
- nextCursorEvm: data.evm?.cursor ?? null,
1033
- nextCursorSvm: data.svm?.cursor ?? null
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/useBalances.ts
1038
- function useBalances(params) {
1039
- const [data, setData] = (0, import_react4.useState)(null);
1040
- const [loading, setLoading] = (0, import_react4.useState)(false);
1041
- const [error, setError] = (0, import_react4.useState)(null);
1042
- const abortRef = (0, import_react4.useRef)(null);
1043
- const stableParams = (0, import_react4.useMemo)(() => {
1044
- if (!params) return null;
1045
- const { addresses, cursorEvm, cursorSvm } = params;
1046
- return {
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
- try {
1063
- const result = await getBalances(stableParams, { signal: controller.signal });
1064
- setData((prev) => {
1065
- if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
1066
- return result;
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
- if (abortRef.current) abortRef.current.abort();
1450
+ isMounted = false;
1083
1451
  };
1084
- }, [fetchBalances]);
1085
- return (0, import_react4.useMemo)(
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");