0xtrails 0.2.5 → 0.2.6
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/abortController.d.ts +8 -0
- package/dist/abortController.d.ts.map +1 -0
- package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/fees.d.ts +19 -0
- package/dist/fees.d.ts.map +1 -0
- package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
- package/dist/index.js +726 -520
- package/dist/prepareSend.d.ts +11 -77
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/transactions.d.ts +4 -2
- package/dist/transactions.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
- package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
- package/dist/widget/components/AccountSettings.d.ts.map +1 -1
- package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +2 -2
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
- package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
- package/dist/widget/components/Earn.d.ts +2 -2
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
- package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
- package/dist/widget/components/FeeBreakdown.d.ts +9 -0
- package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
- package/dist/widget/components/Fund.d.ts +2 -2
- package/dist/widget/components/Fund.d.ts.map +1 -1
- package/dist/widget/components/FundSwap.d.ts +2 -2
- package/dist/widget/components/FundSwap.d.ts.map +1 -1
- package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/Identicon.d.ts.map +1 -1
- package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
- package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
- package/dist/widget/components/Modal.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts +2 -2
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts +3 -2
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/PoolWithdraw.d.ts +3 -2
- package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts +1 -0
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts +2 -2
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
- package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
- package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
- package/dist/widget/components/TokenSelector.d.ts.map +1 -1
- package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
- package/dist/widget/components/Tooltip.d.ts +9 -0
- package/dist/widget/components/Tooltip.d.ts.map +1 -0
- package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
- package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
- package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useQuote.d.ts +83 -0
- package/dist/widget/hooks/useQuote.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +2 -2
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/index.js +2 -2
- package/dist/widget/widget.d.ts +5 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/abortController.ts +35 -0
- package/src/constants.ts +3 -0
- package/src/fees.ts +199 -0
- package/src/prepareSend.ts +225 -398
- package/src/trails.ts +3 -3
- package/src/transactions.ts +62 -18
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
- package/src/widget/components/AccountSettings.tsx +48 -36
- package/src/widget/components/ChainFilterDropdown.tsx +24 -3
- package/src/widget/components/ClassicSwap.tsx +24 -62
- package/src/widget/components/ConnectWallet.tsx +4 -1
- package/src/widget/components/ConnectedWallets.tsx +21 -21
- package/src/widget/components/DynamicInputStyles.tsx +76 -0
- package/src/widget/components/Earn.tsx +34 -29
- package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
- package/src/widget/components/FeeBreakdown.tsx +155 -0
- package/src/widget/components/Fund.tsx +10 -26
- package/src/widget/components/FundSwap.tsx +2 -2
- package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
- package/src/widget/components/Identicon.tsx +164 -95
- package/src/widget/components/MeshConnectExchanges.tsx +2 -15
- package/src/widget/components/Modal.tsx +0 -12
- package/src/widget/components/Pay.tsx +65 -63
- package/src/widget/components/PoolDeposit.tsx +206 -230
- package/src/widget/components/PoolWithdraw.tsx +219 -238
- package/src/widget/components/PriceImpactWarning.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +25 -8
- package/src/widget/components/Receipt.tsx +16 -2
- package/src/widget/components/RecipientSelectorButton.tsx +7 -5
- package/src/widget/components/Recipients.tsx +1 -1
- package/src/widget/components/ScreenHeader.tsx +60 -36
- package/src/widget/components/Swap.tsx +2 -2
- package/src/widget/components/ThemeProvider.tsx +2 -1
- package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
- package/src/widget/components/TokenImage.tsx +1 -1
- package/src/widget/components/TokenSelector.tsx +62 -53
- package/src/widget/components/TokenSelectorButton.tsx +38 -15
- package/src/widget/components/Tooltip.tsx +51 -0
- package/src/widget/components/TransferPendingVertical.tsx +1 -1
- package/src/widget/components/WaasFeeOptions.tsx +124 -5
- package/src/widget/components/WalletConfirmation.tsx +23 -13
- package/src/widget/components/WalletConnect.tsx +93 -29
- package/src/widget/hooks/useQuote.ts +413 -0
- package/src/widget/hooks/useSendForm.ts +8 -4
- package/src/widget/widget.tsx +175 -190
package/src/prepareSend.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
GetIntentCallsPayloadParams,
|
|
3
|
-
GetIntentCallsPayloadsReturn,
|
|
4
3
|
IntentPrecondition,
|
|
5
4
|
} from "@0xsequence/trails-api"
|
|
5
|
+
import type { GetIntentCallsPayloadsReturn } from "./intents.js"
|
|
6
6
|
import type { TrailsAPIClient } from "@0xsequence/trails-api"
|
|
7
7
|
import type { Relayer } from "@0xsequence/relayer"
|
|
8
|
-
import { useQuery } from "@tanstack/react-query"
|
|
9
8
|
import type {
|
|
10
9
|
Account,
|
|
11
10
|
Chain,
|
|
@@ -13,6 +12,8 @@ import type {
|
|
|
13
12
|
TransactionReceipt,
|
|
14
13
|
WalletClient,
|
|
15
14
|
} from "viem"
|
|
15
|
+
import { abortControllerRegistry } from "./abortController.js"
|
|
16
|
+
import { extractTrailsFeeBreakdown, type TrailsFeeBreakdown } from "./fees.js"
|
|
16
17
|
import {
|
|
17
18
|
createPublicClient,
|
|
18
19
|
createWalletClient,
|
|
@@ -35,7 +36,6 @@ import {
|
|
|
35
36
|
trackRelayerCallStarted,
|
|
36
37
|
trackTransactionConfirmed,
|
|
37
38
|
} from "./analytics.js"
|
|
38
|
-
import { useAPIClient } from "./apiClient.js"
|
|
39
39
|
import type { Attestation } from "./cctp.js"
|
|
40
40
|
import {
|
|
41
41
|
approveERC20,
|
|
@@ -51,18 +51,14 @@ import {
|
|
|
51
51
|
import { queueCCTPTransfer } from "./cctpqueue.js"
|
|
52
52
|
import { getChainInfo, getTestnetChainInfo } from "./chains.js"
|
|
53
53
|
import { attemptSwitchChain } from "./chainSwitch.js"
|
|
54
|
-
import {
|
|
55
|
-
getSequenceEnv,
|
|
56
|
-
getSlippageTolerance,
|
|
57
|
-
type SequenceEnv,
|
|
58
|
-
} from "./config.js"
|
|
54
|
+
import { getSlippageTolerance } from "./config.js"
|
|
59
55
|
import { TRAILS_INTENT_ENTRYPOINT_ADDRESS } from "./constants.js"
|
|
60
56
|
import {
|
|
61
57
|
decodeGuestModuleEvents,
|
|
62
58
|
decodeTrailsTokenSweeperEvents,
|
|
63
59
|
} from "./decoders.js"
|
|
64
60
|
import { getERC20TransferData } from "./encoders.js"
|
|
65
|
-
import {
|
|
61
|
+
import { InsufficientBalanceError } from "./error.js"
|
|
66
62
|
import {
|
|
67
63
|
estimateGasCost,
|
|
68
64
|
estimateGasCostUsd,
|
|
@@ -77,7 +73,6 @@ import {
|
|
|
77
73
|
signIntent,
|
|
78
74
|
} from "./gasless.js"
|
|
79
75
|
import { getIntentEntrypointFeeOptions } from "./intentEntrypoint.js"
|
|
80
|
-
import { useIndexerGatewayClient } from "./indexerClient.js"
|
|
81
76
|
import {
|
|
82
77
|
buildSameChainTransactionParams,
|
|
83
78
|
buildCrossChainDepositParams,
|
|
@@ -92,8 +87,7 @@ import { relayerSendMetaTx } from "./metaTxns.js"
|
|
|
92
87
|
import { findFirstPreconditionForChainId } from "./preconditions.js"
|
|
93
88
|
import { calcAmountUsdPrice, getTokenPrice } from "./prices.js"
|
|
94
89
|
import { getQueryParam } from "./queryParams.js"
|
|
95
|
-
import type { MetaTxnReceipt
|
|
96
|
-
import { useRelayers } from "./relayer.js"
|
|
90
|
+
import type { MetaTxnReceipt } from "./relayer.js"
|
|
97
91
|
import {
|
|
98
92
|
executeSimpleRelayTransaction,
|
|
99
93
|
getRelaySDKQuote,
|
|
@@ -111,13 +105,8 @@ import {
|
|
|
111
105
|
formatAmountDisplay,
|
|
112
106
|
formatRawAmount,
|
|
113
107
|
formatUsdAmountDisplay,
|
|
114
|
-
getTokenBalancesWithPrices,
|
|
115
108
|
} from "./tokenBalances.js"
|
|
116
|
-
import {
|
|
117
|
-
getTokenInfo,
|
|
118
|
-
useSupportedTokens,
|
|
119
|
-
type SupportedToken,
|
|
120
|
-
} from "./tokens.js"
|
|
109
|
+
import { getTokenInfo, type SupportedToken } from "./tokens.js"
|
|
121
110
|
import {
|
|
122
111
|
TRAILS_ROUTER_PLACEHOLDER_AMOUNT,
|
|
123
112
|
wrapCalldataWithTrailsRouterIfNeeded,
|
|
@@ -130,9 +119,21 @@ import { getAccountTransactionHistory, getTxTimeDiff } from "./transactions.js"
|
|
|
130
119
|
import { requestWithTimeout } from "./utils.js"
|
|
131
120
|
import type { CheckoutOnHandlers } from "./widget/hooks/useCheckout.js"
|
|
132
121
|
import { logger } from "./logger.js"
|
|
122
|
+
|
|
123
|
+
// Polling intervals for different operations
|
|
124
|
+
const POLLING_INTERVALS = {
|
|
125
|
+
// Consistent interval for transaction history polling (3 seconds)
|
|
126
|
+
TRANSACTION_HISTORY: 3000,
|
|
127
|
+
// Consistent interval for meta transaction polling (2 seconds)
|
|
128
|
+
META_TRANSACTION: 2000,
|
|
129
|
+
// Consistent interval for CCTP queue polling (5 seconds)
|
|
130
|
+
CCTP_QUEUE: 5000,
|
|
131
|
+
// Consistent interval for failure polling (2 seconds)
|
|
132
|
+
FAILURE_POLLING: 2000,
|
|
133
|
+
} as const
|
|
134
|
+
|
|
133
135
|
import { getIsCustomCalldata } from "./contractUtils.js"
|
|
134
136
|
import type { SequenceAPIClient } from "@0xsequence/api"
|
|
135
|
-
import { useTrailsClient } from "./trailsClient.js"
|
|
136
137
|
import { updatePersistentToast } from "./toast.js"
|
|
137
138
|
import {
|
|
138
139
|
getDelegatorSmartAccount,
|
|
@@ -182,6 +183,7 @@ export type PrepareSendOptions = {
|
|
|
182
183
|
refundAddress?: string
|
|
183
184
|
selectedFeeToken?: any
|
|
184
185
|
walletId?: string
|
|
186
|
+
abortSignal?: AbortSignal
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
export type PrepareSendFees = {
|
|
@@ -232,6 +234,7 @@ export type PrepareSendQuote = {
|
|
|
232
234
|
destinationTokenRate: string
|
|
233
235
|
quoteProvider: QuoteProviderInfo | null
|
|
234
236
|
noSufficientBalance: boolean
|
|
237
|
+
trailsFeeBreakdown?: TrailsFeeBreakdown | null
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
export type PrepareSendReturn = {
|
|
@@ -363,6 +366,12 @@ function getIntentArgs(
|
|
|
363
366
|
export async function prepareSend(
|
|
364
367
|
options: PrepareSendOptions,
|
|
365
368
|
): Promise<PrepareSendReturn> {
|
|
369
|
+
// Abort all existing operations when a new quote is generated
|
|
370
|
+
logger.console.log(
|
|
371
|
+
"[trails-sdk] New quote generated - aborting all existing operations",
|
|
372
|
+
)
|
|
373
|
+
abortControllerRegistry.abortAll()
|
|
374
|
+
|
|
366
375
|
const {
|
|
367
376
|
account,
|
|
368
377
|
originTokenAddress,
|
|
@@ -396,6 +405,7 @@ export async function prepareSend(
|
|
|
396
405
|
checkoutOnHandlers,
|
|
397
406
|
selectedFeeToken,
|
|
398
407
|
walletId,
|
|
408
|
+
abortSignal,
|
|
399
409
|
} = options
|
|
400
410
|
let { sourceTokenPriceUsd, destinationTokenPriceUsd } = options
|
|
401
411
|
|
|
@@ -672,6 +682,7 @@ export async function prepareSend(
|
|
|
672
682
|
originRelayer,
|
|
673
683
|
mode,
|
|
674
684
|
fundMethod,
|
|
685
|
+
abortSignal,
|
|
675
686
|
})
|
|
676
687
|
}
|
|
677
688
|
|
|
@@ -714,6 +725,7 @@ export async function prepareSend(
|
|
|
714
725
|
checkoutOnHandlers,
|
|
715
726
|
selectedFeeToken,
|
|
716
727
|
walletId,
|
|
728
|
+
abortSignal,
|
|
717
729
|
})
|
|
718
730
|
}
|
|
719
731
|
|
|
@@ -755,6 +767,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
755
767
|
checkoutOnHandlers,
|
|
756
768
|
selectedFeeToken,
|
|
757
769
|
walletId,
|
|
770
|
+
abortSignal,
|
|
758
771
|
}: {
|
|
759
772
|
mainSignerAddress: string
|
|
760
773
|
originChainId: number
|
|
@@ -794,6 +807,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
794
807
|
checkoutOnHandlers?: CheckoutOnHandlers
|
|
795
808
|
selectedFeeToken?: any
|
|
796
809
|
walletId?: string
|
|
810
|
+
abortSignal?: AbortSignal
|
|
797
811
|
}): Promise<PrepareSendReturn> {
|
|
798
812
|
const testnet = isTestnetDebugMode()
|
|
799
813
|
const useCctp = getUseCctp(
|
|
@@ -1207,6 +1221,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1207
1221
|
quoteProvider: intent.payloads?.quote?.quoteProvider,
|
|
1208
1222
|
noSufficientBalance,
|
|
1209
1223
|
estimatedGasLimit: estimatedGasLimitForQuote,
|
|
1224
|
+
intent,
|
|
1210
1225
|
})
|
|
1211
1226
|
|
|
1212
1227
|
// Call onCheckoutQuote callback if provided
|
|
@@ -1417,6 +1432,7 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1417
1432
|
trailsClient,
|
|
1418
1433
|
selectedFeeToken: effectiveSelectedFeeToken,
|
|
1419
1434
|
walletId,
|
|
1435
|
+
abortSignal,
|
|
1420
1436
|
})
|
|
1421
1437
|
|
|
1422
1438
|
if (!originUserTxReceipt) {
|
|
@@ -1497,10 +1513,19 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1497
1513
|
|
|
1498
1514
|
const checkForDepositTx = async () => {
|
|
1499
1515
|
while (true) {
|
|
1516
|
+
// Check if we should abort
|
|
1517
|
+
if (abortSignal?.aborted) {
|
|
1518
|
+
logger.console.log(
|
|
1519
|
+
"[trails-sdk] Aborting deposit tx check due to abort signal",
|
|
1520
|
+
)
|
|
1521
|
+
return null
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1500
1524
|
try {
|
|
1501
1525
|
const response = await getAccountTransactionHistory({
|
|
1502
1526
|
chainId: originChainId,
|
|
1503
1527
|
accountAddress: originIntentAddress,
|
|
1528
|
+
abortSignal,
|
|
1504
1529
|
})
|
|
1505
1530
|
logger.console.log(
|
|
1506
1531
|
"[trails-sdk] getAccountTransactionHistory response",
|
|
@@ -1509,14 +1534,16 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1509
1534
|
if (response.transactions.length > 0) {
|
|
1510
1535
|
const tx = response.transactions[0]
|
|
1511
1536
|
if (!tx?.txnHash) {
|
|
1512
|
-
await new Promise((resolve) =>
|
|
1537
|
+
await new Promise((resolve) =>
|
|
1538
|
+
setTimeout(resolve, POLLING_INTERVALS.TRANSACTION_HISTORY),
|
|
1539
|
+
)
|
|
1513
1540
|
continue
|
|
1514
1541
|
}
|
|
1515
1542
|
// const isReceive = tx.transfers.some(
|
|
1516
1543
|
// (transfer) => transfer.transferType === "RECEIVE",
|
|
1517
1544
|
// )
|
|
1518
1545
|
// if (!isReceive) {
|
|
1519
|
-
// await new Promise((resolve) => setTimeout(resolve,
|
|
1546
|
+
// await new Promise((resolve) => setTimeout(resolve, POLLING_INTERVALS.TRANSACTION_HISTORY))
|
|
1520
1547
|
// continue
|
|
1521
1548
|
// }
|
|
1522
1549
|
const originDepositTxReceipt =
|
|
@@ -1541,18 +1568,29 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1541
1568
|
} catch (error) {
|
|
1542
1569
|
logger.console.error("Error checking for deposit tx", error)
|
|
1543
1570
|
}
|
|
1544
|
-
await new Promise((resolve) =>
|
|
1571
|
+
await new Promise((resolve) =>
|
|
1572
|
+
setTimeout(resolve, POLLING_INTERVALS.TRANSACTION_HISTORY),
|
|
1573
|
+
)
|
|
1545
1574
|
}
|
|
1546
1575
|
}
|
|
1547
1576
|
|
|
1548
1577
|
const _checkForDestinationDepositTx: () => Promise<TransactionReceipt | null> =
|
|
1549
1578
|
async () => {
|
|
1550
1579
|
while (true) {
|
|
1580
|
+
// Check if we should abort
|
|
1581
|
+
if (abortSignal?.aborted) {
|
|
1582
|
+
logger.console.log(
|
|
1583
|
+
"[trails-sdk] Aborting destination deposit tx check due to abort signal",
|
|
1584
|
+
)
|
|
1585
|
+
return null
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1551
1588
|
try {
|
|
1552
1589
|
const response = await getAccountTransactionHistory({
|
|
1553
1590
|
chainId: destinationChainId,
|
|
1554
1591
|
accountAddress: intent.payloads
|
|
1555
1592
|
.destinationIntentAddress as `0x${string}`,
|
|
1593
|
+
abortSignal,
|
|
1556
1594
|
})
|
|
1557
1595
|
logger.console.log(
|
|
1558
1596
|
"[trails-sdk] getAccountTransactionHistory response",
|
|
@@ -1561,7 +1599,12 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1561
1599
|
if (response.transactions.length > 0) {
|
|
1562
1600
|
const tx = response.transactions[0]
|
|
1563
1601
|
if (!tx?.txnHash) {
|
|
1564
|
-
await new Promise((resolve) =>
|
|
1602
|
+
await new Promise((resolve) =>
|
|
1603
|
+
setTimeout(
|
|
1604
|
+
resolve,
|
|
1605
|
+
POLLING_INTERVALS.TRANSACTION_HISTORY,
|
|
1606
|
+
),
|
|
1607
|
+
)
|
|
1565
1608
|
continue
|
|
1566
1609
|
}
|
|
1567
1610
|
// const isReceive = tx.transfers.some(
|
|
@@ -1588,13 +1631,15 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1588
1631
|
} catch (error) {
|
|
1589
1632
|
logger.console.error("Error checking for deposit tx", error)
|
|
1590
1633
|
}
|
|
1591
|
-
await new Promise((resolve) =>
|
|
1634
|
+
await new Promise((resolve) =>
|
|
1635
|
+
setTimeout(resolve, POLLING_INTERVALS.TRANSACTION_HISTORY),
|
|
1636
|
+
)
|
|
1592
1637
|
}
|
|
1593
1638
|
}
|
|
1594
1639
|
|
|
1595
1640
|
// Variables to store the waitForReceipt functions
|
|
1596
1641
|
let originMetaTxnReceiptPromise:
|
|
1597
|
-
| (() => Promise<MetaTxnReceipt | null>)
|
|
1642
|
+
| ((abortSignal?: AbortSignal) => Promise<MetaTxnReceipt | null>)
|
|
1598
1643
|
| null = null
|
|
1599
1644
|
let destinationMetaTxnReceiptPromise:
|
|
1600
1645
|
| ((abortSignal?: AbortSignal) => Promise<MetaTxnReceipt | null>)
|
|
@@ -1851,6 +1896,14 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1851
1896
|
if (isCctp) {
|
|
1852
1897
|
queueCctpPromise = async () => {
|
|
1853
1898
|
while (true) {
|
|
1899
|
+
// Check if we should abort
|
|
1900
|
+
if (abortSignal?.aborted) {
|
|
1901
|
+
logger.console.log(
|
|
1902
|
+
"[trails-sdk] Aborting CCTP queue due to abort signal",
|
|
1903
|
+
)
|
|
1904
|
+
return
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1854
1907
|
const originMetaTxnHash = originMetaTxnReceipt?.txnHash
|
|
1855
1908
|
if (originMetaTxnHash) {
|
|
1856
1909
|
await queueCCTPTransfer({
|
|
@@ -1861,16 +1914,38 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1861
1914
|
})
|
|
1862
1915
|
break
|
|
1863
1916
|
}
|
|
1864
|
-
await new Promise((resolve) =>
|
|
1917
|
+
await new Promise((resolve) =>
|
|
1918
|
+
setTimeout(resolve, POLLING_INTERVALS.CCTP_QUEUE),
|
|
1919
|
+
)
|
|
1865
1920
|
}
|
|
1866
1921
|
}
|
|
1867
1922
|
} else {
|
|
1868
1923
|
queueCctpPromise = () => Promise.resolve()
|
|
1869
1924
|
}
|
|
1870
1925
|
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1926
|
+
// Only start polling for external deposits if needed:
|
|
1927
|
+
// - QR code or exchange funding (external deposits)
|
|
1928
|
+
// - Gasless flow (relayer-executed deposits)
|
|
1929
|
+
const needsDepositPolling =
|
|
1930
|
+
fundMethod === "qr-code" ||
|
|
1931
|
+
fundMethod === "exchange" ||
|
|
1932
|
+
gasless === true
|
|
1933
|
+
|
|
1934
|
+
if (needsDepositPolling) {
|
|
1935
|
+
logger.console.log(
|
|
1936
|
+
"[trails-sdk] Starting deposit polling for fundMethod:",
|
|
1937
|
+
fundMethod,
|
|
1938
|
+
"gasless:",
|
|
1939
|
+
gasless,
|
|
1940
|
+
)
|
|
1941
|
+
checkForDepositTx().catch((error) => {
|
|
1942
|
+
logger.console.error("Error checking for deposit tx", error)
|
|
1943
|
+
})
|
|
1944
|
+
} else {
|
|
1945
|
+
logger.console.log(
|
|
1946
|
+
"[trails-sdk] Skipping deposit polling for wallet funding with gas fees",
|
|
1947
|
+
)
|
|
1948
|
+
}
|
|
1874
1949
|
|
|
1875
1950
|
// Phase 1: Send meta transactions and queue CCTP
|
|
1876
1951
|
logger.console.log(
|
|
@@ -1895,7 +1970,8 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1895
1970
|
)
|
|
1896
1971
|
if (originMetaTxnReceiptPromise) {
|
|
1897
1972
|
try {
|
|
1898
|
-
originMetaTxnReceipt =
|
|
1973
|
+
originMetaTxnReceipt =
|
|
1974
|
+
await originMetaTxnReceiptPromise(abortSignal)
|
|
1899
1975
|
|
|
1900
1976
|
if (originMetaTxnReceipt && transactionStates[1]) {
|
|
1901
1977
|
transactionStates[1] = getTransactionStateFromReceipt(
|
|
@@ -1975,12 +2051,9 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1975
2051
|
)
|
|
1976
2052
|
if (destinationMetaTxnReceiptPromise) {
|
|
1977
2053
|
try {
|
|
1978
|
-
// Create abort controller for cancelling destination polling
|
|
1979
|
-
const abortController = new AbortController()
|
|
1980
|
-
|
|
1981
2054
|
// Race between destination receipt and failure polling
|
|
1982
2055
|
const destinationReceiptPromise = destinationMetaTxnReceiptPromise
|
|
1983
|
-
? destinationMetaTxnReceiptPromise(
|
|
2056
|
+
? destinationMetaTxnReceiptPromise(abortSignal)
|
|
1984
2057
|
: Promise.resolve(null)
|
|
1985
2058
|
|
|
1986
2059
|
const failurePollingPromise = new Promise<null>((resolve) => {
|
|
@@ -1997,11 +2070,13 @@ async function sendHandlerForDifferentChainDifferentToken({
|
|
|
1997
2070
|
logger.console.log(
|
|
1998
2071
|
"[trails-sdk] Aborting destination meta transaction due to previous transaction failure",
|
|
1999
2072
|
)
|
|
2000
|
-
abortController.abort()
|
|
2001
2073
|
resolve(null)
|
|
2002
2074
|
} else {
|
|
2003
|
-
// Continue polling
|
|
2004
|
-
setTimeout(
|
|
2075
|
+
// Continue polling with consistent interval
|
|
2076
|
+
setTimeout(
|
|
2077
|
+
pollForFailures,
|
|
2078
|
+
POLLING_INTERVALS.FAILURE_POLLING,
|
|
2079
|
+
)
|
|
2005
2080
|
}
|
|
2006
2081
|
}
|
|
2007
2082
|
pollForFailures()
|
|
@@ -2277,6 +2352,7 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2277
2352
|
selectedFeeToken,
|
|
2278
2353
|
trailsClient,
|
|
2279
2354
|
originRelayer,
|
|
2355
|
+
abortSignal,
|
|
2280
2356
|
}: {
|
|
2281
2357
|
originTokenAddress: string
|
|
2282
2358
|
originTokenDecimals: number
|
|
@@ -2303,6 +2379,7 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2303
2379
|
selectedFeeToken?: any
|
|
2304
2380
|
trailsClient: TrailsAPIClient
|
|
2305
2381
|
originRelayer: Relayer.RpcRelayer
|
|
2382
|
+
abortSignal?: AbortSignal
|
|
2306
2383
|
}): Promise<PrepareSendReturn> {
|
|
2307
2384
|
logger.console.log("[trails-sdk] isToSameToken && isToSameChain")
|
|
2308
2385
|
const testnet = isTestnetDebugMode()
|
|
@@ -2386,6 +2463,7 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2386
2463
|
quoteProvider: "",
|
|
2387
2464
|
noSufficientBalance,
|
|
2388
2465
|
estimatedGasLimit: estimatedGasLimitForQuote,
|
|
2466
|
+
intent: undefined,
|
|
2389
2467
|
})
|
|
2390
2468
|
|
|
2391
2469
|
// Call onCheckoutQuote callback if provided
|
|
@@ -2519,6 +2597,7 @@ async function sendHandlerForSameChainSameToken({
|
|
|
2519
2597
|
selectedFeeToken,
|
|
2520
2598
|
trailsClient,
|
|
2521
2599
|
originRelayer,
|
|
2600
|
+
abortSignal,
|
|
2522
2601
|
})
|
|
2523
2602
|
|
|
2524
2603
|
if (receipt) {
|
|
@@ -2900,6 +2979,7 @@ async function _sendHandlerForSameChainDifferentToken({
|
|
|
2900
2979
|
destinationChainId: originChainId,
|
|
2901
2980
|
originNativeTokenPriceUsd,
|
|
2902
2981
|
quoteProvider: "relay",
|
|
2982
|
+
intent: undefined,
|
|
2903
2983
|
})
|
|
2904
2984
|
|
|
2905
2985
|
return {
|
|
@@ -3023,6 +3103,7 @@ async function attemptGaslessDeposit({
|
|
|
3023
3103
|
originRelayer,
|
|
3024
3104
|
feeOptions,
|
|
3025
3105
|
selectedFeeToken,
|
|
3106
|
+
abortSignal,
|
|
3026
3107
|
}: {
|
|
3027
3108
|
paymasterUrl?: string
|
|
3028
3109
|
depositTokenAddress: string
|
|
@@ -3036,6 +3117,7 @@ async function attemptGaslessDeposit({
|
|
|
3036
3117
|
originRelayer: Relayer.RpcRelayer
|
|
3037
3118
|
feeOptions: any
|
|
3038
3119
|
selectedFeeToken?: any
|
|
3120
|
+
abortSignal?: AbortSignal
|
|
3039
3121
|
}): Promise<TransactionReceipt | null> {
|
|
3040
3122
|
let originUserTxReceipt: TransactionReceipt | null = null
|
|
3041
3123
|
const originChainId = chain.id
|
|
@@ -3383,6 +3465,14 @@ async function attemptGaslessDeposit({
|
|
|
3383
3465
|
logger.console.log("[trails-sdk] Waiting for transaction receipt")
|
|
3384
3466
|
// eslint-disable-next-line no-constant-condition
|
|
3385
3467
|
while (true) {
|
|
3468
|
+
// Check if we should abort
|
|
3469
|
+
if (abortSignal?.aborted) {
|
|
3470
|
+
logger.console.log(
|
|
3471
|
+
"[trails-sdk] Aborting gasless deposit polling due to abort signal",
|
|
3472
|
+
)
|
|
3473
|
+
return null
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3386
3476
|
const receipt: any = await getMetaTxStatus(
|
|
3387
3477
|
originRelayer,
|
|
3388
3478
|
depositData.metaTxn.id,
|
|
@@ -3405,7 +3495,9 @@ async function attemptGaslessDeposit({
|
|
|
3405
3495
|
break
|
|
3406
3496
|
}
|
|
3407
3497
|
|
|
3408
|
-
await new Promise((resolve) =>
|
|
3498
|
+
await new Promise((resolve) =>
|
|
3499
|
+
setTimeout(resolve, POLLING_INTERVALS.META_TRANSACTION),
|
|
3500
|
+
)
|
|
3409
3501
|
}
|
|
3410
3502
|
} catch (error) {
|
|
3411
3503
|
logger.console.error(
|
|
@@ -3766,6 +3858,7 @@ async function attemptUserDepositTx({
|
|
|
3766
3858
|
trailsClient,
|
|
3767
3859
|
selectedFeeToken,
|
|
3768
3860
|
walletId,
|
|
3861
|
+
abortSignal,
|
|
3769
3862
|
}: {
|
|
3770
3863
|
originTokenAddress: string
|
|
3771
3864
|
gasless: boolean
|
|
@@ -3795,6 +3888,7 @@ async function attemptUserDepositTx({
|
|
|
3795
3888
|
trailsClient: TrailsAPIClient
|
|
3796
3889
|
selectedFeeToken?: any
|
|
3797
3890
|
walletId?: string
|
|
3891
|
+
abortSignal?: AbortSignal
|
|
3798
3892
|
}): Promise<TransactionReceipt | null> {
|
|
3799
3893
|
let originUserTxReceipt: TransactionReceipt | null = null
|
|
3800
3894
|
const originChainId = chain.id
|
|
@@ -3858,6 +3952,7 @@ async function attemptUserDepositTx({
|
|
|
3858
3952
|
originRelayer,
|
|
3859
3953
|
feeOptions: feeOptions,
|
|
3860
3954
|
selectedFeeToken: selectedFeeToken,
|
|
3955
|
+
abortSignal,
|
|
3861
3956
|
})
|
|
3862
3957
|
} catch (error) {
|
|
3863
3958
|
logger.console.log("[trails-sdk] gassless attempt failed", error)
|
|
@@ -4093,38 +4188,84 @@ async function sendMetaTxAndWaitForReceipt({
|
|
|
4093
4188
|
waitForReceipt: async (abortSignal?: AbortSignal) => {
|
|
4094
4189
|
let originMetaTxnReceipt: MetaTxnReceipt | null = null
|
|
4095
4190
|
|
|
4096
|
-
//
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4191
|
+
// Create a unique ID for this polling operation
|
|
4192
|
+
const pollingId = `meta-txn-${metaTx.id}-${metaTx.chainId}-${Date.now()}`
|
|
4193
|
+
|
|
4194
|
+
// Create an abort controller for this specific polling operation
|
|
4195
|
+
const pollingAbortController = new AbortController()
|
|
4196
|
+
|
|
4197
|
+
// Register this polling operation with the global registry
|
|
4198
|
+
abortControllerRegistry.register(pollingId, pollingAbortController)
|
|
4199
|
+
|
|
4200
|
+
try {
|
|
4201
|
+
// eslint-disable-next-line no-constant-condition
|
|
4202
|
+
while (true) {
|
|
4203
|
+
// Check if we should abort (either from external signal or global registry)
|
|
4204
|
+
if (abortSignal?.aborted || pollingAbortController.signal.aborted) {
|
|
4205
|
+
logger.console.log(
|
|
4206
|
+
"[trails-sdk] Aborting meta transaction polling due to abort signal",
|
|
4207
|
+
)
|
|
4208
|
+
return null
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4100
4211
|
logger.console.log(
|
|
4101
|
-
"[trails-sdk]
|
|
4212
|
+
"[trails-sdk] polling status",
|
|
4213
|
+
metaTx.id as `0x${string}`,
|
|
4214
|
+
metaTx.chainId.toString(),
|
|
4102
4215
|
)
|
|
4103
|
-
|
|
4104
|
-
|
|
4216
|
+
const receipt: any = await getMetaTxStatus(
|
|
4217
|
+
relayer,
|
|
4218
|
+
metaTx.id,
|
|
4219
|
+
Number(metaTx.chainId),
|
|
4220
|
+
)
|
|
4221
|
+
logger.console.log("[trails-sdk] status", receipt)
|
|
4222
|
+
if (receipt?.transactionHash) {
|
|
4223
|
+
originMetaTxnReceipt = receipt.data?.receipt
|
|
4224
|
+
if (!originMetaTxnReceipt) {
|
|
4225
|
+
throw new Error("No meta txn receipt found")
|
|
4226
|
+
}
|
|
4227
|
+
break
|
|
4228
|
+
}
|
|
4105
4229
|
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
relayer,
|
|
4113
|
-
metaTx.id,
|
|
4114
|
-
Number(metaTx.chainId),
|
|
4115
|
-
)
|
|
4116
|
-
logger.console.log("[trails-sdk] status", receipt)
|
|
4117
|
-
if (receipt?.transactionHash) {
|
|
4118
|
-
originMetaTxnReceipt = receipt.data?.receipt
|
|
4119
|
-
if (!originMetaTxnReceipt) {
|
|
4120
|
-
throw new Error("No meta txn receipt found")
|
|
4230
|
+
// Check abort signal before waiting
|
|
4231
|
+
if (abortSignal?.aborted || pollingAbortController.signal.aborted) {
|
|
4232
|
+
logger.console.log(
|
|
4233
|
+
"[trails-sdk] Aborting meta transaction polling before delay",
|
|
4234
|
+
)
|
|
4235
|
+
return null
|
|
4121
4236
|
}
|
|
4122
|
-
|
|
4237
|
+
|
|
4238
|
+
// Use consistent delay and check abort signal during the delay
|
|
4239
|
+
await new Promise((resolve) => {
|
|
4240
|
+
const timeoutId = setTimeout(
|
|
4241
|
+
resolve,
|
|
4242
|
+
POLLING_INTERVALS.META_TRANSACTION,
|
|
4243
|
+
)
|
|
4244
|
+
|
|
4245
|
+
// Listen for abort signal during the delay
|
|
4246
|
+
const abortHandler = () => {
|
|
4247
|
+
clearTimeout(timeoutId)
|
|
4248
|
+
resolve(undefined)
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
if (abortSignal) {
|
|
4252
|
+
abortSignal.addEventListener("abort", abortHandler, {
|
|
4253
|
+
once: true,
|
|
4254
|
+
})
|
|
4255
|
+
}
|
|
4256
|
+
pollingAbortController.signal.addEventListener(
|
|
4257
|
+
"abort",
|
|
4258
|
+
abortHandler,
|
|
4259
|
+
{ once: true },
|
|
4260
|
+
)
|
|
4261
|
+
})
|
|
4123
4262
|
}
|
|
4124
|
-
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
4125
|
-
}
|
|
4126
4263
|
|
|
4127
|
-
|
|
4264
|
+
return originMetaTxnReceipt
|
|
4265
|
+
} finally {
|
|
4266
|
+
// Always unregister when polling completes or is aborted
|
|
4267
|
+
abortControllerRegistry.unregister(pollingId)
|
|
4268
|
+
}
|
|
4128
4269
|
},
|
|
4129
4270
|
}
|
|
4130
4271
|
}
|
|
@@ -4285,341 +4426,18 @@ function getNeedsLifiNativeFee({
|
|
|
4285
4426
|
return needsNativeFee
|
|
4286
4427
|
}
|
|
4287
4428
|
|
|
4288
|
-
export
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
tradeType?: TradeType | null
|
|
4298
|
-
slippageTolerance?: string | number | null
|
|
4299
|
-
onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
|
|
4300
|
-
quoteProvider?: string | null
|
|
4301
|
-
gasless?: boolean
|
|
4302
|
-
paymasterUrl?: string
|
|
4303
|
-
selectedFeeToken?: {
|
|
4304
|
-
tokenAddress: string
|
|
4305
|
-
tokenSymbol?: string
|
|
4306
|
-
} | null
|
|
4307
|
-
}
|
|
4429
|
+
// Re-export useQuote types and hook from the new location
|
|
4430
|
+
export type {
|
|
4431
|
+
UseQuoteProps,
|
|
4432
|
+
Quote,
|
|
4433
|
+
UseQuoteReturn,
|
|
4434
|
+
SwapReturn,
|
|
4435
|
+
QuoteProviderInfo,
|
|
4436
|
+
} from "./widget/hooks/useQuote.js"
|
|
4437
|
+
export { useQuote } from "./widget/hooks/useQuote.js"
|
|
4308
4438
|
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
transactionHash?: string | null
|
|
4312
|
-
explorerUrl?: string | null
|
|
4313
|
-
receipt: TransactionReceipt | MetaTxnReceipt | null
|
|
4314
|
-
}
|
|
4315
|
-
destinationTransaction: {
|
|
4316
|
-
transactionHash?: string | null
|
|
4317
|
-
explorerUrl?: string | null
|
|
4318
|
-
receipt: MetaTxnReceipt | null
|
|
4319
|
-
}
|
|
4320
|
-
totalCompletionSeconds?: number
|
|
4321
|
-
}
|
|
4322
|
-
|
|
4323
|
-
export type QuoteProviderInfo = {
|
|
4324
|
-
id: string
|
|
4325
|
-
name: string
|
|
4326
|
-
url: string
|
|
4327
|
-
}
|
|
4328
|
-
|
|
4329
|
-
export type Quote = {
|
|
4330
|
-
fromAmount: string
|
|
4331
|
-
fromAmountMin: string
|
|
4332
|
-
toAmount: string
|
|
4333
|
-
toAmountMin: string
|
|
4334
|
-
originToken: SupportedToken
|
|
4335
|
-
destinationToken: SupportedToken
|
|
4336
|
-
originChain: Chain
|
|
4337
|
-
destinationChain: Chain
|
|
4338
|
-
fees: PrepareSendFees
|
|
4339
|
-
slippageTolerance: string
|
|
4340
|
-
priceImpact: string
|
|
4341
|
-
completionEstimateSeconds: number
|
|
4342
|
-
transactionStates?: TransactionState[]
|
|
4343
|
-
originTokenRate?: string
|
|
4344
|
-
quoteProvider?: QuoteProviderInfo | null
|
|
4345
|
-
destinationTokenRate?: string
|
|
4346
|
-
fromAmountUsdDisplay?: string
|
|
4347
|
-
toAmountUsdDisplay?: string
|
|
4348
|
-
gasCostUsd?: number
|
|
4349
|
-
gasCostUsdDisplay?: string
|
|
4350
|
-
gasCost?: string
|
|
4351
|
-
gasCostFormatted?: string
|
|
4352
|
-
}
|
|
4353
|
-
|
|
4354
|
-
export type UseQuoteReturn = {
|
|
4355
|
-
quote: Quote | null
|
|
4356
|
-
swap: (() => Promise<SwapReturn | null>) | null
|
|
4357
|
-
isLoadingQuote: boolean
|
|
4358
|
-
quoteError: unknown
|
|
4359
|
-
refetchQuote: () => void
|
|
4360
|
-
}
|
|
4361
|
-
|
|
4362
|
-
export function useQuote({
|
|
4363
|
-
walletClient,
|
|
4364
|
-
fromTokenAddress,
|
|
4365
|
-
fromChainId,
|
|
4366
|
-
toTokenAddress,
|
|
4367
|
-
toChainId,
|
|
4368
|
-
swapAmount,
|
|
4369
|
-
tradeType,
|
|
4370
|
-
toRecipient,
|
|
4371
|
-
toCalldata,
|
|
4372
|
-
slippageTolerance,
|
|
4373
|
-
onStatusUpdate,
|
|
4374
|
-
quoteProvider,
|
|
4375
|
-
gasless,
|
|
4376
|
-
paymasterUrl,
|
|
4377
|
-
selectedFeeToken,
|
|
4378
|
-
relayerEnv,
|
|
4379
|
-
nodeGatewayEnv,
|
|
4380
|
-
}: Partial<
|
|
4381
|
-
UseQuoteProps & { relayerEnv?: RelayerEnv; nodeGatewayEnv?: SequenceEnv }
|
|
4382
|
-
> = {}): UseQuoteReturn {
|
|
4383
|
-
// Set node gateway environment override for this quote session
|
|
4384
|
-
if (nodeGatewayEnv) {
|
|
4385
|
-
;(globalThis as any).__testNodeGatewayEnv = nodeGatewayEnv
|
|
4386
|
-
}
|
|
4387
|
-
|
|
4388
|
-
const apiClient = useAPIClient()
|
|
4389
|
-
const trailsClient = useTrailsClient()
|
|
4390
|
-
const { getRelayer } = useRelayers({
|
|
4391
|
-
env: relayerEnv || (getSequenceEnv() as RelayerEnv),
|
|
4392
|
-
})
|
|
4393
|
-
const indexerGatewayClient = useIndexerGatewayClient()
|
|
4394
|
-
|
|
4395
|
-
const { supportedTokens } = useSupportedTokens()
|
|
4396
|
-
|
|
4397
|
-
const { data, isLoading, error, refetch } = useQuery({
|
|
4398
|
-
queryKey: [
|
|
4399
|
-
"quote",
|
|
4400
|
-
fromTokenAddress,
|
|
4401
|
-
fromChainId,
|
|
4402
|
-
toTokenAddress,
|
|
4403
|
-
toChainId,
|
|
4404
|
-
swapAmount?.toString(),
|
|
4405
|
-
toRecipient,
|
|
4406
|
-
toCalldata,
|
|
4407
|
-
tradeType,
|
|
4408
|
-
slippageTolerance,
|
|
4409
|
-
quoteProvider,
|
|
4410
|
-
],
|
|
4411
|
-
queryFn: async () => {
|
|
4412
|
-
try {
|
|
4413
|
-
if (
|
|
4414
|
-
!walletClient ||
|
|
4415
|
-
!apiClient ||
|
|
4416
|
-
!trailsClient ||
|
|
4417
|
-
!fromTokenAddress ||
|
|
4418
|
-
!toTokenAddress ||
|
|
4419
|
-
!swapAmount ||
|
|
4420
|
-
!toRecipient ||
|
|
4421
|
-
!fromChainId ||
|
|
4422
|
-
!toChainId ||
|
|
4423
|
-
!indexerGatewayClient
|
|
4424
|
-
) {
|
|
4425
|
-
return null
|
|
4426
|
-
}
|
|
4427
|
-
|
|
4428
|
-
// Get token balance using async method
|
|
4429
|
-
const { balances } = await getTokenBalancesWithPrices({
|
|
4430
|
-
account: walletClient.account!.address,
|
|
4431
|
-
indexerGatewayClient,
|
|
4432
|
-
apiClient,
|
|
4433
|
-
})
|
|
4434
|
-
|
|
4435
|
-
const originTokenBalance = balances.find(
|
|
4436
|
-
(b) =>
|
|
4437
|
-
b.chainId === fromChainId &&
|
|
4438
|
-
(b.contractAddress?.toLowerCase() ===
|
|
4439
|
-
fromTokenAddress.toLowerCase() ||
|
|
4440
|
-
(!b.contractAddress && fromTokenAddress === zeroAddress)),
|
|
4441
|
-
)
|
|
4442
|
-
|
|
4443
|
-
const originTokenBalanceAmount = originTokenBalance?.balance ?? "0"
|
|
4444
|
-
const destinationRelayer = getRelayer(toChainId)
|
|
4445
|
-
const originRelayer = getRelayer(fromChainId)
|
|
4446
|
-
|
|
4447
|
-
// Note: Disable this check for now to allow fetching a quote even when the origin balance is zero
|
|
4448
|
-
// if (originTokenBalanceAmount === "0") {
|
|
4449
|
-
// return null
|
|
4450
|
-
// }
|
|
4451
|
-
|
|
4452
|
-
// logger.console.log("supportedTokens", supportedTokens)
|
|
4453
|
-
|
|
4454
|
-
const originToken = supportedTokens?.find(
|
|
4455
|
-
(token) =>
|
|
4456
|
-
token.contractAddress?.toLowerCase() ===
|
|
4457
|
-
fromTokenAddress?.toLowerCase() && token.chainId === fromChainId,
|
|
4458
|
-
)
|
|
4459
|
-
const destinationToken = supportedTokens?.find(
|
|
4460
|
-
(token) =>
|
|
4461
|
-
token.contractAddress?.toLowerCase() ===
|
|
4462
|
-
toTokenAddress?.toLowerCase() && token.chainId === toChainId,
|
|
4463
|
-
)
|
|
4464
|
-
|
|
4465
|
-
const sourceTokenDecimals = originToken?.decimals
|
|
4466
|
-
if (!sourceTokenDecimals) {
|
|
4467
|
-
logger.console.error(
|
|
4468
|
-
"[trails-sdk] [useQuote] Missing source token decimals:",
|
|
4469
|
-
{
|
|
4470
|
-
originToken,
|
|
4471
|
-
fromTokenAddress,
|
|
4472
|
-
fromChainId,
|
|
4473
|
-
},
|
|
4474
|
-
)
|
|
4475
|
-
throw new Error("Source token decimals not found")
|
|
4476
|
-
}
|
|
4477
|
-
const destinationTokenDecimals = destinationToken?.decimals
|
|
4478
|
-
if (!destinationTokenDecimals) {
|
|
4479
|
-
logger.console.error(
|
|
4480
|
-
"[trails-sdk] Missing destination token decimals:",
|
|
4481
|
-
{
|
|
4482
|
-
destinationToken,
|
|
4483
|
-
toTokenAddress,
|
|
4484
|
-
toChainId,
|
|
4485
|
-
},
|
|
4486
|
-
)
|
|
4487
|
-
throw new Error("Destination token decimals not found")
|
|
4488
|
-
}
|
|
4489
|
-
const destinationTokenSymbol = destinationToken?.symbol ?? ""
|
|
4490
|
-
const originTokenSymbol = originToken?.symbol ?? ""
|
|
4491
|
-
|
|
4492
|
-
const options = {
|
|
4493
|
-
account: walletClient.account!,
|
|
4494
|
-
originTokenAddress: fromTokenAddress,
|
|
4495
|
-
originChainId: fromChainId,
|
|
4496
|
-
originTokenBalance: originTokenBalanceAmount,
|
|
4497
|
-
destinationChainId: toChainId,
|
|
4498
|
-
recipient: toRecipient,
|
|
4499
|
-
destinationTokenAddress: toTokenAddress,
|
|
4500
|
-
swapAmount: swapAmount.toString(),
|
|
4501
|
-
tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
|
|
4502
|
-
originTokenSymbol: originTokenSymbol,
|
|
4503
|
-
destinationTokenSymbol: destinationTokenSymbol,
|
|
4504
|
-
destinationCalldata: toCalldata as string,
|
|
4505
|
-
client: walletClient,
|
|
4506
|
-
apiClient,
|
|
4507
|
-
trailsClient,
|
|
4508
|
-
originRelayer,
|
|
4509
|
-
destinationRelayer,
|
|
4510
|
-
sourceTokenDecimals,
|
|
4511
|
-
destinationTokenDecimals,
|
|
4512
|
-
fee: "0",
|
|
4513
|
-
dryMode: false,
|
|
4514
|
-
onTransactionStateChange: onStatusUpdate ?? (() => {}),
|
|
4515
|
-
slippageTolerance: slippageTolerance?.toString(),
|
|
4516
|
-
quoteProvider: quoteProvider,
|
|
4517
|
-
gasless: gasless ?? false,
|
|
4518
|
-
paymasterUrl: paymasterUrl,
|
|
4519
|
-
selectedFeeToken: selectedFeeToken ?? undefined,
|
|
4520
|
-
}
|
|
4521
|
-
|
|
4522
|
-
logger.console.log("[trails-sdk] options", options)
|
|
4523
|
-
|
|
4524
|
-
const { quote: prepareSendQuote, send } = await prepareSend(options)
|
|
4525
|
-
|
|
4526
|
-
const quote = {
|
|
4527
|
-
fromAmount: prepareSendQuote.originAmount,
|
|
4528
|
-
toAmount: prepareSendQuote.destinationAmount,
|
|
4529
|
-
fromAmountMin: prepareSendQuote.originAmountMin,
|
|
4530
|
-
toAmountMin: prepareSendQuote.destinationAmountMin,
|
|
4531
|
-
originToken: prepareSendQuote.originToken,
|
|
4532
|
-
destinationToken: prepareSendQuote.destinationToken,
|
|
4533
|
-
originChain: prepareSendQuote.originChain,
|
|
4534
|
-
destinationChain: prepareSendQuote.destinationChain,
|
|
4535
|
-
fees: prepareSendQuote.fees,
|
|
4536
|
-
priceImpact: prepareSendQuote.priceImpact,
|
|
4537
|
-
completionEstimateSeconds: prepareSendQuote.completionEstimateSeconds,
|
|
4538
|
-
slippageTolerance: prepareSendQuote.slippageTolerance,
|
|
4539
|
-
transactionStates: prepareSendQuote.transactionStates,
|
|
4540
|
-
originTokenRate: prepareSendQuote.originTokenRate,
|
|
4541
|
-
destinationTokenRate: prepareSendQuote.destinationTokenRate,
|
|
4542
|
-
quoteProvider: prepareSendQuote.quoteProvider,
|
|
4543
|
-
fromAmountUsdDisplay:
|
|
4544
|
-
prepareSendQuote.originAmountUsdDisplay ?? undefined,
|
|
4545
|
-
toAmountUsdDisplay:
|
|
4546
|
-
prepareSendQuote.destinationAmountUsdDisplay ?? undefined,
|
|
4547
|
-
gasCostUsd: prepareSendQuote.gasCostUsd ?? undefined,
|
|
4548
|
-
gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? undefined,
|
|
4549
|
-
gasCost: prepareSendQuote.gasCost ?? undefined,
|
|
4550
|
-
gasCostFormatted: prepareSendQuote.gasCostFormatted ?? undefined,
|
|
4551
|
-
}
|
|
4552
|
-
|
|
4553
|
-
const swap = async (): Promise<SwapReturn> => {
|
|
4554
|
-
const {
|
|
4555
|
-
originUserTxReceipt,
|
|
4556
|
-
destinationMetaTxnReceipt,
|
|
4557
|
-
totalCompletionSeconds,
|
|
4558
|
-
} = await send({
|
|
4559
|
-
selectedFeeToken: selectedFeeToken ?? undefined,
|
|
4560
|
-
})
|
|
4561
|
-
|
|
4562
|
-
return {
|
|
4563
|
-
originTransaction: {
|
|
4564
|
-
transactionHash: originUserTxReceipt?.transactionHash,
|
|
4565
|
-
explorerUrl: getExplorerUrl({
|
|
4566
|
-
txHash: originUserTxReceipt?.transactionHash as string,
|
|
4567
|
-
chainId: fromChainId,
|
|
4568
|
-
}),
|
|
4569
|
-
receipt: originUserTxReceipt,
|
|
4570
|
-
},
|
|
4571
|
-
destinationTransaction: {
|
|
4572
|
-
transactionHash: destinationMetaTxnReceipt?.txnHash,
|
|
4573
|
-
explorerUrl: getExplorerUrl({
|
|
4574
|
-
txHash: destinationMetaTxnReceipt?.txnHash as string,
|
|
4575
|
-
chainId: toChainId,
|
|
4576
|
-
}),
|
|
4577
|
-
receipt: destinationMetaTxnReceipt,
|
|
4578
|
-
},
|
|
4579
|
-
totalCompletionSeconds,
|
|
4580
|
-
}
|
|
4581
|
-
}
|
|
4582
|
-
|
|
4583
|
-
return {
|
|
4584
|
-
quote,
|
|
4585
|
-
swap,
|
|
4586
|
-
}
|
|
4587
|
-
} catch (error) {
|
|
4588
|
-
logger.console.error(
|
|
4589
|
-
"[trails-sdk] [useQuote] Error getting quote:",
|
|
4590
|
-
error,
|
|
4591
|
-
)
|
|
4592
|
-
throw getFullErrorMessage(error)
|
|
4593
|
-
}
|
|
4594
|
-
},
|
|
4595
|
-
// Prevent unnecessary refetching
|
|
4596
|
-
enabled: Boolean(
|
|
4597
|
-
walletClient &&
|
|
4598
|
-
apiClient &&
|
|
4599
|
-
fromTokenAddress &&
|
|
4600
|
-
toTokenAddress &&
|
|
4601
|
-
swapAmount &&
|
|
4602
|
-
toRecipient &&
|
|
4603
|
-
fromChainId &&
|
|
4604
|
-
toChainId &&
|
|
4605
|
-
indexerGatewayClient,
|
|
4606
|
-
),
|
|
4607
|
-
staleTime: 30 * 1000, // Consider data fresh for 30 seconds
|
|
4608
|
-
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
|
4609
|
-
refetchOnMount: false, // Don't refetch on component remount if data exists
|
|
4610
|
-
refetchInterval: false, // Disable automatic polling
|
|
4611
|
-
retry: 2, // Limit retry attempts
|
|
4612
|
-
refetchOnReconnect: true, // Refetch when network reconnects
|
|
4613
|
-
})
|
|
4614
|
-
|
|
4615
|
-
return {
|
|
4616
|
-
quote: data?.quote || null,
|
|
4617
|
-
swap: data?.swap || null,
|
|
4618
|
-
isLoadingQuote: isLoading,
|
|
4619
|
-
quoteError: error,
|
|
4620
|
-
refetchQuote: () => refetch(),
|
|
4621
|
-
}
|
|
4622
|
-
}
|
|
4439
|
+
// Import QuoteProviderInfo for use in this file
|
|
4440
|
+
import type { QuoteProviderInfo } from "./widget/hooks/useQuote.js"
|
|
4623
4441
|
|
|
4624
4442
|
export function getFeesFromIntent(
|
|
4625
4443
|
intent: GetIntentCallsPayloadsReturn,
|
|
@@ -4746,6 +4564,7 @@ export async function getNormalizedQuoteObject({
|
|
|
4746
4564
|
quoteProvider,
|
|
4747
4565
|
noSufficientBalance,
|
|
4748
4566
|
estimatedGasLimit,
|
|
4567
|
+
intent,
|
|
4749
4568
|
}: {
|
|
4750
4569
|
originDepositAddress?: string
|
|
4751
4570
|
destinationDepositAddress?: string
|
|
@@ -4770,6 +4589,7 @@ export async function getNormalizedQuoteObject({
|
|
|
4770
4589
|
quoteProvider?: string
|
|
4771
4590
|
noSufficientBalance?: boolean
|
|
4772
4591
|
estimatedGasLimit?: bigint
|
|
4592
|
+
intent?: GetIntentCallsPayloadsReturn
|
|
4773
4593
|
}): Promise<PrepareSendQuote> {
|
|
4774
4594
|
if (!destinationChainId) {
|
|
4775
4595
|
throw new Error("Destination chain id is required")
|
|
@@ -4958,6 +4778,12 @@ export async function getNormalizedQuoteObject({
|
|
|
4958
4778
|
|
|
4959
4779
|
const priceImpactUsdDisplay = formatUsdAmountDisplay(priceImpactUsd)
|
|
4960
4780
|
|
|
4781
|
+
// Extract fee breakdown from intent if available
|
|
4782
|
+
let trailsFeeBreakdown = null
|
|
4783
|
+
if (intent) {
|
|
4784
|
+
trailsFeeBreakdown = await extractTrailsFeeBreakdown(intent)
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4961
4787
|
return {
|
|
4962
4788
|
originDepositAddress: originDepositAddress || "",
|
|
4963
4789
|
destinationDepositAddress: destinationDepositAddress || "",
|
|
@@ -5004,6 +4830,7 @@ export async function getNormalizedQuoteObject({
|
|
|
5004
4830
|
),
|
|
5005
4831
|
quoteProvider: quoteProviderInfo,
|
|
5006
4832
|
noSufficientBalance: noSufficientBalance || false,
|
|
4833
|
+
trailsFeeBreakdown,
|
|
5007
4834
|
}
|
|
5008
4835
|
}
|
|
5009
4836
|
|