@shogun-sdk/swap 0.0.2-test.25 → 0.0.2-test.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -15,105 +18,44 @@ var __copyProps = (to, from, except, desc) => {
15
18
  }
16
19
  return to;
17
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
18
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
19
31
 
20
32
  // src/react/index.ts
21
33
  var react_exports = {};
22
34
  __export(react_exports, {
23
- 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_viem5 = require("viem");
57
+ // src/core/execute/normalizeNative.ts
58
+ var import_intents_sdk2 = require("@shogun-sdk/intents-sdk");
117
59
 
118
60
  // src/utils/address.ts
119
61
  var import_viem = require("viem");
@@ -133,11 +75,25 @@ function normalizeEvmTokenAddress(address) {
133
75
  }
134
76
 
135
77
  // src/utils/chain.ts
136
- var import_intents_sdk2 = require("@shogun-sdk/intents-sdk");
137
- var SOLANA_CHAIN_ID = import_intents_sdk2.ChainID.Solana;
138
- 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 = [
139
95
  {
140
- id: import_intents_sdk2.ChainID.Arbitrum,
96
+ id: import_intents_sdk.ChainID.Arbitrum,
141
97
  name: "Arbitrum",
142
98
  isEVM: true,
143
99
  wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -146,7 +102,7 @@ var SupportedChains = [
146
102
  tokenAddress: NATIVE_TOKEN.ETH
147
103
  },
148
104
  {
149
- id: import_intents_sdk2.ChainID.Optimism,
105
+ id: import_intents_sdk.ChainID.Optimism,
150
106
  name: "Optimism",
151
107
  isEVM: true,
152
108
  wrapped: "0x4200000000000000000000000000000000000006",
@@ -155,7 +111,7 @@ var SupportedChains = [
155
111
  tokenAddress: NATIVE_TOKEN.ETH
156
112
  },
157
113
  {
158
- id: import_intents_sdk2.ChainID.Base,
114
+ id: import_intents_sdk.ChainID.Base,
159
115
  name: "Base",
160
116
  isEVM: true,
161
117
  wrapped: "0x4200000000000000000000000000000000000006",
@@ -164,7 +120,7 @@ var SupportedChains = [
164
120
  tokenAddress: NATIVE_TOKEN.ETH
165
121
  },
166
122
  {
167
- id: import_intents_sdk2.ChainID.Hyperliquid,
123
+ id: import_intents_sdk.ChainID.Hyperliquid,
168
124
  name: "Hyperliquid",
169
125
  isEVM: true,
170
126
  wrapped: "0x5555555555555555555555555555555555555555",
@@ -173,7 +129,7 @@ var SupportedChains = [
173
129
  tokenAddress: NATIVE_TOKEN.ETH
174
130
  },
175
131
  {
176
- id: import_intents_sdk2.ChainID.BSC,
132
+ id: import_intents_sdk.ChainID.BSC,
177
133
  name: "BSC",
178
134
  isEVM: true,
179
135
  wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
@@ -191,6 +147,10 @@ var SupportedChains = [
191
147
  tokenAddress: NATIVE_TOKEN.SOL
192
148
  }
193
149
  ];
150
+ var SupportedChains = SupportedChainsInternal.filter(
151
+ (c) => CURRENT_SUPPORTED.includes(c.id)
152
+ );
153
+ var isEvmChain = import_intents_sdk.isEvmChain;
194
154
 
195
155
  // src/utils/viem.ts
196
156
  function isViemWalletClient(wallet) {
@@ -218,10 +178,198 @@ function serializeBigIntsToStrings(obj) {
218
178
  return obj;
219
179
  }
220
180
 
181
+ // src/core/execute/normalizeNative.ts
182
+ function normalizeNative(chainId, address) {
183
+ if ((0, import_intents_sdk2.isEvmChain)(chainId) && isNativeAddress(address)) {
184
+ const chain = SupportedChains.find((c) => c.id === chainId);
185
+ if (!chain?.wrapped)
186
+ throw new Error(`Wrapped token not found for chainId ${chainId}`);
187
+ return chain.wrapped;
188
+ }
189
+ return address;
190
+ }
191
+
192
+ // src/core/getQuote.ts
193
+ async function getQuote(params) {
194
+ const amount = BigInt(params.amount);
195
+ if (!params.tokenIn?.address || !params.tokenOut?.address) {
196
+ throw new Error("Both tokenIn and tokenOut must include an address.");
197
+ }
198
+ if (!params.sourceChainId || !params.destChainId) {
199
+ throw new Error("Both sourceChainId and destChainId are required.");
200
+ }
201
+ if (amount <= 0n) {
202
+ throw new Error("Amount must be greater than 0.");
203
+ }
204
+ const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
205
+ const data = await import_intents_sdk3.QuoteProvider.getQuote({
206
+ sourceChainId: params.sourceChainId,
207
+ destChainId: params.destChainId,
208
+ tokenIn: normalizedTokenIn,
209
+ tokenOut: params.tokenOut.address,
210
+ amount
211
+ });
212
+ const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
213
+ let warning;
214
+ if (slippagePercent > 10) {
215
+ warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
216
+ }
217
+ const estimatedAmountOut = BigInt(data.estimatedAmountOut);
218
+ const slippageBps = BigInt(Math.round(slippagePercent * 100));
219
+ const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
220
+ const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
221
+ const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
222
+ const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
223
+ const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
224
+ const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
225
+ return {
226
+ amountOut: estimatedAmountOutAfterSlippage,
227
+ amountOutUsd: amountOutUsdAfterSlippage,
228
+ amountInUsd: data.amountInUsd,
229
+ // Input USD stays the same
230
+ minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
231
+ tokenIn: {
232
+ address: params.tokenIn.address,
233
+ decimals: params.tokenIn.decimals ?? 18,
234
+ chainId: params.sourceChainId
235
+ },
236
+ tokenOut: {
237
+ address: params.tokenOut.address,
238
+ decimals: params.tokenOut.decimals ?? 18,
239
+ chainId: params.destChainId
240
+ },
241
+ amountIn: BigInt(params.amount),
242
+ pricePerInputToken,
243
+ slippage: slippagePercent,
244
+ internal: {
245
+ ...data,
246
+ estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
247
+ estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
248
+ },
249
+ warning
250
+ };
251
+ }
252
+ function buildQuoteParams({
253
+ tokenIn,
254
+ tokenOut,
255
+ sourceChainId,
256
+ destChainId,
257
+ amount,
258
+ slippage
259
+ }) {
260
+ return {
261
+ tokenIn,
262
+ tokenOut,
263
+ sourceChainId,
264
+ destChainId,
265
+ amount: (0, import_viem2.parseUnits)(amount.toString(), tokenIn.decimals ?? 18).toString(),
266
+ slippage
267
+ };
268
+ }
269
+
270
+ // src/core/getBalances.ts
271
+ var import_intents_sdk4 = require("@shogun-sdk/intents-sdk");
272
+ async function getBalances(params, options) {
273
+ const { addresses, cursorEvm, cursorSvm } = params;
274
+ const { signal } = options ?? {};
275
+ if (!addresses?.evm && !addresses?.svm) {
276
+ throw new Error("At least one address (EVM or SVM) must be provided.");
277
+ }
278
+ const payload = JSON.stringify({
279
+ addresses,
280
+ cursorEvm,
281
+ cursorSvm
282
+ });
283
+ const start = performance.now();
284
+ const response = await fetch(`${import_intents_sdk4.TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
285
+ method: "POST",
286
+ headers: {
287
+ accept: "application/json",
288
+ "Content-Type": "application/json"
289
+ },
290
+ body: payload,
291
+ signal
292
+ }).catch((err) => {
293
+ if (err.name === "AbortError") {
294
+ throw new Error("Balance request was cancelled.");
295
+ }
296
+ throw err;
297
+ });
298
+ if (!response.ok) {
299
+ const text = await response.text().catch(() => "");
300
+ throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
301
+ }
302
+ const data = await response.json().catch(() => {
303
+ throw new Error("Invalid JSON response from balances API.");
304
+ });
305
+ const duration = (performance.now() - start).toFixed(1);
306
+ if (process.env.NODE_ENV !== "production") {
307
+ console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
308
+ }
309
+ const evmItems = data.evm?.items ?? [];
310
+ const svmItems = data.svm?.items ?? [];
311
+ const combined = [...evmItems, ...svmItems];
312
+ const filtered = combined.filter(
313
+ (b) => CURRENT_SUPPORTED.includes(b.chainId)
314
+ );
315
+ return {
316
+ results: filtered,
317
+ nextCursorEvm: data.evm?.cursor ?? null,
318
+ nextCursorSvm: data.svm?.cursor ?? null
319
+ };
320
+ }
321
+
322
+ // src/core/token-list.ts
323
+ var import_intents_sdk5 = require("@shogun-sdk/intents-sdk");
324
+ async function getTokenList(params) {
325
+ const url = new URL(`${import_intents_sdk5.TOKEN_SEARCH_API_BASE_URL}/tokens/search`);
326
+ if (params.q) url.searchParams.append("q", params.q);
327
+ if (params.networkId) url.searchParams.append("networkId", String(params.networkId));
328
+ if (params.page) url.searchParams.append("page", String(params.page));
329
+ if (params.limit) url.searchParams.append("limit", String(params.limit));
330
+ const res = await fetch(url.toString(), {
331
+ signal: params.signal
332
+ });
333
+ if (!res.ok) {
334
+ throw new Error(`Failed to fetch tokens: ${res.status} ${res.statusText}`);
335
+ }
336
+ const data = await res.json();
337
+ const filteredResults = data.results.filter(
338
+ (token) => CURRENT_SUPPORTED.includes(token.chainId)
339
+ );
340
+ return {
341
+ ...data,
342
+ results: filteredResults,
343
+ count: filteredResults.length
344
+ };
345
+ }
346
+
347
+ // src/core/token.ts
348
+ var import_intents_sdk6 = require("@shogun-sdk/intents-sdk");
349
+ async function getTokensData(addresses) {
350
+ if (!addresses?.length) return [];
351
+ const response = await fetch(`${import_intents_sdk6.TOKEN_SEARCH_API_BASE_URL}/tokens/tokens`, {
352
+ method: "POST",
353
+ headers: {
354
+ "Content-Type": "application/json",
355
+ accept: "*/*"
356
+ },
357
+ body: JSON.stringify({ addresses })
358
+ });
359
+ if (!response.ok) {
360
+ throw new Error(`Failed to fetch token data: ${response.statusText}`);
361
+ }
362
+ const data = await response.json();
363
+ const filtered = data.filter((t) => CURRENT_SUPPORTED.includes(Number(t.chainId)));
364
+ return filtered;
365
+ }
366
+
367
+ // src/core/execute/execute.ts
368
+ var import_intents_sdk12 = require("@shogun-sdk/intents-sdk");
369
+ var import_viem7 = require("viem");
370
+
221
371
  // src/wallet-adapter/evm-wallet-adapter/adapter.ts
222
- var import_ethers = require("ethers/lib/ethers.js");
223
- var import_utils = require("ethers/lib/utils.js");
224
- var import_viem2 = require("viem");
372
+ var import_viem3 = require("viem");
225
373
  function isEVMTransaction(tx) {
226
374
  return typeof tx.from === "string";
227
375
  }
@@ -287,7 +435,7 @@ var adaptViemWallet = (wallet) => {
287
435
  functionName,
288
436
  args = []
289
437
  }) => {
290
- const publicClient = wallet.extend(import_viem2.publicActions);
438
+ const publicClient = wallet.extend(import_viem3.publicActions);
291
439
  return await publicClient.readContract({
292
440
  address: address2,
293
441
  abi,
@@ -297,7 +445,7 @@ var adaptViemWallet = (wallet) => {
297
445
  };
298
446
  return {
299
447
  vmType: "EVM" /* EVM */,
300
- transport: (0, import_viem2.custom)(wallet.transport),
448
+ transport: (0, import_viem3.custom)(wallet.transport),
301
449
  getChainId: async () => wallet.getChainId(),
302
450
  address,
303
451
  sendTransaction,
@@ -307,23 +455,11 @@ var adaptViemWallet = (wallet) => {
307
455
  };
308
456
  };
309
457
 
310
- // src/core/executeOrder/handleEvmExecution.ts
311
- var import_intents_sdk7 = require("@shogun-sdk/intents-sdk");
312
- var import_viem4 = require("viem");
313
-
314
- // src/core/executeOrder/normalizeNative.ts
315
- var import_intents_sdk3 = require("@shogun-sdk/intents-sdk");
316
- function normalizeNative(chainId, address) {
317
- if ((0, import_intents_sdk3.isEvmChain)(chainId) && isNativeAddress(address)) {
318
- const chain = SupportedChains.find((c) => c.id === chainId);
319
- if (!chain?.wrapped)
320
- throw new Error(`Wrapped token not found for chainId ${chainId}`);
321
- return chain.wrapped;
322
- }
323
- return address;
324
- }
458
+ // src/core/execute/handleEvmExecution.ts
459
+ var import_intents_sdk10 = require("@shogun-sdk/intents-sdk");
460
+ var import_viem6 = require("viem");
325
461
 
326
- // src/core/executeOrder/stageMessages.ts
462
+ // src/core/execute/stageMessages.ts
327
463
  var DEFAULT_STAGE_MESSAGES = {
328
464
  processing: "Preparing transaction for execution",
329
465
  approving: "Approving token allowance",
@@ -332,33 +468,57 @@ var DEFAULT_STAGE_MESSAGES = {
332
468
  submitting: "Submitting transaction",
333
469
  initiated: "Transaction initiated.",
334
470
  success: "Transaction Executed successfully",
471
+ success_limit: "Limit order has been submitted successfully.",
335
472
  shogun_processing: "Shogun is processing your transaction",
336
473
  error: "Transaction failed during submission"
337
474
  };
338
475
 
339
- // src/core/executeOrder/buildOrder.ts
340
- 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
341
488
  async function buildOrder({
342
489
  quote,
343
490
  accountAddress,
344
491
  destination,
345
492
  deadline,
346
- isSingleChain
493
+ isSingleChain,
494
+ orderType,
495
+ options
347
496
  }) {
348
497
  const { tokenIn, tokenOut } = quote;
498
+ let amountOutMin = BigInt(quote.internal.estimatedAmountOutReduced);
499
+ if (orderType === "limit" /* LIMIT */ && options && "executionPrice" in options) {
500
+ const executionPrice = Number(options.executionPrice);
501
+ if (Number.isFinite(executionPrice) && executionPrice > 0) {
502
+ const decimalsIn = tokenIn.decimals ?? 18;
503
+ const decimalsOut = tokenOut.decimals ?? 18;
504
+ const formattedAmountIn = Number((0, import_viem4.formatUnits)(BigInt(quote.amountIn.toString()), decimalsIn));
505
+ const rawAmountOut = formattedAmountIn * executionPrice;
506
+ amountOutMin = (0, import_viem4.parseUnits)(rawAmountOut.toString(), decimalsOut);
507
+ }
508
+ }
349
509
  if (isSingleChain) {
350
- return await import_intents_sdk4.SingleChainOrder.create({
510
+ return await import_intents_sdk7.SingleChainOrder.create({
351
511
  user: accountAddress,
352
512
  chainId: tokenIn.chainId,
353
513
  tokenIn: tokenIn.address,
354
514
  tokenOut: tokenOut.address,
355
515
  amountIn: quote.amountIn,
356
- amountOutMin: quote.internal.estimatedAmountOutReduced,
516
+ amountOutMin,
357
517
  deadline,
358
518
  destinationAddress: destination
359
519
  });
360
520
  }
361
- return await import_intents_sdk4.CrossChainOrder.create({
521
+ return await import_intents_sdk7.CrossChainOrder.create({
362
522
  user: accountAddress,
363
523
  sourceChainId: tokenIn.chainId,
364
524
  sourceTokenAddress: tokenIn.address,
@@ -367,13 +527,13 @@ async function buildOrder({
367
527
  destinationTokenAddress: tokenOut.address,
368
528
  destinationAddress: destination,
369
529
  deadline,
370
- destinationTokenMinAmount: quote.internal.estimatedAmountOutReduced,
530
+ destinationTokenMinAmount: amountOutMin,
371
531
  minStablecoinAmount: quote.minStablecoinsAmount
372
532
  });
373
533
  }
374
534
 
375
535
  // src/utils/pollOrderStatus.ts
376
- var import_intents_sdk5 = require("@shogun-sdk/intents-sdk");
536
+ var import_intents_sdk8 = require("@shogun-sdk/intents-sdk");
377
537
  async function pollOrderStatus(address, orderId, options = {}) {
378
538
  const { intervalMs = 2e3, timeoutMs = 3e5 } = options;
379
539
  const startTime = Date.now();
@@ -386,7 +546,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
386
546
  else if (isSuiAddress) queryParam = `suiWallets=${address}`;
387
547
  else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
388
548
  else throw new Error(`Unrecognized wallet address format: ${address}`);
389
- const queryUrl = `${import_intents_sdk5.AUCTIONEER_URL}/user_intent?${queryParam}`;
549
+ const queryUrl = `${import_intents_sdk8.AUCTIONEER_URL}/user_intent?${queryParam}`;
390
550
  return new Promise((resolve, reject) => {
391
551
  const pollInterval = setInterval(async () => {
392
552
  try {
@@ -438,7 +598,7 @@ async function pollOrderStatus(address, orderId, options = {}) {
438
598
  });
439
599
  }
440
600
 
441
- // src/core/executeOrder/handleOrderPollingResult.ts
601
+ // src/core/execute/handleOrderPollingResult.ts
442
602
  async function handleOrderPollingResult({
443
603
  status,
444
604
  orderId,
@@ -476,9 +636,9 @@ async function handleOrderPollingResult({
476
636
  };
477
637
  }
478
638
 
479
- // src/core/executeOrder/ensurePermit2Allowance.ts
480
- var import_viem3 = require("viem");
481
- 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");
482
642
  async function ensurePermit2Allowance({
483
643
  chainId,
484
644
  tokenIn,
@@ -487,7 +647,7 @@ async function ensurePermit2Allowance({
487
647
  requiredAmount,
488
648
  increaseByDelta = false
489
649
  }) {
490
- const spender = import_intents_sdk6.PERMIT2_ADDRESS[chainId];
650
+ const spender = import_intents_sdk9.PERMIT2_ADDRESS[chainId];
491
651
  let currentAllowance = 0n;
492
652
  try {
493
653
  if (!wallet.readContract) {
@@ -495,22 +655,22 @@ async function ensurePermit2Allowance({
495
655
  }
496
656
  currentAllowance = await wallet.readContract({
497
657
  address: tokenIn,
498
- abi: import_viem3.erc20Abi,
658
+ abi: import_viem5.erc20Abi,
499
659
  functionName: "allowance",
500
660
  args: [accountAddress, spender]
501
661
  });
502
662
  } catch (error) {
503
663
  console.warn(`[Permit2] Failed to read allowance for ${tokenIn}`, error);
504
664
  }
505
- const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : import_viem3.maxUint256;
665
+ const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : import_viem5.maxUint256;
506
666
  console.debug(
507
667
  `[Permit2] Approving ${approvalAmount} for ${tokenIn} (current: ${currentAllowance}, required: ${requiredAmount})`
508
668
  );
509
669
  await wallet.sendTransaction({
510
670
  to: tokenIn,
511
671
  from: accountAddress,
512
- data: (0, import_viem3.encodeFunctionData)({
513
- abi: import_viem3.erc20Abi,
672
+ data: (0, import_viem5.encodeFunctionData)({
673
+ abi: import_viem5.erc20Abi,
514
674
  functionName: "approve",
515
675
  args: [spender, approvalAmount]
516
676
  }),
@@ -521,7 +681,7 @@ async function ensurePermit2Allowance({
521
681
  );
522
682
  }
523
683
 
524
- // src/core/executeOrder/handleEvmExecution.ts
684
+ // src/core/execute/handleEvmExecution.ts
525
685
  async function handleEvmExecution({
526
686
  recipientAddress,
527
687
  quote,
@@ -529,19 +689,22 @@ async function handleEvmExecution({
529
689
  accountAddress,
530
690
  wallet,
531
691
  isSingleChain,
532
- deadline,
533
- update
692
+ update,
693
+ orderType,
694
+ options
534
695
  }) {
535
- const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
696
+ const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
697
+ const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
536
698
  await wallet.switchChain(chainId);
537
699
  const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
538
700
  quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
539
701
  const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
540
702
  update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
541
703
  if (shouldWrapNative) {
704
+ quote.tokenIn.address === tokenIn;
542
705
  await wallet.sendTransaction({
543
706
  to: tokenIn,
544
- data: (0, import_viem4.encodeFunctionData)({
707
+ data: (0, import_viem6.encodeFunctionData)({
545
708
  abi: [{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] }],
546
709
  functionName: "deposit",
547
710
  args: []
@@ -565,11 +728,13 @@ async function handleEvmExecution({
565
728
  accountAddress,
566
729
  destination,
567
730
  deadline,
568
- isSingleChain
731
+ isSingleChain,
732
+ orderType,
733
+ options
569
734
  });
570
735
  console.debug(`order`, order);
571
736
  update("processing", messageFor("signing"));
572
- const { orderTypedData, nonce } = isSingleChain ? await (0, 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);
573
738
  const typedData = serializeBigIntsToStrings(orderTypedData);
574
739
  if (!wallet.signTypedData) {
575
740
  throw new Error("Wallet does not support EIP-712 signing");
@@ -589,18 +754,29 @@ async function handleEvmExecution({
589
754
  update("initiated", messageFor("initiated"));
590
755
  const { intentId: orderId } = res.data;
591
756
  update("initiated", messageFor("shogun_processing"));
592
- const status = await pollOrderStatus(accountAddress, orderId);
593
- return await handleOrderPollingResult({
594
- status,
595
- orderId,
596
- chainId,
597
- update,
598
- messageFor
599
- });
757
+ if (orderType === "limit" /* LIMIT */) {
758
+ update("success", messageFor("success_limit"));
759
+ return {
760
+ status: true,
761
+ orderId,
762
+ chainId,
763
+ finalStatus: "OrderPlaced",
764
+ stage: "success"
765
+ };
766
+ } else {
767
+ const status = await pollOrderStatus(accountAddress, orderId);
768
+ return await handleOrderPollingResult({
769
+ status,
770
+ orderId,
771
+ chainId,
772
+ update,
773
+ messageFor
774
+ });
775
+ }
600
776
  }
601
777
 
602
- // src/core/executeOrder/handleSolanaExecution.ts
603
- 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");
604
780
  var import_web3 = require("@solana/web3.js");
605
781
  async function handleSolanaExecution({
606
782
  recipientAddress,
@@ -609,12 +785,14 @@ async function handleSolanaExecution({
609
785
  isSingleChain,
610
786
  update,
611
787
  accountAddress,
612
- deadline
788
+ orderType,
789
+ options
613
790
  }) {
614
791
  if (!wallet.rpcUrl) {
615
792
  throw new Error("Solana wallet is missing rpcUrl");
616
793
  }
617
- const 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] ?? "";
618
796
  update("processing", messageFor("processing"));
619
797
  const destination = recipientAddress ?? accountAddress;
620
798
  const order = await buildOrder({
@@ -622,7 +800,9 @@ async function handleSolanaExecution({
622
800
  accountAddress,
623
801
  destination,
624
802
  deadline,
625
- isSingleChain
803
+ isSingleChain,
804
+ orderType,
805
+ options
626
806
  });
627
807
  const txData = await getSolanaOrderInstructions({
628
808
  order,
@@ -644,14 +824,25 @@ async function handleSolanaExecution({
644
824
  update("initiated", messageFor("initiated"));
645
825
  const { jwt, intentId: orderId } = response.data;
646
826
  update("initiated", messageFor("shogun_processing"));
647
- const status = await pollOrderStatus(jwt, orderId);
648
- return await handleOrderPollingResult({
649
- status,
650
- orderId,
651
- chainId: SOLANA_CHAIN_ID,
652
- update,
653
- messageFor
654
- });
827
+ if (orderType === "limit" /* LIMIT */) {
828
+ update("success", messageFor("success_limit"));
829
+ return {
830
+ status: true,
831
+ orderId,
832
+ chainId: SOLANA_CHAIN_ID,
833
+ finalStatus: "OrderPlaced",
834
+ stage: "success"
835
+ };
836
+ } else {
837
+ const status = await pollOrderStatus(jwt, orderId);
838
+ return await handleOrderPollingResult({
839
+ status,
840
+ orderId,
841
+ chainId: SOLANA_CHAIN_ID,
842
+ update,
843
+ messageFor
844
+ });
845
+ }
655
846
  }
656
847
  async function getSolanaOrderInstructions({
657
848
  order,
@@ -659,11 +850,11 @@ async function getSolanaOrderInstructions({
659
850
  rpcUrl
660
851
  }) {
661
852
  if (isSingleChain) {
662
- return await (0, import_intents_sdk8.getSolanaSingleChainOrderInstructions)(order, {
853
+ return await (0, import_intents_sdk11.getSolanaSingleChainOrderInstructions)(order, {
663
854
  rpcUrl
664
855
  });
665
856
  }
666
- return await (0, import_intents_sdk8.getSolanaCrossChainOrderInstructions)(order, {
857
+ return await (0, import_intents_sdk11.getSolanaCrossChainOrderInstructions)(order, {
667
858
  rpcUrl
668
859
  });
669
860
  }
@@ -684,13 +875,14 @@ async function submitToAuctioneer({
684
875
  });
685
876
  }
686
877
 
687
- // src/core/executeOrder/execute.ts
878
+ // src/core/execute/execute.ts
688
879
  async function executeOrder({
689
880
  quote,
690
881
  accountAddress,
691
882
  recipientAddress,
692
883
  wallet,
693
884
  onStatus,
885
+ orderType = "market" /* MARKET */,
694
886
  options = {}
695
887
  }) {
696
888
  const isDev = process.env.NODE_ENV !== "production";
@@ -703,21 +895,31 @@ async function executeOrder({
703
895
  onStatus?.(stage, message ?? messageFor(stage));
704
896
  };
705
897
  try {
706
- const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
707
898
  log("Starting execution:", {
708
899
  accountAddress,
709
900
  recipientAddress,
710
- deadline,
711
901
  tokenIn: quote?.tokenIn,
712
902
  tokenOut: quote?.tokenOut
713
903
  });
714
904
  const adapter = normalizeWallet(wallet);
715
905
  if (!adapter) throw new Error("No wallet provided");
716
906
  const { tokenIn, tokenOut } = quote;
907
+ const srcChain = Number(tokenIn.chainId);
908
+ const destChain = Number(tokenOut.chainId);
909
+ if (!CURRENT_SUPPORTED.includes(srcChain) || !CURRENT_SUPPORTED.includes(destChain)) {
910
+ const unsupportedChains = [
911
+ !CURRENT_SUPPORTED.includes(srcChain) ? srcChain : null,
912
+ !CURRENT_SUPPORTED.includes(destChain) ? destChain : null
913
+ ].filter(Boolean).join(", ");
914
+ const errorMsg = `Unsupported chain(s): ${unsupportedChains}`;
915
+ update("error", errorMsg);
916
+ log("Error:", errorMsg);
917
+ throw new Error(errorMsg);
918
+ }
717
919
  const isSingleChain = tokenIn.chainId === tokenOut.chainId;
718
920
  const chainId = Number(tokenIn.chainId);
719
921
  update("processing");
720
- if ((0, import_intents_sdk9.isEvmChain)(chainId)) {
922
+ if ((0, import_intents_sdk12.isEvmChain)(chainId)) {
721
923
  log("Detected EVM chain:", chainId);
722
924
  const result = await handleEvmExecution({
723
925
  recipientAddress,
@@ -726,13 +928,14 @@ async function executeOrder({
726
928
  accountAddress,
727
929
  wallet: adapter,
728
930
  isSingleChain,
729
- deadline,
730
- update
931
+ update,
932
+ orderType,
933
+ options
731
934
  });
732
935
  log("EVM execution result:", result);
733
936
  return result;
734
937
  }
735
- if (chainId === import_intents_sdk9.ChainID.Solana) {
938
+ if (chainId === import_intents_sdk12.ChainID.Solana) {
736
939
  log("Detected Solana chain");
737
940
  const result = await handleSolanaExecution({
738
941
  recipientAddress,
@@ -740,8 +943,9 @@ async function executeOrder({
740
943
  accountAddress,
741
944
  wallet: adapter,
742
945
  isSingleChain,
743
- deadline,
744
- update
946
+ update,
947
+ orderType,
948
+ options
745
949
  });
746
950
  log("Solana execution result:", result);
747
951
  return result;
@@ -751,8 +955,13 @@ async function executeOrder({
751
955
  log("Error:", unsupported);
752
956
  return { status: false, message: unsupported, stage: "error" };
753
957
  } catch (error) {
754
- const message = error instanceof import_viem5.BaseError ? error.shortMessage : error instanceof Error ? error.message : String(error);
755
- 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
+ }
756
965
  update("error", message);
757
966
  return { status: false, message, stage: "error" };
758
967
  }
@@ -763,345 +972,483 @@ function normalizeWallet(wallet) {
763
972
  return wallet;
764
973
  }
765
974
 
766
- // src/react/useExecuteOrder.ts
767
- function useExecuteOrder() {
768
- const [status, setStatus] = (0, import_react2.useState)("processing");
769
- const [message, setMessage] = (0, import_react2.useState)(null);
770
- const [loading, setLoading] = (0, import_react2.useState)(false);
771
- const [data, setData] = (0, import_react2.useState)(null);
772
- const [error, setError] = (0, import_react2.useState)(null);
773
- 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 });
774
1142
  (0, import_react2.useEffect)(() => {
775
- return () => {
776
- isMounted.current = false;
777
- };
778
- }, []);
779
- 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)(
780
1275
  async ({
781
1276
  quote,
782
1277
  accountAddress,
783
1278
  recipientAddress,
784
1279
  wallet,
785
- deadline
1280
+ orderType,
1281
+ options
786
1282
  }) => {
787
- if (!quote || !wallet) {
788
- throw new Error("Quote and wallet are required for order execution.");
789
- }
790
1283
  setLoading(true);
791
1284
  setError(null);
792
- setData(null);
793
- setMessage(null);
1285
+ setResult(null);
1286
+ const onStatus = (s, msg) => {
1287
+ setStage(s);
1288
+ setMessage(msg ?? "");
1289
+ };
794
1290
  try {
795
- const effectiveDeadline = deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
796
- const onStatus = (stage, msg) => {
797
- if (!isMounted.current) return;
798
- setStatus(stage);
799
- if (msg) setMessage(msg);
800
- };
801
- const result = await executeOrder({
1291
+ const res = await sdk.executeTransaction({
802
1292
  quote,
1293
+ wallet,
803
1294
  accountAddress,
804
1295
  recipientAddress,
805
- wallet,
806
1296
  onStatus,
807
- options: { deadline: effectiveDeadline }
1297
+ orderType,
1298
+ options
808
1299
  });
809
- if (!isMounted.current) return result;
810
- setData(result);
811
- setStatus(result.stage);
812
- setMessage("Order executed successfully");
813
- return result;
814
- } catch (err) {
815
- const errorObj = err instanceof Error ? err : new Error(String(err));
816
- if (isMounted.current) {
817
- setError(errorObj);
818
- setStatus("error");
819
- setMessage(errorObj.message);
820
- setData({
821
- status: false,
822
- stage: "error",
823
- message: errorObj.message
1300
+ if (res && typeof res === "object" && "status" in res && "stage" in res && typeof res.stage === "string") {
1301
+ setResult({
1302
+ ...res,
1303
+ stage: res.stage
824
1304
  });
1305
+ } else {
1306
+ setResult(res);
825
1307
  }
826
- return {
827
- status: false,
828
- stage: "error",
829
- message: errorObj.message
830
- };
1308
+ return res;
1309
+ } catch (err) {
1310
+ const msg = err instanceof Error ? err.message : String(err);
1311
+ setError(msg);
1312
+ throw err;
831
1313
  } finally {
832
- if (isMounted.current) setLoading(false);
1314
+ await Promise.allSettled([refetchQuote(), refetchBalances()]);
1315
+ setLoading(false);
833
1316
  }
834
1317
  },
835
- []
1318
+ [sdk, refetchQuote, refetchBalances]
836
1319
  );
837
1320
  return {
838
- /** Executes the swap order. */
839
1321
  execute,
840
- /** Current execution stage. */
841
- status,
842
- /** Human-readable status message. */
1322
+ isLoading,
1323
+ stage,
843
1324
  message,
844
- /** Whether execution is ongoing. */
845
- loading,
846
- /** Raw SDK response data. */
847
- data,
848
- /** Captured error (if execution failed). */
849
- error
1325
+ error,
1326
+ result
850
1327
  };
851
1328
  }
852
1329
 
853
- // src/react/useQuote.ts
854
- var import_react3 = require("react");
855
-
856
- // src/core/getQuote.ts
857
- var import_intents_sdk10 = require("@shogun-sdk/intents-sdk");
858
- var import_viem6 = require("viem");
859
- async function getQuote(params) {
860
- if (!params.tokenIn?.address || !params.tokenOut?.address) {
861
- throw new Error("Both tokenIn and tokenOut must include an address.");
862
- }
863
- if (!params.sourceChainId || !params.destChainId) {
864
- throw new Error("Both sourceChainId and destChainId are required.");
865
- }
866
- if (params.amount <= 0n) {
867
- throw new Error("Amount must be greater than 0.");
868
- }
869
- const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
870
- const data = await import_intents_sdk10.QuoteProvider.getQuote({
871
- sourceChainId: params.sourceChainId,
872
- destChainId: params.destChainId,
873
- tokenIn: normalizedTokenIn,
874
- tokenOut: params.tokenOut.address,
875
- amount: params.amount
876
- });
877
- const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
878
- let warning;
879
- if (slippagePercent > 10) {
880
- warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
881
- }
882
- const estimatedAmountOut = BigInt(data.estimatedAmountOut);
883
- const slippageBps = BigInt(Math.round(slippagePercent * 100));
884
- const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
885
- const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
886
- const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
887
- const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
888
- const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
889
- const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
890
- return {
891
- amountOut: estimatedAmountOutAfterSlippage,
892
- amountOutUsd: amountOutUsdAfterSlippage,
893
- amountInUsd: data.amountInUsd,
894
- // Input USD stays the same
895
- minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
896
- tokenIn: {
897
- address: params.tokenIn.address,
898
- decimals: params.tokenIn.decimals ?? 18,
899
- chainId: params.sourceChainId
900
- },
901
- tokenOut: {
902
- address: params.tokenOut.address,
903
- decimals: params.tokenOut.decimals ?? 18,
904
- chainId: params.destChainId
905
- },
906
- amountIn: params.amount,
907
- pricePerInputToken,
908
- slippage: slippagePercent,
909
- internal: {
910
- ...data,
911
- estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
912
- 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
+ }
913
1406
  },
914
- warning
915
- };
916
- }
917
-
918
- // src/react/useQuote.ts
919
- function useQuote(params, options) {
920
- const [data, setData] = (0, import_react3.useState)(null);
921
- const [loading, setLoading] = (0, import_react3.useState)(false);
922
- const [error, setError] = (0, import_react3.useState)(null);
923
- const [warning, setWarning] = (0, import_react3.useState)(null);
924
- const debounceMs = options?.debounceMs ?? 250;
925
- const autoRefreshMs = options?.autoRefreshMs;
926
- const abortRef = (0, import_react3.useRef)(null);
927
- const debounceRef = (0, import_react3.useRef)(null);
928
- const mounted = (0, import_react3.useRef)(false);
929
- const paramsKey = (0, import_react3.useMemo)(
930
- () => params ? JSON.stringify(serializeBigIntsToStrings(params)) : null,
931
- [params]
1407
+ [sdk]
932
1408
  );
933
- (0, import_react3.useEffect)(() => {
934
- mounted.current = true;
935
- return () => {
936
- mounted.current = false;
937
- abortRef.current?.abort();
938
- if (debounceRef.current) clearTimeout(debounceRef.current);
939
- };
1409
+ const resetTokens = (0, import_react5.useCallback)(() => {
1410
+ setTokens([]);
1411
+ setPage(1);
1412
+ setHasMore(true);
1413
+ pageRef.current = 1;
1414
+ hasMoreRef.current = true;
1415
+ cache.current.clear();
1416
+ lastQuery.current = {};
940
1417
  }, []);
941
- const fetchQuote = (0, import_react3.useCallback)(async () => {
942
- if (!params) return;
943
- try {
944
- setLoading(true);
945
- setWarning(null);
946
- const result = await getQuote(params);
947
- const serializeResult = serializeBigIntsToStrings(result);
948
- if (!mounted.current) return;
949
- setData((prev) => {
950
- if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
951
- return serializeResult;
952
- });
953
- setWarning(result.warning ?? null);
954
- setError(null);
955
- } catch (err) {
956
- if (err.name === "AbortError") return;
957
- console.error("[useQuote] fetch error:", err);
958
- if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
959
- } finally {
960
- if (mounted.current) setLoading(false);
961
- }
962
- }, [paramsKey]);
963
- (0, import_react3.useEffect)(() => {
964
- if (!paramsKey) return;
965
- if (debounceRef.current) clearTimeout(debounceRef.current);
966
- debounceRef.current = setTimeout(() => {
967
- fetchQuote();
968
- }, debounceMs);
969
- return () => {
970
- if (debounceRef.current) clearTimeout(debounceRef.current);
971
- abortRef.current?.abort();
972
- };
973
- }, [paramsKey, debounceMs, fetchQuote]);
974
- (0, import_react3.useEffect)(() => {
975
- if (!autoRefreshMs || !paramsKey) return;
976
- const interval = setInterval(() => fetchQuote(), autoRefreshMs);
977
- return () => clearInterval(interval);
978
- }, [autoRefreshMs, paramsKey, fetchQuote]);
979
- return (0, import_react3.useMemo)(
980
- () => ({
981
- data,
982
- loading,
983
- error,
984
- warning,
985
- refetch: fetchQuote
986
- }),
987
- [data, loading, error, warning, fetchQuote]
988
- );
989
- }
990
-
991
- // src/react/useBalances.ts
992
- var import_react4 = require("react");
993
-
994
- // src/core/getBalances.ts
995
- var import_intents_sdk11 = require("@shogun-sdk/intents-sdk");
996
- async function getBalances(params, options) {
997
- const { addresses, cursorEvm, cursorSvm } = params;
998
- const { signal } = options ?? {};
999
- if (!addresses?.evm && !addresses?.svm) {
1000
- throw new Error("At least one address (EVM or SVM) must be provided.");
1001
- }
1002
- const payload = JSON.stringify({
1003
- addresses,
1004
- cursorEvm,
1005
- cursorSvm
1006
- });
1007
- const start = performance.now();
1008
- const response = await fetch(`${import_intents_sdk11.TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
1009
- method: "POST",
1010
- headers: {
1011
- accept: "application/json",
1012
- "Content-Type": "application/json"
1013
- },
1014
- body: payload,
1015
- signal
1016
- }).catch((err) => {
1017
- if (err.name === "AbortError") {
1018
- throw new Error("Balance request was cancelled.");
1019
- }
1020
- throw err;
1021
- });
1022
- if (!response.ok) {
1023
- const text = await response.text().catch(() => "");
1024
- throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
1025
- }
1026
- const data = await response.json().catch(() => {
1027
- throw new Error("Invalid JSON response from balances API.");
1028
- });
1029
- const duration = (performance.now() - start).toFixed(1);
1030
- if (process.env.NODE_ENV !== "production") {
1031
- console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
1032
- }
1033
- const evmItems = data.evm?.items ?? [];
1034
- const svmItems = data.svm?.items ?? [];
1035
- const combined = [...evmItems, ...svmItems];
1036
1418
  return {
1037
- results: combined,
1038
- nextCursorEvm: data.evm?.cursor ?? null,
1039
- nextCursorSvm: data.svm?.cursor ?? null
1419
+ tokens,
1420
+ loading,
1421
+ error,
1422
+ hasMore,
1423
+ page,
1424
+ lastQuery: lastQuery.current,
1425
+ loadTokens,
1426
+ resetTokens
1040
1427
  };
1041
1428
  }
1042
1429
 
1043
- // src/react/useBalances.ts
1044
- function useBalances(params) {
1045
- const [data, setData] = (0, import_react4.useState)(null);
1046
- const [loading, setLoading] = (0, import_react4.useState)(false);
1047
- const [error, setError] = (0, import_react4.useState)(null);
1048
- const abortRef = (0, import_react4.useRef)(null);
1049
- const stableParams = (0, import_react4.useMemo)(() => {
1050
- if (!params) return null;
1051
- const { addresses, cursorEvm, cursorSvm } = params;
1052
- return {
1053
- addresses: {
1054
- evm: addresses?.evm ?? void 0,
1055
- svm: addresses?.svm ?? void 0
1056
- },
1057
- cursorEvm,
1058
- cursorSvm
1059
- };
1060
- }, [params?.addresses?.evm, params?.addresses?.svm, params?.cursorEvm, params?.cursorSvm]);
1061
- const fetchBalances = (0, import_react4.useCallback)(async () => {
1062
- if (!stableParams) return;
1063
- if (abortRef.current) abortRef.current.abort();
1064
- const controller = new AbortController();
1065
- abortRef.current = controller;
1430
+ // src/react/useTokensData.ts
1431
+ var import_react6 = require("react");
1432
+ function useTokensData(addresses) {
1433
+ const sdk = useSwap();
1434
+ const [data, setData] = (0, import_react6.useState)([]);
1435
+ const [loading, setLoading] = (0, import_react6.useState)(false);
1436
+ const [error, setError] = (0, import_react6.useState)(null);
1437
+ (0, import_react6.useEffect)(() => {
1438
+ if (!addresses?.length) return;
1439
+ let isMounted = true;
1066
1440
  setLoading(true);
1067
1441
  setError(null);
1068
- try {
1069
- const result = await getBalances(stableParams, { signal: controller.signal });
1070
- setData((prev) => {
1071
- if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
1072
- return result;
1073
- });
1074
- return result;
1075
- } catch (err) {
1076
- if (err.name === "AbortError") return;
1077
- const e = err instanceof Error ? err : new Error(String(err));
1078
- setError(e);
1079
- throw e;
1080
- } finally {
1081
- setLoading(false);
1082
- }
1083
- }, [stableParams]);
1084
- (0, import_react4.useEffect)(() => {
1085
- if (stableParams) fetchBalances().catch(() => {
1442
+ sdk.getTokensData(addresses).then((res) => {
1443
+ if (isMounted) setData(res);
1444
+ }).catch((e) => {
1445
+ if (isMounted) setError(e instanceof Error ? e : new Error(String(e)));
1446
+ }).finally(() => {
1447
+ if (isMounted) setLoading(false);
1086
1448
  });
1087
1449
  return () => {
1088
- if (abortRef.current) abortRef.current.abort();
1450
+ isMounted = false;
1089
1451
  };
1090
- }, [fetchBalances]);
1091
- return (0, import_react4.useMemo)(
1092
- () => ({
1093
- /** Latest fetched balance data */
1094
- data,
1095
- /** Whether the hook is currently fetching */
1096
- loading,
1097
- /** Error object if fetching failed */
1098
- error,
1099
- /** Manually trigger a refresh */
1100
- refetch: fetchBalances
1101
- }),
1102
- [data, loading, error, fetchBalances]
1103
- );
1452
+ }, [sdk, JSON.stringify(addresses)]);
1453
+ return { data, loading, error };
1104
1454
  }
1105
-
1106
- // src/react/index.ts
1107
- var import_intents_sdk12 = require("@shogun-sdk/intents-sdk");