@shogun-sdk/swap 0.0.2-test.3 → 0.0.2-test.30

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/index.js CHANGED
@@ -1,17 +1,16 @@
1
- // src/core/token-list.ts
2
- import { getTokenList as intentsGetTokenList } from "@shogun-sdk/intents-sdk";
3
- async function getTokenList(params) {
4
- return intentsGetTokenList(params);
5
- }
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
4
 
7
5
  // src/core/getQuote.ts
8
6
  import { QuoteProvider } from "@shogun-sdk/intents-sdk";
9
7
  import { parseUnits } from "viem";
10
8
 
11
- // src/core/executeOrder/normalizeNative.ts
12
- import { isEvmChain } from "@shogun-sdk/intents-sdk";
9
+ // src/core/execute/normalizeNative.ts
10
+ import { isEvmChain as isEvmChain2 } from "@shogun-sdk/intents-sdk";
13
11
 
14
12
  // src/utils/address.ts
13
+ import { zeroAddress } from "viem";
15
14
  var NATIVE_TOKEN = {
16
15
  ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
17
16
  SOL: "So11111111111111111111111111111111111111111",
@@ -22,13 +21,32 @@ var isNativeAddress = (tokenAddress) => {
22
21
  const normalizedTokenAddress = tokenAddress.toLowerCase();
23
22
  return !!tokenAddress && NATIVE_ADDRESSES.includes(normalizedTokenAddress);
24
23
  };
24
+ function normalizeEvmTokenAddress(address) {
25
+ const lower = address.toLowerCase();
26
+ return lower === NATIVE_TOKEN.ETH.toLowerCase() ? zeroAddress : address;
27
+ }
25
28
 
26
29
  // src/utils/chain.ts
27
- import { ChainID } from "@shogun-sdk/intents-sdk";
28
- var SOLANA_CHAIN_ID = ChainID.Solana;
29
- var SupportedChains = [
30
+ import { ChainID as BaseChainID, isEvmChain as isEvmChainIntent } from "@shogun-sdk/intents-sdk";
31
+ var SOLANA_CHAIN_ID = BaseChainID.Solana;
32
+ var CURRENT_SUPPORTED = [
33
+ BaseChainID.Solana,
34
+ BaseChainID.BSC,
35
+ BaseChainID.Base,
36
+ BaseChainID.MONAD
37
+ ];
38
+ var ChainId = Object.entries(BaseChainID).reduce(
39
+ (acc, [key, value]) => {
40
+ if (typeof value === "number" && CURRENT_SUPPORTED.includes(value)) {
41
+ acc[key] = value;
42
+ }
43
+ return acc;
44
+ },
45
+ {}
46
+ );
47
+ var SupportedChainsInternal = [
30
48
  {
31
- id: ChainID.Arbitrum,
49
+ id: BaseChainID.Arbitrum,
32
50
  name: "Arbitrum",
33
51
  isEVM: true,
34
52
  wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
@@ -37,7 +55,7 @@ var SupportedChains = [
37
55
  tokenAddress: NATIVE_TOKEN.ETH
38
56
  },
39
57
  {
40
- id: ChainID.Optimism,
58
+ id: BaseChainID.Optimism,
41
59
  name: "Optimism",
42
60
  isEVM: true,
43
61
  wrapped: "0x4200000000000000000000000000000000000006",
@@ -46,7 +64,7 @@ var SupportedChains = [
46
64
  tokenAddress: NATIVE_TOKEN.ETH
47
65
  },
48
66
  {
49
- id: ChainID.Base,
67
+ id: BaseChainID.Base,
50
68
  name: "Base",
51
69
  isEVM: true,
52
70
  wrapped: "0x4200000000000000000000000000000000000006",
@@ -55,7 +73,7 @@ var SupportedChains = [
55
73
  tokenAddress: NATIVE_TOKEN.ETH
56
74
  },
57
75
  {
58
- id: ChainID.Hyperliquid,
76
+ id: BaseChainID.Hyperliquid,
59
77
  name: "Hyperliquid",
60
78
  isEVM: true,
61
79
  wrapped: "0x5555555555555555555555555555555555555555",
@@ -64,7 +82,7 @@ var SupportedChains = [
64
82
  tokenAddress: NATIVE_TOKEN.ETH
65
83
  },
66
84
  {
67
- id: ChainID.BSC,
85
+ id: BaseChainID.BSC,
68
86
  name: "BSC",
69
87
  isEVM: true,
70
88
  wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
@@ -80,8 +98,21 @@ var SupportedChains = [
80
98
  symbol: "SOL",
81
99
  decimals: 9,
82
100
  tokenAddress: NATIVE_TOKEN.SOL
101
+ },
102
+ {
103
+ id: BaseChainID.MONAD,
104
+ name: "Monad",
105
+ isEVM: true,
106
+ wrapped: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
107
+ symbol: "MON",
108
+ decimals: 18,
109
+ tokenAddress: NATIVE_TOKEN.ETH
83
110
  }
84
111
  ];
112
+ var SupportedChains = SupportedChainsInternal.filter(
113
+ (c) => CURRENT_SUPPORTED.includes(c.id)
114
+ );
115
+ var isEvmChain = isEvmChainIntent;
85
116
 
86
117
  // src/utils/viem.ts
87
118
  function isViemWalletClient(wallet) {
@@ -109,9 +140,9 @@ function serializeBigIntsToStrings(obj) {
109
140
  return obj;
110
141
  }
111
142
 
112
- // src/core/executeOrder/normalizeNative.ts
143
+ // src/core/execute/normalizeNative.ts
113
144
  function normalizeNative(chainId, address) {
114
- if (isEvmChain(chainId) && isNativeAddress(address)) {
145
+ if (isEvmChain2(chainId) && isNativeAddress(address)) {
115
146
  const chain = SupportedChains.find((c) => c.id === chainId);
116
147
  if (!chain?.wrapped)
117
148
  throw new Error(`Wrapped token not found for chainId ${chainId}`);
@@ -122,13 +153,14 @@ function normalizeNative(chainId, address) {
122
153
 
123
154
  // src/core/getQuote.ts
124
155
  async function getQuote(params) {
156
+ const amount = BigInt(params.amount);
125
157
  if (!params.tokenIn?.address || !params.tokenOut?.address) {
126
158
  throw new Error("Both tokenIn and tokenOut must include an address.");
127
159
  }
128
160
  if (!params.sourceChainId || !params.destChainId) {
129
161
  throw new Error("Both sourceChainId and destChainId are required.");
130
162
  }
131
- if (params.amount <= 0n) {
163
+ if (amount <= 0n) {
132
164
  throw new Error("Amount must be greater than 0.");
133
165
  }
134
166
  const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
@@ -137,24 +169,27 @@ async function getQuote(params) {
137
169
  destChainId: params.destChainId,
138
170
  tokenIn: normalizedTokenIn,
139
171
  tokenOut: params.tokenOut.address,
140
- amount: params.amount
172
+ amount
141
173
  });
142
- const inputSlippage = params.slippage ?? 5;
143
- const slippageDecimal = inputSlippage / 100;
144
- const slippage = Math.min(Math.max(slippageDecimal, 0), 0.5);
174
+ const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
145
175
  let warning;
146
- if (slippage > 0.1) {
147
- warning = `\u26A0\uFE0F High slippage tolerance (${(slippage * 100).toFixed(2)}%) \u2014 price may vary significantly.`;
176
+ if (slippagePercent > 10) {
177
+ warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
148
178
  }
149
- const estimatedAmountOut = BigInt(data.estimatedAmountOutReduced);
150
- const slippageBps = BigInt(Math.round(slippage * 1e4));
179
+ const estimatedAmountOut = BigInt(data.estimatedAmountOut);
180
+ const slippageBps = BigInt(Math.round(slippagePercent * 100));
151
181
  const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
182
+ const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
183
+ const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
184
+ const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
185
+ const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
152
186
  const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
153
187
  return {
154
- amountOut: estimatedAmountOut,
155
- amountOutUsd: data.estimatedAmountOutUsd,
188
+ amountOut: estimatedAmountOutAfterSlippage,
189
+ amountOutUsd: amountOutUsdAfterSlippage,
156
190
  amountInUsd: data.amountInUsd,
157
- minStablecoinsAmount: data.estimatedAmountInAsMinStablecoinAmount,
191
+ // Input USD stays the same
192
+ minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
158
193
  tokenIn: {
159
194
  address: params.tokenIn.address,
160
195
  decimals: params.tokenIn.decimals ?? 18,
@@ -165,12 +200,13 @@ async function getQuote(params) {
165
200
  decimals: params.tokenOut.decimals ?? 18,
166
201
  chainId: params.destChainId
167
202
  },
168
- amountIn: params.amount,
203
+ amountIn: BigInt(params.amount),
169
204
  pricePerInputToken,
170
- slippage,
205
+ slippage: slippagePercent,
171
206
  internal: {
172
207
  ...data,
173
- estimatedAmountOutReduced: estimatedAmountOutAfterSlippage
208
+ estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
209
+ estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
174
210
  },
175
211
  warning
176
212
  };
@@ -188,7 +224,7 @@ function buildQuoteParams({
188
224
  tokenOut,
189
225
  sourceChainId,
190
226
  destChainId,
191
- amount: parseUnits(amount.toString(), tokenIn.decimals ?? 18),
227
+ amount: parseUnits(amount.toString(), tokenIn.decimals ?? 18).toString(),
192
228
  slippage
193
229
  };
194
230
  }
@@ -235,107 +271,73 @@ async function getBalances(params, options) {
235
271
  const evmItems = data.evm?.items ?? [];
236
272
  const svmItems = data.svm?.items ?? [];
237
273
  const combined = [...evmItems, ...svmItems];
274
+ const filtered = combined.filter(
275
+ (b) => CURRENT_SUPPORTED.includes(b.chainId)
276
+ );
238
277
  return {
239
- results: combined,
278
+ results: filtered,
240
279
  nextCursorEvm: data.evm?.cursor ?? null,
241
280
  nextCursorSvm: data.svm?.cursor ?? null
242
281
  };
243
282
  }
244
283
 
245
- // src/core/executeOrder/execute.ts
246
- import { ChainID as ChainID4, isEvmChain as isEvmChain2 } from "@shogun-sdk/intents-sdk";
247
- import { BaseError } from "viem";
248
-
249
- // src/wallet-adapter/svm-wallet-adapter/adapter.ts
250
- import {
251
- Connection
252
- } from "@solana/web3.js";
253
- var adaptSolanaWallet = (walletAddress, chainId, rpcUrl, signAndSendTransaction) => {
254
- let _chainId = chainId;
255
- const connection = new Connection(rpcUrl, { commitment: "confirmed" });
256
- const getChainId = async () => _chainId;
257
- const sendTransaction = async (transaction) => {
258
- const txHash = await signAndSendTransaction(transaction);
259
- console.log(`\u{1F6F0} Sent transaction: ${txHash}`);
260
- const maxRetries = 20;
261
- const delayMs = 2e3;
262
- for (let attempt = 0; attempt < maxRetries; attempt++) {
263
- const res = await connection.getSignatureStatus(txHash, { searchTransactionHistory: true });
264
- if (res?.value?.confirmationStatus === "confirmed" || res?.value?.confirmationStatus === "finalized") {
265
- return txHash;
266
- }
267
- await new Promise((resolve) => setTimeout(resolve, delayMs));
268
- }
269
- throw new Error(`Transaction not confirmed after ${maxRetries * (delayMs / 1e3)}s`);
270
- };
271
- const signTypedData = async () => {
272
- throw new Error("signTypedData not implemented for Solana");
273
- };
274
- const switchChain = async (newChainId) => {
275
- _chainId = newChainId;
276
- };
284
+ // src/core/token-list.ts
285
+ import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL2 } from "@shogun-sdk/intents-sdk";
286
+ async function getTokenList(params) {
287
+ const url = new URL(`${TOKEN_SEARCH_API_BASE_URL2}/tokens/search`);
288
+ if (params.q) url.searchParams.append("q", params.q);
289
+ if (params.networkId) url.searchParams.append("networkId", String(params.networkId));
290
+ if (params.page) url.searchParams.append("page", String(params.page));
291
+ if (params.limit) url.searchParams.append("limit", String(params.limit));
292
+ const res = await fetch(url.toString(), {
293
+ signal: params.signal
294
+ });
295
+ if (!res.ok) {
296
+ throw new Error(`Failed to fetch tokens: ${res.status} ${res.statusText}`);
297
+ }
298
+ const data = await res.json();
299
+ const filteredResults = data.results.filter(
300
+ (token) => CURRENT_SUPPORTED.includes(token.chainId)
301
+ );
277
302
  return {
278
- vmType: "SVM" /* SVM */,
279
- getChainId,
280
- address: async () => walletAddress,
281
- sendTransaction,
282
- switchChain,
283
- signTypedData,
284
- rpcUrl
303
+ ...data,
304
+ results: filteredResults,
305
+ count: filteredResults.length
285
306
  };
286
- };
307
+ }
308
+
309
+ // src/core/token.ts
310
+ import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL3 } from "@shogun-sdk/intents-sdk";
311
+ async function getTokensData(addresses) {
312
+ if (!addresses?.length) return [];
313
+ const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL3}/tokens/tokens`, {
314
+ method: "POST",
315
+ headers: {
316
+ "Content-Type": "application/json",
317
+ accept: "*/*"
318
+ },
319
+ body: JSON.stringify({ addresses })
320
+ });
321
+ if (!response.ok) {
322
+ throw new Error(`Failed to fetch token data: ${response.statusText}`);
323
+ }
324
+ const data = await response.json();
325
+ const filtered = data.filter((t) => CURRENT_SUPPORTED.includes(Number(t.chainId)));
326
+ return filtered;
327
+ }
328
+
329
+ // src/core/execute/execute.ts
330
+ import { ChainID as ChainID2, isEvmChain as isEvmChain3 } from "@shogun-sdk/intents-sdk";
331
+ import "viem";
287
332
 
288
333
  // src/wallet-adapter/evm-wallet-adapter/adapter.ts
289
- import "ethers/lib/ethers.js";
290
- import { hexValue } from "ethers/lib/utils.js";
291
334
  import {
292
- custom
335
+ custom,
336
+ publicActions
293
337
  } from "viem";
294
338
  function isEVMTransaction(tx) {
295
339
  return typeof tx.from === "string";
296
340
  }
297
- var adaptEthersSigner = (signer, transport) => {
298
- const signTypedData = async (signData) => {
299
- const typedSigner = signer;
300
- return await typedSigner._signTypedData(
301
- signData.domain,
302
- signData.types,
303
- signData.value
304
- );
305
- };
306
- const sendTransaction = async (transaction) => {
307
- if (!isEVMTransaction(transaction)) {
308
- throw new Error("Expected EVMTransaction but got SolanaTransaction");
309
- }
310
- const tx = await signer.sendTransaction({
311
- from: transaction.from,
312
- to: transaction.to,
313
- data: transaction.data,
314
- value: transaction.value
315
- });
316
- return tx.hash;
317
- };
318
- const switchChain = async (chainId) => {
319
- try {
320
- await window.ethereum.request({
321
- method: "wallet_switchEthereumChain",
322
- params: [{ chainId: hexValue(chainId) }]
323
- });
324
- } catch (error) {
325
- console.error("Failed to switch chain:", error);
326
- throw error;
327
- }
328
- };
329
- return {
330
- vmType: "EVM" /* EVM */,
331
- transport,
332
- getChainId: async () => signer.getChainId(),
333
- address: async () => signer.getAddress(),
334
- sendTransaction,
335
- signTypedData,
336
- switchChain
337
- };
338
- };
339
341
  var adaptViemWallet = (wallet) => {
340
342
  const signTypedData = async (signData) => {
341
343
  return await wallet.signTypedData({
@@ -350,15 +352,26 @@ var adaptViemWallet = (wallet) => {
350
352
  if (!isEVMTransaction(transaction)) {
351
353
  throw new Error("Expected EVMTransaction but got SolanaTransaction");
352
354
  }
353
- const tx = await wallet.sendTransaction({
354
- from: transaction.from,
355
- to: transaction.to,
356
- data: transaction.data,
357
- value: transaction.value,
358
- account: wallet.account?.address,
359
- chain: wallet.chain
360
- });
361
- return tx;
355
+ if (wallet.transport.type === "http") {
356
+ const request = await wallet.prepareTransactionRequest({
357
+ to: transaction.to,
358
+ data: transaction.data,
359
+ value: transaction.value,
360
+ chain: wallet.chain
361
+ });
362
+ const serializedTransaction = await wallet.signTransaction(request);
363
+ const tx = await wallet.sendRawTransaction({ serializedTransaction });
364
+ return tx;
365
+ } else {
366
+ const hash = await wallet.sendTransaction({
367
+ to: transaction.to,
368
+ data: transaction.data,
369
+ value: transaction.value,
370
+ chain: wallet.chain,
371
+ account: wallet.account
372
+ });
373
+ return hash;
374
+ }
362
375
  };
363
376
  const switchChain = async (chainId) => {
364
377
  try {
@@ -381,6 +394,20 @@ var adaptViemWallet = (wallet) => {
381
394
  if (!addr) throw new Error("No address found");
382
395
  return addr;
383
396
  };
397
+ const readContract = async ({
398
+ address: address2,
399
+ abi,
400
+ functionName,
401
+ args = []
402
+ }) => {
403
+ const publicClient = wallet.extend(publicActions);
404
+ return await publicClient.readContract({
405
+ address: address2,
406
+ abi,
407
+ functionName,
408
+ args
409
+ });
410
+ };
384
411
  return {
385
412
  vmType: "EVM" /* EVM */,
386
413
  transport: custom(wallet.transport),
@@ -388,39 +415,65 @@ var adaptViemWallet = (wallet) => {
388
415
  address,
389
416
  sendTransaction,
390
417
  signTypedData,
391
- switchChain
418
+ switchChain,
419
+ readContract
392
420
  };
393
421
  };
394
422
 
395
- // src/core/executeOrder/handleEvmExecution.ts
423
+ // src/core/execute/handleEvmExecution.ts
396
424
  import {
397
425
  getEVMSingleChainOrderTypedData,
398
- getEVMCrossChainOrderTypedData,
399
- PERMIT2_ADDRESS
426
+ getEVMCrossChainOrderTypedData
400
427
  } from "@shogun-sdk/intents-sdk";
401
- import { encodeFunctionData, erc20Abi } from "viem";
428
+ import { encodeFunctionData as encodeFunctionData2 } from "viem";
402
429
 
403
- // src/core/executeOrder/stageMessages.ts
430
+ // src/core/execute/stageMessages.ts
404
431
  var DEFAULT_STAGE_MESSAGES = {
405
432
  processing: "Preparing transaction for execution",
406
433
  approving: "Approving token allowance",
407
434
  approved: "Token approved successfully",
408
- signing: "Signing order for submission",
409
- submitting: "Submitting order to Auctioneer",
410
- success: "Order executed successfully",
411
- error: "Order execution failed"
435
+ signing: "Signing transaction for submission",
436
+ submitting: "Submitting transaction",
437
+ initiated: "Transaction initiated.",
438
+ success: "Transaction Executed successfully",
439
+ success_limit: "Limit order has been submitted successfully.",
440
+ shogun_processing: "Shogun is processing your transaction",
441
+ error: "Transaction failed during submission"
412
442
  };
413
443
 
414
- // src/core/executeOrder/buildOrder.ts
444
+ // src/core/execute/buildOrder.ts
415
445
  import { CrossChainOrder, SingleChainOrder } from "@shogun-sdk/intents-sdk";
446
+ import { formatUnits, parseUnits as parseUnits2 } from "viem";
447
+
448
+ // src/utils/order.ts
449
+ var OrderExecutionType = /* @__PURE__ */ ((OrderExecutionType2) => {
450
+ OrderExecutionType2["LIMIT"] = "limit";
451
+ OrderExecutionType2["MARKET"] = "market";
452
+ return OrderExecutionType2;
453
+ })(OrderExecutionType || {});
454
+
455
+ // src/core/execute/buildOrder.ts
416
456
  async function buildOrder({
417
457
  quote,
418
458
  accountAddress,
419
459
  destination,
420
460
  deadline,
421
- isSingleChain
461
+ isSingleChain,
462
+ orderType,
463
+ options
422
464
  }) {
423
465
  const { tokenIn, tokenOut } = quote;
466
+ let amountOutMin = BigInt(quote.internal.estimatedAmountOutReduced);
467
+ if (orderType === "limit" /* LIMIT */ && options && "executionPrice" in options) {
468
+ const executionPrice = Number(options.executionPrice);
469
+ if (Number.isFinite(executionPrice) && executionPrice > 0) {
470
+ const decimalsIn = tokenIn.decimals ?? 18;
471
+ const decimalsOut = tokenOut.decimals ?? 18;
472
+ const formattedAmountIn = Number(formatUnits(BigInt(quote.amountIn.toString()), decimalsIn));
473
+ const rawAmountOut = formattedAmountIn * executionPrice;
474
+ amountOutMin = parseUnits2(rawAmountOut.toString(), decimalsOut);
475
+ }
476
+ }
424
477
  if (isSingleChain) {
425
478
  return await SingleChainOrder.create({
426
479
  user: accountAddress,
@@ -428,7 +481,7 @@ async function buildOrder({
428
481
  tokenIn: tokenIn.address,
429
482
  tokenOut: tokenOut.address,
430
483
  amountIn: quote.amountIn,
431
- amountOutMin: quote.internal.estimatedAmountOutReduced,
484
+ amountOutMin,
432
485
  deadline,
433
486
  destinationAddress: destination
434
487
  });
@@ -442,12 +495,161 @@ async function buildOrder({
442
495
  destinationTokenAddress: tokenOut.address,
443
496
  destinationAddress: destination,
444
497
  deadline,
445
- destinationTokenMinAmount: quote.internal.estimatedAmountOutReduced,
498
+ destinationTokenMinAmount: amountOutMin,
446
499
  minStablecoinAmount: quote.minStablecoinsAmount
447
500
  });
448
501
  }
449
502
 
450
- // src/core/executeOrder/handleEvmExecution.ts
503
+ // src/utils/pollOrderStatus.ts
504
+ import { AUCTIONEER_URL } from "@shogun-sdk/intents-sdk";
505
+ async function pollOrderStatus(address, orderId, options = {}) {
506
+ const { intervalMs = 2e3, timeoutMs = 3e5 } = options;
507
+ const startTime = Date.now();
508
+ const isDebug = process.env.NODE_ENV !== "production";
509
+ const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(address);
510
+ const isSuiAddress = /^0x[a-fA-F0-9]{64}$/.test(address);
511
+ const isSolanaAddress = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
512
+ let queryParam;
513
+ if (isEvmAddress) queryParam = `evmWallets=${address}`;
514
+ else if (isSuiAddress) queryParam = `suiWallets=${address}`;
515
+ else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
516
+ else throw new Error(`Unrecognized wallet address format: ${address}`);
517
+ const queryUrl = `${AUCTIONEER_URL}/user_intent?${queryParam}`;
518
+ return new Promise((resolve, reject) => {
519
+ const pollInterval = setInterval(async () => {
520
+ try {
521
+ if (Date.now() - startTime > timeoutMs) {
522
+ clearInterval(pollInterval);
523
+ return resolve("Timeout");
524
+ }
525
+ const res = await fetch(queryUrl, {
526
+ method: "GET",
527
+ headers: { "Content-Type": "application/json" }
528
+ });
529
+ if (!res.ok) {
530
+ clearInterval(pollInterval);
531
+ return reject(
532
+ new Error(`Failed to fetch orders: ${res.status} ${res.statusText}`)
533
+ );
534
+ }
535
+ const json = await res.json();
536
+ const data = json?.data ?? {};
537
+ const allOrders = [
538
+ ...data.crossChainDcaOrders ?? [],
539
+ ...data.crossChainLimitOrders ?? [],
540
+ ...data.singleChainDcaOrders ?? [],
541
+ ...data.singleChainLimitOrders ?? []
542
+ ];
543
+ const targetOrder = allOrders.find((o) => o.orderId === orderId);
544
+ if (!targetOrder) {
545
+ if (isDebug)
546
+ console.debug(`[pollOrderStatus] [${orderId}] Not found yet`);
547
+ return;
548
+ }
549
+ const { orderStatus } = targetOrder;
550
+ if (isDebug) {
551
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
552
+ console.debug(`targetOrder`, targetOrder);
553
+ console.debug(
554
+ `[pollOrderStatus] [${orderId}] status=${orderStatus} (elapsed ${elapsed}s)`
555
+ );
556
+ }
557
+ if (["Fulfilled", "Cancelled", "Outdated"].includes(orderStatus)) {
558
+ clearInterval(pollInterval);
559
+ return resolve(orderStatus);
560
+ }
561
+ } catch (error) {
562
+ clearInterval(pollInterval);
563
+ return reject(error);
564
+ }
565
+ }, intervalMs);
566
+ });
567
+ }
568
+
569
+ // src/core/execute/handleOrderPollingResult.ts
570
+ async function handleOrderPollingResult({
571
+ status,
572
+ orderId,
573
+ chainId,
574
+ update,
575
+ messageFor
576
+ }) {
577
+ switch (status) {
578
+ case "Fulfilled":
579
+ update("success", messageFor("success"));
580
+ return {
581
+ status: true,
582
+ orderId,
583
+ chainId,
584
+ finalStatus: status,
585
+ stage: "success"
586
+ };
587
+ case "Cancelled":
588
+ update("error", "Order was cancelled before fulfillment");
589
+ break;
590
+ case "Timeout":
591
+ update("error", "Order polling timed out");
592
+ break;
593
+ case "NotFound":
594
+ default:
595
+ update("error", "Order not found");
596
+ break;
597
+ }
598
+ return {
599
+ status: false,
600
+ orderId,
601
+ chainId,
602
+ finalStatus: status,
603
+ stage: "error"
604
+ };
605
+ }
606
+
607
+ // src/core/execute/ensurePermit2Allowance.ts
608
+ import { encodeFunctionData, erc20Abi, maxUint256 } from "viem";
609
+ import { PERMIT2_ADDRESS } from "@shogun-sdk/intents-sdk";
610
+ async function ensurePermit2Allowance({
611
+ chainId,
612
+ tokenIn,
613
+ wallet,
614
+ accountAddress,
615
+ requiredAmount,
616
+ increaseByDelta = false
617
+ }) {
618
+ const spender = PERMIT2_ADDRESS[chainId];
619
+ let currentAllowance = 0n;
620
+ try {
621
+ if (!wallet.readContract) {
622
+ throw new Error("Wallet does not implement readContract()");
623
+ }
624
+ currentAllowance = await wallet.readContract({
625
+ address: tokenIn,
626
+ abi: erc20Abi,
627
+ functionName: "allowance",
628
+ args: [accountAddress, spender]
629
+ });
630
+ } catch (error) {
631
+ console.warn(`[Permit2] Failed to read allowance for ${tokenIn}`, error);
632
+ }
633
+ const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : maxUint256;
634
+ console.debug(
635
+ `[Permit2] Approving ${approvalAmount} for ${tokenIn} (current: ${currentAllowance}, required: ${requiredAmount})`
636
+ );
637
+ await wallet.sendTransaction({
638
+ to: tokenIn,
639
+ from: accountAddress,
640
+ data: encodeFunctionData({
641
+ abi: erc20Abi,
642
+ functionName: "approve",
643
+ args: [spender, approvalAmount]
644
+ }),
645
+ value: 0n
646
+ });
647
+ console.info(
648
+ `[Permit2] Approval transaction sent for ${tokenIn} on chain ${chainId}`
649
+ );
650
+ }
651
+
652
+ // src/core/execute/handleEvmExecution.ts
451
653
  async function handleEvmExecution({
452
654
  recipientAddress,
453
655
  quote,
@@ -455,18 +657,22 @@ async function handleEvmExecution({
455
657
  accountAddress,
456
658
  wallet,
457
659
  isSingleChain,
458
- deadline,
459
- update
660
+ update,
661
+ orderType,
662
+ options
460
663
  }) {
461
- const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
664
+ const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
665
+ const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
462
666
  await wallet.switchChain(chainId);
463
667
  const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
668
+ quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
464
669
  const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
465
670
  update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
466
671
  if (shouldWrapNative) {
672
+ quote.tokenIn.address === tokenIn;
467
673
  await wallet.sendTransaction({
468
674
  to: tokenIn,
469
- data: encodeFunctionData({
675
+ data: encodeFunctionData2({
470
676
  abi: [{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] }],
471
677
  functionName: "deposit",
472
678
  args: []
@@ -475,48 +681,74 @@ async function handleEvmExecution({
475
681
  from: accountAddress
476
682
  });
477
683
  }
478
- update("approving", messageFor("approving"));
479
- await wallet.sendTransaction({
480
- to: tokenIn,
481
- data: encodeFunctionData({
482
- abi: erc20Abi,
483
- functionName: "approve",
484
- args: [PERMIT2_ADDRESS[chainId], BigInt(quote.amountIn)]
485
- }),
486
- value: 0n,
487
- from: accountAddress
684
+ update("processing", messageFor("approving"));
685
+ await ensurePermit2Allowance({
686
+ chainId,
687
+ tokenIn,
688
+ wallet,
689
+ accountAddress,
690
+ requiredAmount: BigInt(quote.amountIn)
488
691
  });
489
- update("approved", messageFor("approved"));
692
+ update("processing", messageFor("approved"));
490
693
  const destination = recipientAddress ?? accountAddress;
491
694
  const order = await buildOrder({
492
695
  quote,
493
696
  accountAddress,
494
697
  destination,
495
698
  deadline,
496
- isSingleChain
699
+ isSingleChain,
700
+ orderType,
701
+ options
497
702
  });
498
- update("signing", messageFor("signing"));
703
+ console.debug(`order`, order);
704
+ update("processing", messageFor("signing"));
499
705
  const { orderTypedData, nonce } = isSingleChain ? await getEVMSingleChainOrderTypedData(order) : await getEVMCrossChainOrderTypedData(order);
706
+ const typedData = serializeBigIntsToStrings(orderTypedData);
500
707
  if (!wallet.signTypedData) {
501
708
  throw new Error("Wallet does not support EIP-712 signing");
502
709
  }
503
- const signature = await wallet.signTypedData(serializeBigIntsToStrings(orderTypedData));
504
- update("submitting", messageFor("submitting"));
710
+ const signature = await wallet.signTypedData({
711
+ domain: typedData.domain,
712
+ types: typedData.types,
713
+ primaryType: typedData.primaryType,
714
+ value: typedData.message,
715
+ message: typedData.message
716
+ });
717
+ update("processing", messageFor("submitting"));
505
718
  const res = await order.sendToAuctioneer({ signature, nonce: nonce.toString() });
506
719
  if (!res.success) {
507
720
  throw new Error("Auctioneer submission failed");
508
721
  }
509
- update("success", messageFor("success"));
510
- return { status: true, txHash: res.data, chainId, stage: "success" };
722
+ update("initiated", messageFor("initiated"));
723
+ const { intentId: orderId } = res.data;
724
+ update("initiated", messageFor("shogun_processing"));
725
+ if (orderType === "limit" /* LIMIT */) {
726
+ update("success", messageFor("success_limit"));
727
+ return {
728
+ status: true,
729
+ orderId,
730
+ chainId,
731
+ finalStatus: "OrderPlaced",
732
+ stage: "success"
733
+ };
734
+ } else {
735
+ const status = await pollOrderStatus(accountAddress, orderId);
736
+ return await handleOrderPollingResult({
737
+ status,
738
+ orderId,
739
+ chainId,
740
+ update,
741
+ messageFor
742
+ });
743
+ }
511
744
  }
512
745
 
513
- // src/core/executeOrder/handleSolanaExecution.ts
746
+ // src/core/execute/handleSolanaExecution.ts
514
747
  import {
515
- ChainID as ChainID3,
516
748
  getSolanaSingleChainOrderInstructions,
517
749
  getSolanaCrossChainOrderInstructions
518
750
  } from "@shogun-sdk/intents-sdk";
519
- import { VersionedTransaction as VersionedTransaction2 } from "@solana/web3.js";
751
+ import { VersionedTransaction } from "@solana/web3.js";
520
752
  async function handleSolanaExecution({
521
753
  recipientAddress,
522
754
  quote,
@@ -524,12 +756,14 @@ async function handleSolanaExecution({
524
756
  isSingleChain,
525
757
  update,
526
758
  accountAddress,
527
- deadline
759
+ orderType,
760
+ options
528
761
  }) {
529
762
  if (!wallet.rpcUrl) {
530
763
  throw new Error("Solana wallet is missing rpcUrl");
531
764
  }
532
- const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
765
+ const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
766
+ const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
533
767
  update("processing", messageFor("processing"));
534
768
  const destination = recipientAddress ?? accountAddress;
535
769
  const order = await buildOrder({
@@ -537,18 +771,19 @@ async function handleSolanaExecution({
537
771
  accountAddress,
538
772
  destination,
539
773
  deadline,
540
- isSingleChain
774
+ isSingleChain,
775
+ orderType,
776
+ options
541
777
  });
542
778
  const txData = await getSolanaOrderInstructions({
543
779
  order,
544
780
  isSingleChain,
545
781
  rpcUrl: wallet.rpcUrl
546
782
  });
547
- const transaction = VersionedTransaction2.deserialize(Uint8Array.from(txData.txBytes));
548
- update("signing", messageFor("signing"));
549
- console.log({ order });
550
- const txSignature = await wallet.sendTransaction(transaction);
551
- update("submitting", messageFor("submitting"));
783
+ const transaction = VersionedTransaction.deserialize(Uint8Array.from(txData.txBytes));
784
+ update("processing", messageFor("signing"));
785
+ await wallet.sendTransaction(transaction);
786
+ update("processing", messageFor("submitting"));
552
787
  const response = await submitToAuctioneer({
553
788
  order,
554
789
  isSingleChain,
@@ -557,13 +792,28 @@ async function handleSolanaExecution({
557
792
  if (!response.success) {
558
793
  throw new Error("Auctioneer submission failed");
559
794
  }
560
- update("success", messageFor("success"));
561
- return {
562
- status: true,
563
- txHash: txSignature,
564
- chainId: ChainID3.Solana,
565
- stage: "success"
566
- };
795
+ update("initiated", messageFor("initiated"));
796
+ const { intentId: orderId } = response.data;
797
+ update("initiated", messageFor("shogun_processing"));
798
+ if (orderType === "limit" /* LIMIT */) {
799
+ update("success", messageFor("success_limit"));
800
+ return {
801
+ status: true,
802
+ orderId,
803
+ chainId: SOLANA_CHAIN_ID,
804
+ finalStatus: "OrderPlaced",
805
+ stage: "success"
806
+ };
807
+ } else {
808
+ const status = await pollOrderStatus(accountAddress, orderId);
809
+ return await handleOrderPollingResult({
810
+ status,
811
+ orderId,
812
+ chainId: SOLANA_CHAIN_ID,
813
+ update,
814
+ messageFor
815
+ });
816
+ }
567
817
  }
568
818
  async function getSolanaOrderInstructions({
569
819
  order,
@@ -596,52 +846,93 @@ async function submitToAuctioneer({
596
846
  });
597
847
  }
598
848
 
599
- // src/core/executeOrder/execute.ts
849
+ // src/core/execute/execute.ts
600
850
  async function executeOrder({
601
851
  quote,
602
852
  accountAddress,
603
853
  recipientAddress,
604
854
  wallet,
605
855
  onStatus,
856
+ orderType = "market" /* MARKET */,
606
857
  options = {}
607
858
  }) {
608
- const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
859
+ const isDev = process.env.NODE_ENV !== "production";
860
+ const log = (...args) => {
861
+ if (isDev) console.debug("[OneShot::executeOrder]", ...args);
862
+ };
609
863
  const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
610
- const update = (stage, message) => onStatus?.(stage, message ?? messageFor(stage));
864
+ const update = (stage, message) => {
865
+ log("Stage:", stage, "| Message:", message ?? messageFor(stage));
866
+ onStatus?.(stage, message ?? messageFor(stage));
867
+ };
611
868
  try {
869
+ log("Starting execution:", {
870
+ accountAddress,
871
+ recipientAddress,
872
+ tokenIn: quote?.tokenIn,
873
+ tokenOut: quote?.tokenOut
874
+ });
612
875
  const adapter = normalizeWallet(wallet);
613
876
  if (!adapter) throw new Error("No wallet provided");
614
877
  const { tokenIn, tokenOut } = quote;
878
+ const srcChain = Number(tokenIn.chainId);
879
+ const destChain = Number(tokenOut.chainId);
880
+ if (!CURRENT_SUPPORTED.includes(srcChain) || !CURRENT_SUPPORTED.includes(destChain)) {
881
+ const unsupportedChains = [
882
+ !CURRENT_SUPPORTED.includes(srcChain) ? srcChain : null,
883
+ !CURRENT_SUPPORTED.includes(destChain) ? destChain : null
884
+ ].filter(Boolean).join(", ");
885
+ const errorMsg = `Unsupported chain(s): ${unsupportedChains}`;
886
+ update("error", errorMsg);
887
+ log("Error:", errorMsg);
888
+ throw new Error(errorMsg);
889
+ }
615
890
  const isSingleChain = tokenIn.chainId === tokenOut.chainId;
616
891
  const chainId = Number(tokenIn.chainId);
617
- update("processing", messageFor("processing"));
618
- if (isEvmChain2(chainId)) {
619
- return await handleEvmExecution({
892
+ update("processing");
893
+ if (isEvmChain3(chainId)) {
894
+ log("Detected EVM chain:", chainId);
895
+ const result = await handleEvmExecution({
620
896
  recipientAddress,
621
897
  quote,
622
898
  chainId,
623
899
  accountAddress,
624
900
  wallet: adapter,
625
901
  isSingleChain,
626
- deadline,
627
- update
902
+ update,
903
+ orderType,
904
+ options
628
905
  });
906
+ log("EVM execution result:", result);
907
+ return result;
629
908
  }
630
- if (chainId === ChainID4.Solana) {
631
- return await handleSolanaExecution({
909
+ if (chainId === ChainID2.Solana) {
910
+ log("Detected Solana chain");
911
+ const result = await handleSolanaExecution({
632
912
  recipientAddress,
633
913
  quote,
634
914
  accountAddress,
635
915
  wallet: adapter,
636
916
  isSingleChain,
637
- deadline,
638
- update
917
+ update,
918
+ orderType,
919
+ options
639
920
  });
921
+ log("Solana execution result:", result);
922
+ return result;
640
923
  }
641
- update("error", "Unsupported chain");
642
- return { status: false, message: "Unsupported chain", stage: "error" };
924
+ const unsupported = `Unsupported chain: ${chainId}`;
925
+ update("error", unsupported);
926
+ log("Error:", unsupported);
927
+ return { status: false, message: unsupported, stage: "error" };
643
928
  } catch (error) {
644
- const message = error instanceof BaseError ? error.shortMessage : error instanceof Error ? error.message : String(error);
929
+ let message = "An unknown error occurred";
930
+ if (error && typeof error === "object") {
931
+ const err = error;
932
+ message = err.details ?? err.message ?? message;
933
+ } else if (typeof error === "string") {
934
+ message = error;
935
+ }
645
936
  update("error", message);
646
937
  return { status: false, message, stage: "error" };
647
938
  }
@@ -652,317 +943,502 @@ function normalizeWallet(wallet) {
652
943
  return wallet;
653
944
  }
654
945
 
655
- // src/react/useTokenList.ts
656
- import { useEffect, useMemo, useRef, useState } from "react";
657
- var tokenCache = /* @__PURE__ */ new Map();
658
- function useTokenList(params) {
659
- const [data, setData] = useState(null);
660
- const [loading, setLoading] = useState(false);
661
- const [error, setError] = useState(null);
662
- const controllerRef = useRef(null);
663
- const debounceRef = useRef(null);
664
- const debounceMs = params.debounceMs ?? 250;
665
- const cacheKey = useMemo(() => {
666
- return JSON.stringify({
667
- q: params.q?.trim().toLowerCase() ?? "",
668
- networkId: params.networkId ?? "all",
669
- page: params.page ?? 1,
670
- limit: params.limit ?? 50
671
- });
672
- }, [params.q, params.networkId, params.page, params.limit]);
673
- async function fetchTokens(signal) {
674
- if (tokenCache.has(cacheKey)) {
675
- setData(tokenCache.get(cacheKey));
676
- setLoading(false);
677
- setError(null);
678
- return;
946
+ // src/core/orders/getOrders.ts
947
+ import { fetchUserOrders } from "@shogun-sdk/intents-sdk";
948
+ async function getOrders({
949
+ evmAddress,
950
+ solAddress
951
+ }) {
952
+ if (!evmAddress && !solAddress) {
953
+ throw new Error("At least one wallet address (EVM, Solana) must be provided.");
954
+ }
955
+ const orders = await fetchUserOrders(evmAddress, solAddress);
956
+ return orders;
957
+ }
958
+
959
+ // src/core/orders/cancelOrder.ts
960
+ import {
961
+ cancelCrossChainOrderInstructionsAsBytes,
962
+ cancelSingleChainOrderInstructionsAsBytes,
963
+ ChainID as ChainID3,
964
+ CROSS_CHAIN_GUARD_ADDRESSES,
965
+ PERMIT2_ADDRESS as PERMIT2_ADDRESS2,
966
+ SINGLE_CHAIN_GUARD_ADDRESSES
967
+ } from "@shogun-sdk/intents-sdk";
968
+ import {
969
+ VersionedMessage,
970
+ VersionedTransaction as VersionedTransaction2
971
+ } from "@solana/web3.js";
972
+ import { encodeFunctionData as encodeFunctionData3 } from "viem";
973
+ async function cancelIntentsOrder({
974
+ order,
975
+ wallet,
976
+ sol_rpc
977
+ }) {
978
+ const isCrossChain = "srcChainId" in order && "destChainId" in order && order.srcChainId !== order.destChainId;
979
+ const srcChain = "srcChainId" in order ? order.srcChainId : order.chainId;
980
+ const isSolanaOrder = srcChain === ChainID3.Solana;
981
+ if (isSolanaOrder) {
982
+ if (!isCrossChain) {
983
+ const { versionedMessageBytes: versionedMessageBytes2 } = await cancelSingleChainOrderInstructionsAsBytes(
984
+ order.orderId,
985
+ order.user,
986
+ { rpcUrl: sol_rpc }
987
+ );
988
+ const message2 = VersionedMessage.deserialize(
989
+ versionedMessageBytes2
990
+ );
991
+ const tx2 = new VersionedTransaction2(message2);
992
+ return await wallet.sendTransaction(tx2);
679
993
  }
680
- try {
681
- setLoading(true);
682
- const result = await getTokenList({ ...params, signal });
683
- tokenCache.set(cacheKey, result);
684
- setData(result);
685
- setError(null);
686
- } catch (err) {
687
- if (err.name !== "AbortError") {
688
- const e = err instanceof Error ? err : new Error("Unknown error while fetching tokens");
689
- setError(e);
994
+ const { versionedMessageBytes } = await cancelCrossChainOrderInstructionsAsBytes(
995
+ order.orderId,
996
+ order.user,
997
+ { rpcUrl: sol_rpc }
998
+ );
999
+ const message = VersionedMessage.deserialize(
1000
+ versionedMessageBytes
1001
+ );
1002
+ const tx = new VersionedTransaction2(message);
1003
+ return await wallet.sendTransaction(tx);
1004
+ }
1005
+ const chainId = srcChain;
1006
+ const { readContract } = wallet;
1007
+ if (!readContract) {
1008
+ throw new Error("Wallet does not support readContract");
1009
+ }
1010
+ const nonce = BigInt(order.nonce ?? "0");
1011
+ const nonceWordPos = nonce >> 8n;
1012
+ const nonceBitPos = nonce - nonceWordPos * 256n;
1013
+ if (isCrossChain) {
1014
+ const [orderData = [false, false], currentNonceBitmap = 0n] = await Promise.all([
1015
+ readContract({
1016
+ address: CROSS_CHAIN_GUARD_ADDRESSES[chainId],
1017
+ abi: [
1018
+ {
1019
+ inputs: [{ name: "orderId", type: "bytes32" }],
1020
+ name: "orderData",
1021
+ outputs: [
1022
+ { name: "initialized", type: "bool" },
1023
+ { name: "deactivated", type: "bool" }
1024
+ ],
1025
+ stateMutability: "view",
1026
+ type: "function"
1027
+ }
1028
+ ],
1029
+ functionName: "orderData",
1030
+ args: [order.orderId]
1031
+ }),
1032
+ readContract({
1033
+ address: PERMIT2_ADDRESS2[chainId],
1034
+ abi: [
1035
+ {
1036
+ inputs: [
1037
+ { name: "owner", type: "address" },
1038
+ { name: "wordPos", type: "uint256" }
1039
+ ],
1040
+ name: "nonceBitmap",
1041
+ outputs: [{ name: "", type: "uint256" }],
1042
+ stateMutability: "view",
1043
+ type: "function"
1044
+ }
1045
+ ],
1046
+ functionName: "nonceBitmap",
1047
+ args: [order.user, nonceWordPos]
1048
+ })
1049
+ ]);
1050
+ const [initialized, deactivated] = orderData;
1051
+ if (initialized) {
1052
+ if (deactivated) {
1053
+ throw new Error("Order is already deactivated");
690
1054
  }
691
- } finally {
692
- setLoading(false);
1055
+ return await wallet.sendTransaction({
1056
+ to: CROSS_CHAIN_GUARD_ADDRESSES[order.srcChainId],
1057
+ data: encodeFunctionData3({
1058
+ abi: [
1059
+ {
1060
+ inputs: [
1061
+ {
1062
+ components: [
1063
+ { name: "user", type: "address" },
1064
+ { name: "tokenIn", type: "address" },
1065
+ { name: "srcChainId", type: "uint256" },
1066
+ { name: "deadline", type: "uint256" },
1067
+ { name: "amountIn", type: "uint256" },
1068
+ { name: "minStablecoinsAmount", type: "uint256" },
1069
+ { name: "executionDetailsHash", type: "bytes32" },
1070
+ { name: "nonce", type: "uint256" }
1071
+ ],
1072
+ name: "orderInfo",
1073
+ type: "tuple"
1074
+ }
1075
+ ],
1076
+ name: "cancelOrder",
1077
+ outputs: [],
1078
+ stateMutability: "nonpayable",
1079
+ type: "function"
1080
+ }
1081
+ ],
1082
+ functionName: "cancelOrder",
1083
+ args: [
1084
+ {
1085
+ user: order.user,
1086
+ tokenIn: order.tokenIn,
1087
+ srcChainId: BigInt(order.srcChainId),
1088
+ deadline: BigInt(order.deadline),
1089
+ amountIn: BigInt(order.amountIn),
1090
+ minStablecoinsAmount: BigInt(order.minStablecoinsAmount),
1091
+ executionDetailsHash: order.executionDetailsHash,
1092
+ nonce
1093
+ }
1094
+ ]
1095
+ }),
1096
+ value: BigInt(0),
1097
+ from: order.user
1098
+ });
1099
+ } else {
1100
+ if ((currentNonceBitmap & 1n << nonceBitPos) !== 0n) {
1101
+ throw new Error("Nonce is already invalidated");
1102
+ }
1103
+ const mask2 = 1n << nonceBitPos;
1104
+ return await wallet.sendTransaction({
1105
+ to: PERMIT2_ADDRESS2[order.srcChainId],
1106
+ data: encodeFunctionData3({
1107
+ abi: [
1108
+ {
1109
+ inputs: [
1110
+ { name: "wordPos", type: "uint256" },
1111
+ { name: "mask", type: "uint256" }
1112
+ ],
1113
+ name: "invalidateUnorderedNonces",
1114
+ outputs: [],
1115
+ stateMutability: "nonpayable",
1116
+ type: "function"
1117
+ }
1118
+ ],
1119
+ functionName: "invalidateUnorderedNonces",
1120
+ args: [nonceWordPos, mask2]
1121
+ }),
1122
+ value: BigInt(0),
1123
+ from: order.user
1124
+ });
693
1125
  }
694
1126
  }
695
- useEffect(() => {
696
- if (!params.q && !params.networkId) return;
697
- if (debounceRef.current) clearTimeout(debounceRef.current);
698
- if (controllerRef.current) controllerRef.current.abort();
699
- const controller = new AbortController();
700
- controllerRef.current = controller;
701
- debounceRef.current = setTimeout(() => {
702
- fetchTokens(controller.signal);
703
- }, debounceMs);
704
- return () => {
705
- controller.abort();
706
- if (debounceRef.current) clearTimeout(debounceRef.current);
707
- };
708
- }, [cacheKey, debounceMs]);
709
- return useMemo(
710
- () => ({
711
- /** Current fetched data (cached when possible) */
712
- data,
713
- /** Whether a request is in progress */
714
- loading,
715
- /** Error object if a request failed */
716
- error,
717
- /** Manually refetch the token list */
718
- refetch: () => fetchTokens(),
719
- /** Clear all cached token results (shared across hook instances) */
720
- clearCache: () => tokenCache.clear()
1127
+ const [wasManuallyInitialized, currentBitmap = 0n] = await Promise.all([
1128
+ readContract({
1129
+ address: SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
1130
+ abi: [
1131
+ {
1132
+ inputs: [{ name: "orderHash", type: "bytes32" }],
1133
+ name: "orderManuallyInitialized",
1134
+ outputs: [{ name: "", type: "bool" }],
1135
+ stateMutability: "view",
1136
+ type: "function"
1137
+ }
1138
+ ],
1139
+ functionName: "orderManuallyInitialized",
1140
+ args: [order.orderId]
721
1141
  }),
722
- [data, loading, error]
723
- );
724
- }
725
-
726
- // src/react/useExecuteOrder.ts
727
- import { useState as useState2, useCallback, useRef as useRef2, useEffect as useEffect2 } from "react";
728
- function useExecuteOrder() {
729
- const [status, setStatus] = useState2("processing");
730
- const [message, setMessage] = useState2(null);
731
- const [loading, setLoading] = useState2(false);
732
- const [data, setData] = useState2(null);
733
- const [error, setError] = useState2(null);
734
- const isMounted = useRef2(true);
735
- useEffect2(() => {
736
- return () => {
737
- isMounted.current = false;
738
- };
739
- }, []);
740
- const execute = useCallback(
741
- async ({
742
- quote,
743
- accountAddress,
744
- recipientAddress,
745
- wallet,
746
- deadline
747
- }) => {
748
- if (!quote || !wallet) {
749
- throw new Error("Quote and wallet are required for order execution.");
750
- }
751
- setLoading(true);
752
- setError(null);
753
- setData(null);
754
- setMessage(null);
755
- try {
756
- const effectiveDeadline = deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
757
- const onStatus = (stage, msg) => {
758
- if (!isMounted.current) return;
759
- setStatus(stage);
760
- if (msg) setMessage(msg);
761
- };
762
- const result = await executeOrder({
763
- quote,
764
- accountAddress,
765
- recipientAddress,
766
- wallet,
767
- onStatus,
768
- options: { deadline: effectiveDeadline }
769
- });
770
- if (!isMounted.current) return result;
771
- setData(result);
772
- setStatus(result.stage);
773
- setMessage("Order executed successfully");
774
- return result;
775
- } catch (err) {
776
- const errorObj = err instanceof Error ? err : new Error(String(err));
777
- if (isMounted.current) {
778
- setError(errorObj);
779
- setStatus("error");
780
- setMessage(errorObj.message);
781
- setData({
782
- status: false,
783
- stage: "error",
784
- message: errorObj.message
785
- });
1142
+ readContract({
1143
+ address: PERMIT2_ADDRESS2[chainId],
1144
+ abi: [
1145
+ {
1146
+ inputs: [
1147
+ { name: "owner", type: "address" },
1148
+ { name: "wordPos", type: "uint256" }
1149
+ ],
1150
+ name: "nonceBitmap",
1151
+ outputs: [{ name: "", type: "uint256" }],
1152
+ stateMutability: "view",
1153
+ type: "function"
786
1154
  }
787
- return {
788
- status: false,
789
- stage: "error",
790
- message: errorObj.message
791
- };
792
- } finally {
793
- if (isMounted.current) setLoading(false);
794
- }
795
- },
796
- []
797
- );
798
- return {
799
- /** Executes the swap order. */
800
- execute,
801
- /** Current execution stage. */
802
- status,
803
- /** Human-readable status message. */
804
- message,
805
- /** Whether execution is ongoing. */
806
- loading,
807
- /** Raw SDK response data. */
808
- data,
809
- /** Captured error (if execution failed). */
810
- error
811
- };
812
- }
813
-
814
- // src/react/useQuote.ts
815
- import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
816
- function useQuote(params, options) {
817
- const [data, setData] = useState3(null);
818
- const [loading, setLoading] = useState3(false);
819
- const [error, setError] = useState3(null);
820
- const [warning, setWarning] = useState3(null);
821
- const debounceMs = options?.debounceMs ?? 250;
822
- const autoRefreshMs = options?.autoRefreshMs;
823
- const abortRef = useRef3(null);
824
- const debounceRef = useRef3(null);
825
- const mounted = useRef3(false);
826
- useEffect3(() => {
827
- mounted.current = true;
828
- return () => {
829
- mounted.current = false;
830
- abortRef.current?.abort();
831
- if (debounceRef.current) clearTimeout(debounceRef.current);
832
- };
833
- }, []);
834
- const fetchQuote = useCallback2(
835
- async () => {
836
- if (!params) return;
837
- try {
838
- setLoading(true);
839
- setWarning(null);
840
- const result = await getQuote(params);
841
- if (!mounted.current) return;
842
- setData((prev) => {
843
- if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
844
- return result;
845
- });
846
- setWarning(result.warning ?? null);
847
- setError(null);
848
- } catch (err) {
849
- if (err.name === "AbortError") return;
850
- console.error("[useQuote] fetch error:", err);
851
- if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
852
- } finally {
853
- if (mounted.current) setLoading(false);
854
- }
855
- },
856
- [params]
857
- );
858
- useEffect3(() => {
859
- if (!params) return;
860
- if (debounceRef.current) clearTimeout(debounceRef.current);
861
- debounceRef.current = setTimeout(() => {
862
- fetchQuote();
863
- }, debounceMs);
864
- return () => {
865
- if (debounceRef.current) clearTimeout(debounceRef.current);
866
- abortRef.current?.abort();
867
- };
868
- }, [params, debounceMs, fetchQuote]);
869
- useEffect3(() => {
870
- if (!autoRefreshMs || !params) return;
871
- const interval = setInterval(() => fetchQuote(), autoRefreshMs);
872
- return () => clearInterval(interval);
873
- }, [autoRefreshMs, params, fetchQuote]);
874
- return useMemo2(
875
- () => ({
876
- data,
877
- loading,
878
- error,
879
- warning,
880
- refetch: () => fetchQuote()
1155
+ ],
1156
+ functionName: "nonceBitmap",
1157
+ args: [order.user, nonceWordPos]
1158
+ })
1159
+ ]);
1160
+ if (wasManuallyInitialized) {
1161
+ return await wallet.sendTransaction({
1162
+ to: SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
1163
+ data: encodeFunctionData3({
1164
+ abi: [
1165
+ {
1166
+ inputs: [
1167
+ {
1168
+ name: "order",
1169
+ type: "tuple",
1170
+ components: [
1171
+ { name: "amountIn", type: "uint256" },
1172
+ { name: "tokenIn", type: "address" },
1173
+ { name: "deadline", type: "uint256" },
1174
+ { name: "nonce", type: "uint256" },
1175
+ { name: "encodedExternalCallData", type: "bytes" },
1176
+ {
1177
+ name: "extraTransfers",
1178
+ type: "tuple[]",
1179
+ components: [
1180
+ { name: "amount", type: "uint256" },
1181
+ { name: "receiver", type: "address" },
1182
+ { name: "token", type: "address" }
1183
+ ]
1184
+ },
1185
+ {
1186
+ name: "requestedOutput",
1187
+ type: "tuple",
1188
+ components: [
1189
+ { name: "amount", type: "uint256" },
1190
+ { name: "receiver", type: "address" },
1191
+ { name: "token", type: "address" }
1192
+ ]
1193
+ },
1194
+ { name: "user", type: "address" }
1195
+ ]
1196
+ }
1197
+ ],
1198
+ name: "cancelManuallyCreatedOrder",
1199
+ outputs: [],
1200
+ stateMutability: "nonpayable",
1201
+ type: "function"
1202
+ }
1203
+ ],
1204
+ functionName: "cancelManuallyCreatedOrder",
1205
+ args: [
1206
+ {
1207
+ amountIn: BigInt(order.amountIn),
1208
+ tokenIn: order.tokenIn,
1209
+ deadline: BigInt(order.deadline),
1210
+ nonce,
1211
+ encodedExternalCallData: "0x",
1212
+ extraTransfers: (order.extraTransfers || []).map((t) => ({
1213
+ amount: BigInt(t.amount),
1214
+ receiver: t.receiver,
1215
+ token: t.token
1216
+ })),
1217
+ requestedOutput: {
1218
+ amount: BigInt(order.amountOutMin),
1219
+ receiver: order.destinationAddress,
1220
+ token: order.tokenOut
1221
+ },
1222
+ user: order.user
1223
+ }
1224
+ ]
1225
+ }),
1226
+ value: 0n,
1227
+ from: order.user
1228
+ });
1229
+ }
1230
+ const mask = 1n << nonceBitPos;
1231
+ if ((currentBitmap & mask) !== 0n) {
1232
+ throw new Error("Nonce is already invalidated");
1233
+ }
1234
+ return await wallet.sendTransaction({
1235
+ to: PERMIT2_ADDRESS2[chainId],
1236
+ data: encodeFunctionData3({
1237
+ abi: [
1238
+ {
1239
+ inputs: [
1240
+ { name: "wordPos", type: "uint256" },
1241
+ { name: "mask", type: "uint256" }
1242
+ ],
1243
+ name: "invalidateUnorderedNonces",
1244
+ outputs: [],
1245
+ stateMutability: "nonpayable",
1246
+ type: "function"
1247
+ }
1248
+ ],
1249
+ functionName: "invalidateUnorderedNonces",
1250
+ args: [nonceWordPos, mask]
881
1251
  }),
882
- [data, loading, error, warning, fetchQuote]
883
- );
1252
+ value: 0n,
1253
+ from: order.user
1254
+ });
884
1255
  }
885
1256
 
886
- // src/react/useBalances.ts
887
- import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
888
- function useBalances(params) {
889
- const [data, setData] = useState4(null);
890
- const [loading, setLoading] = useState4(false);
891
- const [error, setError] = useState4(null);
892
- const abortRef = useRef4(null);
893
- const stableParams = useMemo3(() => {
894
- if (!params) return null;
895
- const { addresses, cursorEvm, cursorSvm } = params;
896
- return {
897
- addresses: {
898
- evm: addresses?.evm ?? void 0,
899
- svm: addresses?.svm ?? void 0
900
- },
901
- cursorEvm,
902
- cursorSvm
903
- };
904
- }, [params?.addresses?.evm, params?.addresses?.svm, params?.cursorEvm, params?.cursorSvm]);
905
- const fetchBalances = useCallback3(async () => {
906
- if (!stableParams) return;
907
- if (abortRef.current) abortRef.current.abort();
908
- const controller = new AbortController();
909
- abortRef.current = controller;
910
- setLoading(true);
911
- setError(null);
912
- try {
913
- const result = await getBalances(stableParams, { signal: controller.signal });
914
- setData((prev) => {
915
- if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
916
- return result;
917
- });
918
- return result;
919
- } catch (err) {
920
- if (err.name === "AbortError") return;
921
- const e = err instanceof Error ? err : new Error(String(err));
922
- setError(e);
923
- throw e;
924
- } finally {
925
- setLoading(false);
1257
+ // src/core/client.ts
1258
+ var SwapSDK = class {
1259
+ constructor(config) {
1260
+ __publicField(this, "apiKey");
1261
+ /**
1262
+ * Fetches metadata for one or more tokens from the Shogun Token Search API.
1263
+ *
1264
+ * ---
1265
+ * ### Overview
1266
+ * `getTokensData` retrieves normalized token information such as symbol, name,
1267
+ * decimals, logo URI, and verified status — for a given list of token addresses.
1268
+ *
1269
+ * It supports both **EVM** and **SVM (Solana)** tokens, returning metadata from
1270
+ * Shogun’s unified token registry.
1271
+ *
1272
+ * ---
1273
+ * @example
1274
+ * ```ts
1275
+ * const tokens = await getTokensData([
1276
+ * "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
1277
+ * "So11111111111111111111111111111111111111112", // SOL
1278
+ * ]);
1279
+ *
1280
+ * console.log(tokens);
1281
+ * [
1282
+ * { symbol: "USDC", name: "USD Coin", chainId: 1, decimals: 6, ... },
1283
+ * { symbol: "SOL", name: "Solana", chainId: 101, decimals: 9, ... }
1284
+ * ]
1285
+ * ```
1286
+ *
1287
+ * @param addresses - An array of token addresses (EVM or SVM) to fetch metadata for.
1288
+ * @returns A promise resolving to an array of {@link TokenInfo} objects.
1289
+ *
1290
+ * @throws Will throw an error if the network request fails or the API responds with a non-OK status.
1291
+ */
1292
+ __publicField(this, "getTokensData", getTokensData.bind(this));
1293
+ if (!config.apiKey) {
1294
+ throw new Error("SwapSDK: Missing API key");
926
1295
  }
927
- }, [stableParams]);
928
- useEffect4(() => {
929
- if (stableParams) fetchBalances().catch(() => {
1296
+ this.apiKey = config.apiKey;
1297
+ if (this.apiKey) void this.apiKey;
1298
+ }
1299
+ /**
1300
+ * Retrieves a swap quote for the given input and output tokens.
1301
+ *
1302
+ * @param params - Quote parameters including source/destination tokens and amount.
1303
+ * @returns A normalized `SwapQuoteResponse` containing output amount, route, and metadata.
1304
+ */
1305
+ async getQuote(params) {
1306
+ return getQuote(params);
1307
+ }
1308
+ /**
1309
+ * Fetches token balances for the specified user wallet(s).
1310
+ *
1311
+ * Supports both EVM and SVM (Solana) wallet addresses.
1312
+ *
1313
+ * @param params - Wallet address and optional chain filters.
1314
+ * @param options - Optional abort signal for cancellation.
1315
+ * @returns A unified balance response with per-chain token details.
1316
+ */
1317
+ async getBalances(params, options) {
1318
+ return getBalances(params, options);
1319
+ }
1320
+ /**
1321
+ * Retrieves a list of verified tokens based on search query or chain filter.
1322
+ *
1323
+ * @param params - Search parameters (query, chain ID, pagination options).
1324
+ * @returns Paginated `TokenSearchResponse` containing token metadata.
1325
+ */
1326
+ async getTokenList(params) {
1327
+ return getTokenList(params);
1328
+ }
1329
+ /**
1330
+ * Executes a prepared swap quote using the provided wallet and configuration.
1331
+ *
1332
+ * Handles:
1333
+ * - Token approval (if required)
1334
+ * - Transaction signing and broadcasting
1335
+ * - Confirmation polling and stage-based status updates
1336
+ *
1337
+ * Supports both:
1338
+ * - Market orders (with optional deadline)
1339
+ * - Limit orders (requires executionPrice and deadline)
1340
+ *
1341
+ * @param quote - The swap quote to execute, containing route and metadata.
1342
+ * @param accountAddress - The user's wallet address executing the swap.
1343
+ * @param recipientAddress - Optional recipient address for the output tokens (defaults to sender).
1344
+ * @param wallet - Adapted wallet instance (EVM/Solana) or a standard Viem `WalletClient`.
1345
+ * @param onStatus - Optional callback for receiving execution stage updates and messages.
1346
+ * @param orderType - Defines whether this is a market or limit order.
1347
+ * @param options - Execution parameters (different per order type).
1348
+ *
1349
+ * @returns A finalized execution result containing transaction hash, status, and any returned data.
1350
+ *
1351
+ * @example
1352
+ * ```ts
1353
+ * * Market order
1354
+ * const result = await sdk.executeTransaction({
1355
+ * quote,
1356
+ * accountAddress: "0x123...",
1357
+ * wallet,
1358
+ * orderType: OrderExecutionType.MARKET,
1359
+ * options: { deadline: 1800 },
1360
+ * onStatus: (stage, msg) => console.log(stage, msg),
1361
+ * });
1362
+ *
1363
+ * * Limit order
1364
+ * const result = await sdk.executeTransaction({
1365
+ * quote,
1366
+ * accountAddress: "0x123...",
1367
+ * wallet,
1368
+ * orderType: OrderExecutionType.LIMIT,
1369
+ * options: { executionPrice: "0.0021", deadline: 3600 },
1370
+ * });
1371
+ * ```
1372
+ */
1373
+ async executeTransaction({
1374
+ quote,
1375
+ accountAddress,
1376
+ recipientAddress,
1377
+ wallet,
1378
+ onStatus,
1379
+ orderType,
1380
+ options
1381
+ }) {
1382
+ return executeOrder({
1383
+ quote,
1384
+ wallet,
1385
+ accountAddress,
1386
+ recipientAddress,
1387
+ onStatus,
1388
+ orderType,
1389
+ options
930
1390
  });
931
- return () => {
932
- if (abortRef.current) abortRef.current.abort();
933
- };
934
- }, [fetchBalances]);
935
- return useMemo3(
936
- () => ({
937
- /** Latest fetched balance data */
938
- data,
939
- /** Whether the hook is currently fetching */
940
- loading,
941
- /** Error object if fetching failed */
942
- error,
943
- /** Manually trigger a refresh */
944
- refetch: fetchBalances
945
- }),
946
- [data, loading, error, fetchBalances]
947
- );
948
- }
1391
+ }
1392
+ /**
1393
+ * Fetches all user orders (Market, Limit, Cross-chain) for connected wallets.
1394
+ *
1395
+ * ---
1396
+ * ### Overview
1397
+ * Retrieves both **single-chain** and **cross-chain** orders from the Shogun Intents API.
1398
+ * Works across EVM, Solana
1399
+ *
1400
+ * ---
1401
+ * @example
1402
+ * ```ts
1403
+ * const orders = await sdk.getOrders({
1404
+ * evmAddress: "0x123...",
1405
+ * solAddress: "9d12hF...abc",
1406
+ * });
1407
+ *
1408
+ * console.log(orders.singleChainLimitOrders);
1409
+ * ```
1410
+ *
1411
+ * @param params - Wallet addresses to fetch orders for (EVM, Solana).
1412
+ * @returns A structured {@link ApiUserOrders} object containing all user orders.
1413
+ */
1414
+ async getOrders(params) {
1415
+ return getOrders(params);
1416
+ }
1417
+ /**
1418
+ * Cancels an order (single-chain or cross-chain) on EVM or Solana.
1419
+ *
1420
+ * Internally routes to the correct guard contract or Solana program to
1421
+ * deactivate the order or invalidate its nonce.
1422
+ *
1423
+ * @param params.order - Order payload returned from the Intents API.
1424
+ * @param params.wallet - Adapted wallet used to submit the cancellation transaction.
1425
+ * @param params.solRpc - Solana RPC URL used for SVM cancellations.
1426
+ *
1427
+ * @returns A transaction signature or hash from the underlying wallet.
1428
+ */
1429
+ async cancelOrder(params) {
1430
+ return cancelIntentsOrder({
1431
+ order: params.order,
1432
+ wallet: params.wallet,
1433
+ sol_rpc: params.solRpc
1434
+ });
1435
+ }
1436
+ };
949
1437
  export {
950
- NATIVE_TOKEN,
951
- SOLANA_CHAIN_ID,
1438
+ ChainId,
1439
+ OrderExecutionType,
952
1440
  SupportedChains,
953
- adaptEthersSigner,
954
- adaptSolanaWallet,
955
- adaptViemWallet,
1441
+ SwapSDK,
956
1442
  buildQuoteParams,
957
- executeOrder,
958
- getBalances,
959
- getQuote,
960
- getTokenList,
961
- isNativeAddress,
962
- isViemWalletClient,
963
- serializeBigIntsToStrings,
964
- useBalances,
965
- useExecuteOrder,
966
- useQuote,
967
- useTokenList
1443
+ isEvmChain
968
1444
  };