@shogun-sdk/swap 0.0.2-test.2 → 0.0.2-test.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.cjs +226 -62
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +219 -56
- package/dist/{execute-HX1fQ7wG.d.ts → execute-CKTsf_tD.d.ts} +14 -16
- package/dist/{execute-FaLLPp1i.d.cts → execute-DOv1i2Su.d.cts} +14 -16
- package/dist/index.cjs +260 -109
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +254 -104
- package/dist/react.cjs +266 -115
- package/dist/react.d.cts +6 -40
- package/dist/react.d.ts +6 -40
- package/dist/react.js +255 -105
- package/dist/wallet-adapter.cjs +21 -10
- package/dist/wallet-adapter.js +21 -10
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -139,22 +139,25 @@ async function getQuote(params) {
|
|
|
139
139
|
tokenOut: params.tokenOut.address,
|
|
140
140
|
amount: params.amount
|
|
141
141
|
});
|
|
142
|
-
const
|
|
143
|
-
const slippageDecimal = inputSlippage / 100;
|
|
144
|
-
const slippage = Math.min(Math.max(slippageDecimal, 0), 0.5);
|
|
142
|
+
const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
|
|
145
143
|
let warning;
|
|
146
|
-
if (
|
|
147
|
-
warning = `\u26A0\uFE0F High slippage tolerance (${
|
|
144
|
+
if (slippagePercent > 10) {
|
|
145
|
+
warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
|
|
148
146
|
}
|
|
149
|
-
const estimatedAmountOut = BigInt(data.
|
|
150
|
-
const slippageBps = BigInt(Math.round(
|
|
147
|
+
const estimatedAmountOut = BigInt(data.estimatedAmountOut);
|
|
148
|
+
const slippageBps = BigInt(Math.round(slippagePercent * 100));
|
|
151
149
|
const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
|
|
150
|
+
const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
|
|
151
|
+
const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
|
|
152
|
+
const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
|
|
153
|
+
const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
|
|
152
154
|
const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
|
|
153
155
|
return {
|
|
154
|
-
amountOut:
|
|
155
|
-
amountOutUsd:
|
|
156
|
+
amountOut: estimatedAmountOutAfterSlippage,
|
|
157
|
+
amountOutUsd: amountOutUsdAfterSlippage,
|
|
156
158
|
amountInUsd: data.amountInUsd,
|
|
157
|
-
|
|
159
|
+
// Input USD stays the same
|
|
160
|
+
minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
|
|
158
161
|
tokenIn: {
|
|
159
162
|
address: params.tokenIn.address,
|
|
160
163
|
decimals: params.tokenIn.decimals ?? 18,
|
|
@@ -167,10 +170,11 @@ async function getQuote(params) {
|
|
|
167
170
|
},
|
|
168
171
|
amountIn: params.amount,
|
|
169
172
|
pricePerInputToken,
|
|
170
|
-
slippage,
|
|
173
|
+
slippage: slippagePercent,
|
|
171
174
|
internal: {
|
|
172
175
|
...data,
|
|
173
|
-
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage
|
|
176
|
+
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
|
|
177
|
+
estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
|
|
174
178
|
},
|
|
175
179
|
warning
|
|
176
180
|
};
|
|
@@ -243,7 +247,7 @@ async function getBalances(params, options) {
|
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
// src/core/executeOrder/execute.ts
|
|
246
|
-
import { ChainID as
|
|
250
|
+
import { ChainID as ChainID3, isEvmChain as isEvmChain2 } from "@shogun-sdk/intents-sdk";
|
|
247
251
|
import { BaseError } from "viem";
|
|
248
252
|
|
|
249
253
|
// src/wallet-adapter/svm-wallet-adapter/adapter.ts
|
|
@@ -256,7 +260,7 @@ var adaptSolanaWallet = (walletAddress, chainId, rpcUrl, signAndSendTransaction)
|
|
|
256
260
|
const getChainId = async () => _chainId;
|
|
257
261
|
const sendTransaction = async (transaction) => {
|
|
258
262
|
const txHash = await signAndSendTransaction(transaction);
|
|
259
|
-
console.
|
|
263
|
+
console.debug(`\u{1F6F0} Sent transaction: ${txHash}`);
|
|
260
264
|
const maxRetries = 20;
|
|
261
265
|
const delayMs = 2e3;
|
|
262
266
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
@@ -350,15 +354,26 @@ var adaptViemWallet = (wallet) => {
|
|
|
350
354
|
if (!isEVMTransaction(transaction)) {
|
|
351
355
|
throw new Error("Expected EVMTransaction but got SolanaTransaction");
|
|
352
356
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
357
|
+
if (wallet.transport.type === "http") {
|
|
358
|
+
const request = await wallet.prepareTransactionRequest({
|
|
359
|
+
to: transaction.to,
|
|
360
|
+
data: transaction.data,
|
|
361
|
+
value: transaction.value,
|
|
362
|
+
chain: wallet.chain
|
|
363
|
+
});
|
|
364
|
+
const serializedTransaction = await wallet.signTransaction(request);
|
|
365
|
+
const tx = await wallet.sendRawTransaction({ serializedTransaction });
|
|
366
|
+
return tx;
|
|
367
|
+
} else {
|
|
368
|
+
const hash = await wallet.sendTransaction({
|
|
369
|
+
to: transaction.to,
|
|
370
|
+
data: transaction.data,
|
|
371
|
+
value: transaction.value,
|
|
372
|
+
chain: wallet.chain,
|
|
373
|
+
account: wallet.account
|
|
374
|
+
});
|
|
375
|
+
return hash;
|
|
376
|
+
}
|
|
362
377
|
};
|
|
363
378
|
const switchChain = async (chainId) => {
|
|
364
379
|
try {
|
|
@@ -405,10 +420,12 @@ var DEFAULT_STAGE_MESSAGES = {
|
|
|
405
420
|
processing: "Preparing transaction for execution",
|
|
406
421
|
approving: "Approving token allowance",
|
|
407
422
|
approved: "Token approved successfully",
|
|
408
|
-
signing: "Signing
|
|
409
|
-
submitting: "Submitting
|
|
410
|
-
|
|
411
|
-
|
|
423
|
+
signing: "Signing transaction for submission",
|
|
424
|
+
submitting: "Submitting transaction",
|
|
425
|
+
initiated: "Transaction initiated.",
|
|
426
|
+
success: "Transaction Executed successfully",
|
|
427
|
+
shogun_processing: "Shogun is processing your transaction",
|
|
428
|
+
error: "Transaction failed during submission"
|
|
412
429
|
};
|
|
413
430
|
|
|
414
431
|
// src/core/executeOrder/buildOrder.ts
|
|
@@ -447,6 +464,110 @@ async function buildOrder({
|
|
|
447
464
|
});
|
|
448
465
|
}
|
|
449
466
|
|
|
467
|
+
// src/utils/pollOrderStatus.ts
|
|
468
|
+
import { AUCTIONEER_URL } from "@shogun-sdk/intents-sdk";
|
|
469
|
+
async function pollOrderStatus(address, orderId, options = {}) {
|
|
470
|
+
const { intervalMs = 2e3, timeoutMs = 3e5 } = options;
|
|
471
|
+
const startTime = Date.now();
|
|
472
|
+
const isDebug = process.env.NODE_ENV !== "production";
|
|
473
|
+
const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
474
|
+
const isSuiAddress = /^0x[a-fA-F0-9]{64}$/.test(address);
|
|
475
|
+
const isSolanaAddress = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
|
|
476
|
+
let queryParam;
|
|
477
|
+
if (isEvmAddress) queryParam = `evmWallets=${address}`;
|
|
478
|
+
else if (isSuiAddress) queryParam = `suiWallets=${address}`;
|
|
479
|
+
else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
|
|
480
|
+
else throw new Error(`Unrecognized wallet address format: ${address}`);
|
|
481
|
+
const queryUrl = `${AUCTIONEER_URL}/user_intent?${queryParam}`;
|
|
482
|
+
return new Promise((resolve, reject) => {
|
|
483
|
+
const pollInterval = setInterval(async () => {
|
|
484
|
+
try {
|
|
485
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
486
|
+
clearInterval(pollInterval);
|
|
487
|
+
return resolve("Timeout");
|
|
488
|
+
}
|
|
489
|
+
const res = await fetch(queryUrl, {
|
|
490
|
+
method: "GET",
|
|
491
|
+
headers: { "Content-Type": "application/json" }
|
|
492
|
+
});
|
|
493
|
+
if (!res.ok) {
|
|
494
|
+
clearInterval(pollInterval);
|
|
495
|
+
return reject(
|
|
496
|
+
new Error(`Failed to fetch orders: ${res.status} ${res.statusText}`)
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
const json = await res.json();
|
|
500
|
+
const data = json?.data ?? {};
|
|
501
|
+
const allOrders = [
|
|
502
|
+
...data.crossChainDcaOrders ?? [],
|
|
503
|
+
...data.crossChainLimitOrders ?? [],
|
|
504
|
+
...data.singleChainDcaOrders ?? [],
|
|
505
|
+
...data.singleChainLimitOrders ?? []
|
|
506
|
+
];
|
|
507
|
+
const targetOrder = allOrders.find((o) => o.orderId === orderId);
|
|
508
|
+
if (!targetOrder) {
|
|
509
|
+
if (isDebug)
|
|
510
|
+
console.debug(`[pollOrderStatus] [${orderId}] Not found yet`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const { orderStatus } = targetOrder;
|
|
514
|
+
if (isDebug) {
|
|
515
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
516
|
+
console.debug(`targetOrder`, targetOrder);
|
|
517
|
+
console.debug(
|
|
518
|
+
`[pollOrderStatus] [${orderId}] status=${orderStatus} (elapsed ${elapsed}s)`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
if (["Fulfilled", "Cancelled", "Outdated"].includes(orderStatus)) {
|
|
522
|
+
clearInterval(pollInterval);
|
|
523
|
+
return resolve(orderStatus);
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
clearInterval(pollInterval);
|
|
527
|
+
return reject(error);
|
|
528
|
+
}
|
|
529
|
+
}, intervalMs);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/core/executeOrder/handleOrderPollingResult.ts
|
|
534
|
+
async function handleOrderPollingResult({
|
|
535
|
+
status,
|
|
536
|
+
orderId,
|
|
537
|
+
chainId,
|
|
538
|
+
update,
|
|
539
|
+
messageFor
|
|
540
|
+
}) {
|
|
541
|
+
switch (status) {
|
|
542
|
+
case "Fulfilled":
|
|
543
|
+
update("success", messageFor("success"));
|
|
544
|
+
return {
|
|
545
|
+
status: true,
|
|
546
|
+
orderId,
|
|
547
|
+
chainId,
|
|
548
|
+
finalStatus: status,
|
|
549
|
+
stage: "success"
|
|
550
|
+
};
|
|
551
|
+
case "Cancelled":
|
|
552
|
+
update("error", "Order was cancelled before fulfillment");
|
|
553
|
+
break;
|
|
554
|
+
case "Timeout":
|
|
555
|
+
update("error", "Order polling timed out");
|
|
556
|
+
break;
|
|
557
|
+
case "NotFound":
|
|
558
|
+
default:
|
|
559
|
+
update("error", "Order not found");
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
status: false,
|
|
564
|
+
orderId,
|
|
565
|
+
chainId,
|
|
566
|
+
finalStatus: status,
|
|
567
|
+
stage: "error"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
450
571
|
// src/core/executeOrder/handleEvmExecution.ts
|
|
451
572
|
async function handleEvmExecution({
|
|
452
573
|
recipientAddress,
|
|
@@ -475,18 +596,18 @@ async function handleEvmExecution({
|
|
|
475
596
|
from: accountAddress
|
|
476
597
|
});
|
|
477
598
|
}
|
|
478
|
-
update("
|
|
599
|
+
update("processing", messageFor("approving"));
|
|
479
600
|
await wallet.sendTransaction({
|
|
480
601
|
to: tokenIn,
|
|
481
602
|
data: encodeFunctionData({
|
|
482
603
|
abi: erc20Abi,
|
|
483
604
|
functionName: "approve",
|
|
484
|
-
args: [PERMIT2_ADDRESS[chainId],
|
|
605
|
+
args: [PERMIT2_ADDRESS[chainId], quote.amountIn]
|
|
485
606
|
}),
|
|
486
607
|
value: 0n,
|
|
487
608
|
from: accountAddress
|
|
488
609
|
});
|
|
489
|
-
update("
|
|
610
|
+
update("processing", messageFor("approved"));
|
|
490
611
|
const destination = recipientAddress ?? accountAddress;
|
|
491
612
|
const order = await buildOrder({
|
|
492
613
|
quote,
|
|
@@ -495,24 +616,40 @@ async function handleEvmExecution({
|
|
|
495
616
|
deadline,
|
|
496
617
|
isSingleChain
|
|
497
618
|
});
|
|
498
|
-
|
|
619
|
+
console.debug(`order`, order);
|
|
620
|
+
update("processing", messageFor("signing"));
|
|
499
621
|
const { orderTypedData, nonce } = isSingleChain ? await getEVMSingleChainOrderTypedData(order) : await getEVMCrossChainOrderTypedData(order);
|
|
622
|
+
const typedData = serializeBigIntsToStrings(orderTypedData);
|
|
500
623
|
if (!wallet.signTypedData) {
|
|
501
624
|
throw new Error("Wallet does not support EIP-712 signing");
|
|
502
625
|
}
|
|
503
|
-
const signature = await wallet.signTypedData(
|
|
504
|
-
|
|
626
|
+
const signature = await wallet.signTypedData({
|
|
627
|
+
domain: typedData.domain,
|
|
628
|
+
types: typedData.types,
|
|
629
|
+
primaryType: typedData.primaryType,
|
|
630
|
+
value: typedData.message,
|
|
631
|
+
message: typedData.message
|
|
632
|
+
});
|
|
633
|
+
update("processing", messageFor("submitting"));
|
|
505
634
|
const res = await order.sendToAuctioneer({ signature, nonce: nonce.toString() });
|
|
506
635
|
if (!res.success) {
|
|
507
636
|
throw new Error("Auctioneer submission failed");
|
|
508
637
|
}
|
|
509
|
-
update("
|
|
510
|
-
|
|
638
|
+
update("initiated", messageFor("initiated"));
|
|
639
|
+
const { intentId: orderId } = res.data;
|
|
640
|
+
update("initiated", messageFor("shogun_processing"));
|
|
641
|
+
const status = await pollOrderStatus(accountAddress, orderId);
|
|
642
|
+
return await handleOrderPollingResult({
|
|
643
|
+
status,
|
|
644
|
+
orderId,
|
|
645
|
+
chainId,
|
|
646
|
+
update,
|
|
647
|
+
messageFor
|
|
648
|
+
});
|
|
511
649
|
}
|
|
512
650
|
|
|
513
651
|
// src/core/executeOrder/handleSolanaExecution.ts
|
|
514
652
|
import {
|
|
515
|
-
ChainID as ChainID3,
|
|
516
653
|
getSolanaSingleChainOrderInstructions,
|
|
517
654
|
getSolanaCrossChainOrderInstructions
|
|
518
655
|
} from "@shogun-sdk/intents-sdk";
|
|
@@ -545,10 +682,9 @@ async function handleSolanaExecution({
|
|
|
545
682
|
rpcUrl: wallet.rpcUrl
|
|
546
683
|
});
|
|
547
684
|
const transaction = VersionedTransaction2.deserialize(Uint8Array.from(txData.txBytes));
|
|
548
|
-
update("
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
update("submitting", messageFor("submitting"));
|
|
685
|
+
update("processing", messageFor("signing"));
|
|
686
|
+
await wallet.sendTransaction(transaction);
|
|
687
|
+
update("processing", messageFor("submitting"));
|
|
552
688
|
const response = await submitToAuctioneer({
|
|
553
689
|
order,
|
|
554
690
|
isSingleChain,
|
|
@@ -557,13 +693,17 @@ async function handleSolanaExecution({
|
|
|
557
693
|
if (!response.success) {
|
|
558
694
|
throw new Error("Auctioneer submission failed");
|
|
559
695
|
}
|
|
560
|
-
update("
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
696
|
+
update("initiated", messageFor("initiated"));
|
|
697
|
+
const { jwt, intentId: orderId } = response.data;
|
|
698
|
+
update("initiated", messageFor("shogun_processing"));
|
|
699
|
+
const status = await pollOrderStatus(jwt, orderId);
|
|
700
|
+
return await handleOrderPollingResult({
|
|
701
|
+
status,
|
|
702
|
+
orderId,
|
|
703
|
+
chainId: SOLANA_CHAIN_ID,
|
|
704
|
+
update,
|
|
705
|
+
messageFor
|
|
706
|
+
});
|
|
567
707
|
}
|
|
568
708
|
async function getSolanaOrderInstructions({
|
|
569
709
|
order,
|
|
@@ -605,18 +745,33 @@ async function executeOrder({
|
|
|
605
745
|
onStatus,
|
|
606
746
|
options = {}
|
|
607
747
|
}) {
|
|
608
|
-
const
|
|
748
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
749
|
+
const log = (...args) => {
|
|
750
|
+
if (isDev) console.debug("[OneShot::executeOrder]", ...args);
|
|
751
|
+
};
|
|
609
752
|
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
610
|
-
const update = (stage, message) =>
|
|
753
|
+
const update = (stage, message) => {
|
|
754
|
+
log("Stage:", stage, "| Message:", message ?? messageFor(stage));
|
|
755
|
+
onStatus?.(stage, message ?? messageFor(stage));
|
|
756
|
+
};
|
|
611
757
|
try {
|
|
758
|
+
const deadline = options.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
759
|
+
log("Starting execution:", {
|
|
760
|
+
accountAddress,
|
|
761
|
+
recipientAddress,
|
|
762
|
+
deadline,
|
|
763
|
+
tokenIn: quote?.tokenIn,
|
|
764
|
+
tokenOut: quote?.tokenOut
|
|
765
|
+
});
|
|
612
766
|
const adapter = normalizeWallet(wallet);
|
|
613
767
|
if (!adapter) throw new Error("No wallet provided");
|
|
614
768
|
const { tokenIn, tokenOut } = quote;
|
|
615
769
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
616
770
|
const chainId = Number(tokenIn.chainId);
|
|
617
|
-
update("processing"
|
|
771
|
+
update("processing");
|
|
618
772
|
if (isEvmChain2(chainId)) {
|
|
619
|
-
|
|
773
|
+
log("Detected EVM chain:", chainId);
|
|
774
|
+
const result = await handleEvmExecution({
|
|
620
775
|
recipientAddress,
|
|
621
776
|
quote,
|
|
622
777
|
chainId,
|
|
@@ -626,9 +781,12 @@ async function executeOrder({
|
|
|
626
781
|
deadline,
|
|
627
782
|
update
|
|
628
783
|
});
|
|
784
|
+
log("EVM execution result:", result);
|
|
785
|
+
return result;
|
|
629
786
|
}
|
|
630
|
-
if (chainId ===
|
|
631
|
-
|
|
787
|
+
if (chainId === ChainID3.Solana) {
|
|
788
|
+
log("Detected Solana chain");
|
|
789
|
+
const result = await handleSolanaExecution({
|
|
632
790
|
recipientAddress,
|
|
633
791
|
quote,
|
|
634
792
|
accountAddress,
|
|
@@ -637,11 +795,16 @@ async function executeOrder({
|
|
|
637
795
|
deadline,
|
|
638
796
|
update
|
|
639
797
|
});
|
|
798
|
+
log("Solana execution result:", result);
|
|
799
|
+
return result;
|
|
640
800
|
}
|
|
641
|
-
|
|
642
|
-
|
|
801
|
+
const unsupported = `Unsupported chain: ${chainId}`;
|
|
802
|
+
update("error", unsupported);
|
|
803
|
+
log("Error:", unsupported);
|
|
804
|
+
return { status: false, message: unsupported, stage: "error" };
|
|
643
805
|
} catch (error) {
|
|
644
806
|
const message = error instanceof BaseError ? error.shortMessage : error instanceof Error ? error.message : String(error);
|
|
807
|
+
log("Execution failed:", { message, error });
|
|
645
808
|
update("error", message);
|
|
646
809
|
return { status: false, message, stage: "error" };
|
|
647
810
|
}
|
|
@@ -822,77 +985,64 @@ function useQuote(params, options) {
|
|
|
822
985
|
const autoRefreshMs = options?.autoRefreshMs;
|
|
823
986
|
const abortRef = useRef3(null);
|
|
824
987
|
const debounceRef = useRef3(null);
|
|
825
|
-
const
|
|
988
|
+
const mounted = useRef3(false);
|
|
989
|
+
const paramsKey = useMemo2(
|
|
990
|
+
() => params ? JSON.stringify(serializeBigIntsToStrings(params)) : null,
|
|
991
|
+
[params]
|
|
992
|
+
);
|
|
826
993
|
useEffect3(() => {
|
|
994
|
+
mounted.current = true;
|
|
827
995
|
return () => {
|
|
828
|
-
|
|
996
|
+
mounted.current = false;
|
|
829
997
|
abortRef.current?.abort();
|
|
830
998
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
831
999
|
};
|
|
832
1000
|
}, []);
|
|
833
|
-
const fetchQuote = useCallback2(
|
|
834
|
-
async (signal) => {
|
|
835
|
-
if (!params) return;
|
|
836
|
-
try {
|
|
837
|
-
setLoading(true);
|
|
838
|
-
setWarning(null);
|
|
839
|
-
const result = await getQuote({ ...params, signal });
|
|
840
|
-
if (!mountedRef.current) return;
|
|
841
|
-
setData((prev) => {
|
|
842
|
-
if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
|
|
843
|
-
return result;
|
|
844
|
-
});
|
|
845
|
-
setWarning(result.warning ?? null);
|
|
846
|
-
setError(null);
|
|
847
|
-
} catch (err) {
|
|
848
|
-
if (err.name === "AbortError") return;
|
|
849
|
-
const e = err instanceof Error ? err : new Error(String(err));
|
|
850
|
-
if (mountedRef.current) setError(e);
|
|
851
|
-
} finally {
|
|
852
|
-
if (mountedRef.current) setLoading(false);
|
|
853
|
-
}
|
|
854
|
-
},
|
|
855
|
-
[params]
|
|
856
|
-
);
|
|
857
|
-
useEffect3(() => {
|
|
1001
|
+
const fetchQuote = useCallback2(async () => {
|
|
858
1002
|
if (!params) return;
|
|
1003
|
+
try {
|
|
1004
|
+
setLoading(true);
|
|
1005
|
+
setWarning(null);
|
|
1006
|
+
const result = await getQuote(params);
|
|
1007
|
+
const serializeResult = serializeBigIntsToStrings(result);
|
|
1008
|
+
if (!mounted.current) return;
|
|
1009
|
+
setData((prev) => {
|
|
1010
|
+
if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
|
|
1011
|
+
return serializeResult;
|
|
1012
|
+
});
|
|
1013
|
+
setWarning(result.warning ?? null);
|
|
1014
|
+
setError(null);
|
|
1015
|
+
} catch (err) {
|
|
1016
|
+
if (err.name === "AbortError") return;
|
|
1017
|
+
console.error("[useQuote] fetch error:", err);
|
|
1018
|
+
if (mounted.current) setError(err instanceof Error ? err : new Error(String(err)));
|
|
1019
|
+
} finally {
|
|
1020
|
+
if (mounted.current) setLoading(false);
|
|
1021
|
+
}
|
|
1022
|
+
}, [paramsKey]);
|
|
1023
|
+
useEffect3(() => {
|
|
1024
|
+
if (!paramsKey) return;
|
|
859
1025
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
860
|
-
abortRef.current?.abort();
|
|
861
|
-
const controller = new AbortController();
|
|
862
|
-
abortRef.current = controller;
|
|
863
1026
|
debounceRef.current = setTimeout(() => {
|
|
864
|
-
fetchQuote(
|
|
1027
|
+
fetchQuote();
|
|
865
1028
|
}, debounceMs);
|
|
866
1029
|
return () => {
|
|
867
|
-
controller.abort();
|
|
868
1030
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
1031
|
+
abortRef.current?.abort();
|
|
869
1032
|
};
|
|
870
|
-
}, [
|
|
871
|
-
params?.tokenIn?.address,
|
|
872
|
-
params?.tokenOut?.address,
|
|
873
|
-
params?.sourceChainId,
|
|
874
|
-
params?.destChainId,
|
|
875
|
-
params?.amount,
|
|
876
|
-
debounceMs,
|
|
877
|
-
fetchQuote
|
|
878
|
-
]);
|
|
1033
|
+
}, [paramsKey, debounceMs, fetchQuote]);
|
|
879
1034
|
useEffect3(() => {
|
|
880
|
-
if (!autoRefreshMs || !
|
|
1035
|
+
if (!autoRefreshMs || !paramsKey) return;
|
|
881
1036
|
const interval = setInterval(() => fetchQuote(), autoRefreshMs);
|
|
882
1037
|
return () => clearInterval(interval);
|
|
883
|
-
}, [autoRefreshMs,
|
|
1038
|
+
}, [autoRefreshMs, paramsKey, fetchQuote]);
|
|
884
1039
|
return useMemo2(
|
|
885
1040
|
() => ({
|
|
886
|
-
/** Latest quote data */
|
|
887
1041
|
data,
|
|
888
|
-
/** Whether a fetch is ongoing */
|
|
889
1042
|
loading,
|
|
890
|
-
/** Error (if any) */
|
|
891
1043
|
error,
|
|
892
|
-
/** Warning (e.g. high slippage alert) */
|
|
893
1044
|
warning,
|
|
894
|
-
|
|
895
|
-
refetch: () => fetchQuote()
|
|
1045
|
+
refetch: fetchQuote
|
|
896
1046
|
}),
|
|
897
1047
|
[data, loading, error, warning, fetchQuote]
|
|
898
1048
|
);
|