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.
Files changed (116) hide show
  1. package/dist/abortController.d.ts +8 -0
  2. package/dist/abortController.d.ts.map +1 -0
  3. package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/fees.d.ts +19 -0
  7. package/dist/fees.d.ts.map +1 -0
  8. package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
  9. package/dist/index.js +726 -520
  10. package/dist/prepareSend.d.ts +11 -77
  11. package/dist/prepareSend.d.ts.map +1 -1
  12. package/dist/transactions.d.ts +4 -2
  13. package/dist/transactions.d.ts.map +1 -1
  14. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  15. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  16. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  17. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  18. package/dist/widget/components/ClassicSwap.d.ts +2 -2
  19. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  20. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  21. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  22. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  23. package/dist/widget/components/Earn.d.ts +2 -2
  24. package/dist/widget/components/Earn.d.ts.map +1 -1
  25. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  26. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  27. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  28. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  29. package/dist/widget/components/Fund.d.ts +2 -2
  30. package/dist/widget/components/Fund.d.ts.map +1 -1
  31. package/dist/widget/components/FundSwap.d.ts +2 -2
  32. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  33. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  34. package/dist/widget/components/Identicon.d.ts.map +1 -1
  35. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  36. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  37. package/dist/widget/components/Modal.d.ts.map +1 -1
  38. package/dist/widget/components/Pay.d.ts +2 -2
  39. package/dist/widget/components/Pay.d.ts.map +1 -1
  40. package/dist/widget/components/PoolDeposit.d.ts +3 -2
  41. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  42. package/dist/widget/components/PoolWithdraw.d.ts +3 -2
  43. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  44. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  45. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  46. package/dist/widget/components/Receipt.d.ts.map +1 -1
  47. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  48. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  49. package/dist/widget/components/Swap.d.ts +2 -2
  50. package/dist/widget/components/Swap.d.ts.map +1 -1
  51. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  52. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  53. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  54. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  55. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  56. package/dist/widget/components/Tooltip.d.ts +9 -0
  57. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  58. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  59. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  60. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  61. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  62. package/dist/widget/css/compiled.css +1 -1
  63. package/dist/widget/hooks/useQuote.d.ts +83 -0
  64. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  65. package/dist/widget/hooks/useSendForm.d.ts +2 -2
  66. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  67. package/dist/widget/index.js +2 -2
  68. package/dist/widget/widget.d.ts +5 -0
  69. package/dist/widget/widget.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/abortController.ts +35 -0
  72. package/src/constants.ts +3 -0
  73. package/src/fees.ts +199 -0
  74. package/src/prepareSend.ts +225 -398
  75. package/src/trails.ts +3 -3
  76. package/src/transactions.ts +62 -18
  77. package/src/widget/compiled.css +1 -1
  78. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  79. package/src/widget/components/AccountSettings.tsx +48 -36
  80. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  81. package/src/widget/components/ClassicSwap.tsx +24 -62
  82. package/src/widget/components/ConnectWallet.tsx +4 -1
  83. package/src/widget/components/ConnectedWallets.tsx +21 -21
  84. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  85. package/src/widget/components/Earn.tsx +34 -29
  86. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  87. package/src/widget/components/FeeBreakdown.tsx +155 -0
  88. package/src/widget/components/Fund.tsx +10 -26
  89. package/src/widget/components/FundSwap.tsx +2 -2
  90. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  91. package/src/widget/components/Identicon.tsx +164 -95
  92. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  93. package/src/widget/components/Modal.tsx +0 -12
  94. package/src/widget/components/Pay.tsx +65 -63
  95. package/src/widget/components/PoolDeposit.tsx +206 -230
  96. package/src/widget/components/PoolWithdraw.tsx +219 -238
  97. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  98. package/src/widget/components/QuoteDetails.tsx +25 -8
  99. package/src/widget/components/Receipt.tsx +16 -2
  100. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  101. package/src/widget/components/Recipients.tsx +1 -1
  102. package/src/widget/components/ScreenHeader.tsx +60 -36
  103. package/src/widget/components/Swap.tsx +2 -2
  104. package/src/widget/components/ThemeProvider.tsx +2 -1
  105. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  106. package/src/widget/components/TokenImage.tsx +1 -1
  107. package/src/widget/components/TokenSelector.tsx +62 -53
  108. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  109. package/src/widget/components/Tooltip.tsx +51 -0
  110. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  111. package/src/widget/components/WaasFeeOptions.tsx +124 -5
  112. package/src/widget/components/WalletConfirmation.tsx +23 -13
  113. package/src/widget/components/WalletConnect.tsx +93 -29
  114. package/src/widget/hooks/useQuote.ts +413 -0
  115. package/src/widget/hooks/useSendForm.ts +8 -4
  116. package/src/widget/widget.tsx +175 -190
@@ -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 { getFullErrorMessage, InsufficientBalanceError } from "./error.js"
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, RelayerEnv } from "./relayer.js"
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) => setTimeout(resolve, 1000))
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, 1000))
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) => setTimeout(resolve, 1000))
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) => setTimeout(resolve, 1000))
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) => setTimeout(resolve, 1000))
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) => setTimeout(resolve, 1000))
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
- checkForDepositTx().catch((error) => {
1872
- logger.console.error("Error checking for deposit tx", error)
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 = await originMetaTxnReceiptPromise()
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(abortController.signal)
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 every 1 second
2004
- setTimeout(pollForFailures, 1000)
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) => setTimeout(resolve, 1000))
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
- // eslint-disable-next-line no-constant-condition
4097
- while (true) {
4098
- // Check if we should abort
4099
- if (abortSignal?.aborted) {
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] Aborting meta transaction polling due to abort signal",
4212
+ "[trails-sdk] polling status",
4213
+ metaTx.id as `0x${string}`,
4214
+ metaTx.chainId.toString(),
4102
4215
  )
4103
- return null
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
- logger.console.log(
4107
- "[trails-sdk] polling status",
4108
- metaTx.id as `0x${string}`,
4109
- metaTx.chainId.toString(),
4110
- )
4111
- const receipt: any = await getMetaTxStatus(
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
- break
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
- return originMetaTxnReceipt
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 type UseQuoteProps = {
4289
- walletClient?: any // TODO: fix this, has to do with viem/wagmi versions
4290
- fromTokenAddress?: string | null
4291
- fromChainId?: number | null
4292
- toTokenAddress?: string | null
4293
- toChainId?: number | null
4294
- toCalldata?: string | null
4295
- swapAmount?: string | bigint
4296
- toRecipient?: string | null
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
- export type SwapReturn = {
4310
- originTransaction: {
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