0xtrails 0.1.2 → 0.1.4

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 (158) hide show
  1. package/dist/address.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +86 -1
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/apiClient.d.ts +1 -1
  5. package/dist/apiClient.d.ts.map +1 -1
  6. package/dist/{ccip-BmFTEOaB.js → ccip-dLSEJjCf.js} +55 -55
  7. package/dist/cctpqueue.d.ts +1 -1
  8. package/dist/cctpqueue.d.ts.map +1 -1
  9. package/dist/chains.d.ts +9 -3
  10. package/dist/chains.d.ts.map +1 -1
  11. package/dist/constants.d.ts +1 -0
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/decoders.d.ts +58 -0
  14. package/dist/decoders.d.ts.map +1 -0
  15. package/dist/ens.d.ts +13 -0
  16. package/dist/ens.d.ts.map +1 -0
  17. package/dist/error.d.ts +9 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/{index-BPsVj7zK.js → index-BXbaLmtt.js} +28779 -25738
  20. package/dist/index.js +2 -2
  21. package/dist/intents.d.ts +4 -4
  22. package/dist/intents.d.ts.map +1 -1
  23. package/dist/lifi.d.ts +4 -0
  24. package/dist/lifi.d.ts.map +1 -0
  25. package/dist/metaTxns.d.ts +1 -1
  26. package/dist/metaTxns.d.ts.map +1 -1
  27. package/dist/mode.d.ts +1 -1
  28. package/dist/mode.d.ts.map +1 -1
  29. package/dist/preconditions.d.ts +1 -1
  30. package/dist/preconditions.d.ts.map +1 -1
  31. package/dist/prepareSend.d.ts +32 -24
  32. package/dist/prepareSend.d.ts.map +1 -1
  33. package/dist/prices.d.ts +3 -1
  34. package/dist/prices.d.ts.map +1 -1
  35. package/dist/proxyCaller.d.ts +0 -1
  36. package/dist/proxyCaller.d.ts.map +1 -1
  37. package/dist/relaySdk.d.ts.map +1 -1
  38. package/dist/relayer.d.ts.map +1 -1
  39. package/dist/tokenBalances.d.ts +1 -1
  40. package/dist/tokenBalances.d.ts.map +1 -1
  41. package/dist/tokens.d.ts +2 -1
  42. package/dist/tokens.d.ts.map +1 -1
  43. package/dist/trails.d.ts +4 -4
  44. package/dist/trails.d.ts.map +1 -1
  45. package/dist/transactions.d.ts +4 -0
  46. package/dist/transactions.d.ts.map +1 -1
  47. package/dist/utils.d.ts +6 -0
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/wallets.d.ts +247 -5
  50. package/dist/wallets.d.ts.map +1 -1
  51. package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
  52. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  53. package/dist/widget/components/ConnectWallet.d.ts +1 -0
  54. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  55. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
  56. package/dist/widget/components/ErrorDisplay.d.ts +9 -0
  57. package/dist/widget/components/ErrorDisplay.d.ts.map +1 -0
  58. package/dist/widget/components/FundSendForm.d.ts +2 -2
  59. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  60. package/dist/widget/components/OriginTransferInformation.d.ts +10 -0
  61. package/dist/widget/components/OriginTransferInformation.d.ts.map +1 -0
  62. package/dist/widget/components/PaySendForm.d.ts +2 -2
  63. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  64. package/dist/widget/components/QrCode.d.ts +1 -1
  65. package/dist/widget/components/QrCode.d.ts.map +1 -1
  66. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  67. package/dist/widget/components/Receipt.d.ts.map +1 -1
  68. package/dist/widget/components/Receive.d.ts +12 -0
  69. package/dist/widget/components/Receive.d.ts.map +1 -0
  70. package/dist/widget/components/RefundAddressInput.d.ts +13 -0
  71. package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
  72. package/dist/widget/components/Swap.d.ts +47 -0
  73. package/dist/widget/components/Swap.d.ts.map +1 -0
  74. package/dist/widget/components/SwapDisplay.d.ts +9 -0
  75. package/dist/widget/components/SwapDisplay.d.ts.map +1 -0
  76. package/dist/widget/components/TokenList.d.ts +0 -2
  77. package/dist/widget/components/TokenList.d.ts.map +1 -1
  78. package/dist/widget/components/TokenSelector.d.ts +26 -0
  79. package/dist/widget/components/TokenSelector.d.ts.map +1 -0
  80. package/dist/widget/components/TransferPendingVertical.d.ts +2 -0
  81. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  82. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  83. package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
  84. package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
  85. package/dist/widget/components/WalletList.d.ts.map +1 -1
  86. package/dist/widget/components/YellowWarningAnimation.d.ts +2 -0
  87. package/dist/widget/components/YellowWarningAnimation.d.ts.map +1 -0
  88. package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
  89. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  90. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  91. package/dist/widget/hooks/useDebugScreens.d.ts +22 -0
  92. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -0
  93. package/dist/widget/hooks/useSendForm.d.ts +12 -6
  94. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  95. package/dist/widget/hooks/useTokenList.d.ts +2 -3
  96. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  97. package/dist/widget/index.js +1 -1
  98. package/dist/widget/widget.d.ts.map +1 -1
  99. package/package.json +19 -15
  100. package/src/aave.ts +13 -13
  101. package/src/address.ts +3 -0
  102. package/src/analytics.ts +192 -8
  103. package/src/apiClient.ts +1 -1
  104. package/src/cctpqueue.ts +1 -1
  105. package/src/chains.ts +45 -7
  106. package/src/constants.ts +7 -4
  107. package/src/decoders.ts +310 -0
  108. package/src/ens.ts +32 -0
  109. package/src/error.ts +101 -1
  110. package/src/intents.ts +10 -2
  111. package/src/lifi.ts +58 -0
  112. package/src/metaTxns.ts +1 -1
  113. package/src/mode.ts +1 -1
  114. package/src/morpho.ts +3 -3
  115. package/src/pools.ts +18 -18
  116. package/src/preconditions.ts +1 -1
  117. package/src/prepareSend.ts +463 -113
  118. package/src/prices.ts +26 -1
  119. package/src/proxyCaller.ts +2 -14
  120. package/src/relaySdk.ts +1 -0
  121. package/src/relayer.ts +8 -0
  122. package/src/tokenBalances.ts +24 -17
  123. package/src/tokens.ts +147 -22
  124. package/src/trails.ts +4 -4
  125. package/src/transactions.ts +35 -17
  126. package/src/utils.ts +28 -0
  127. package/src/wallets.ts +275 -35
  128. package/src/widget/compiled.css +2 -2
  129. package/src/widget/components/ChainFilterDropdown.tsx +42 -33
  130. package/src/widget/components/ChainImage.tsx +1 -1
  131. package/src/widget/components/ConnectWallet.tsx +92 -128
  132. package/src/widget/components/DebugScreensDropdown.tsx +6 -0
  133. package/src/widget/components/ErrorDisplay.tsx +150 -0
  134. package/src/widget/components/FundSendForm.tsx +78 -11
  135. package/src/widget/components/OriginTransferInformation.tsx +59 -0
  136. package/src/widget/components/PaySendForm.tsx +80 -13
  137. package/src/widget/components/QRCodeDeposit.tsx +6 -6
  138. package/src/widget/components/QrCode.tsx +278 -17
  139. package/src/widget/components/QuoteDetails.tsx +93 -25
  140. package/src/widget/components/Receipt.tsx +296 -103
  141. package/src/widget/components/Receive.tsx +146 -0
  142. package/src/widget/components/RecentTokens.tsx +1 -1
  143. package/src/widget/components/RefundAddressInput.tsx +149 -0
  144. package/src/widget/components/Swap.tsx +769 -0
  145. package/src/widget/components/SwapDisplay.tsx +68 -0
  146. package/src/widget/components/TokenList.tsx +27 -363
  147. package/src/widget/components/TokenSelector.tsx +405 -0
  148. package/src/widget/components/TransferPendingVertical.tsx +162 -112
  149. package/src/widget/components/WalletConnect.tsx +9 -7
  150. package/src/widget/components/WalletConnectionPending.tsx +157 -0
  151. package/src/widget/components/WalletList.tsx +6 -5
  152. package/src/widget/components/YellowWarningAnimation.tsx +146 -0
  153. package/src/widget/hooks/useAmountUsd.ts +3 -8
  154. package/src/widget/hooks/useCheckout.ts +3 -2
  155. package/src/widget/hooks/useDebugScreens.ts +583 -0
  156. package/src/widget/hooks/useSendForm.ts +111 -35
  157. package/src/widget/hooks/useTokenList.ts +155 -122
  158. package/src/widget/widget.tsx +503 -523
@@ -1,4 +1,4 @@
1
- import type { TokenPrice } from "@0xsequence/api"
1
+ import type { TokenPrice } from "@0xsequence/trails-api"
2
2
  import type React from "react"
3
3
  import { useCallback, useEffect, useMemo, useState } from "react"
4
4
  import {
@@ -9,11 +9,9 @@ import {
9
9
  type WalletClient,
10
10
  zeroAddress,
11
11
  } from "viem"
12
- import { mainnet } from "viem/chains"
13
- import { useEnsAddress } from "wagmi"
14
12
  import { useAPIClient } from "../../apiClient.js"
15
13
  import { getChainInfo, useSupportedChains } from "../../chains.js"
16
- import { getFullErrorMessage } from "../../error.js"
14
+ import { getFullErrorMessage, getPrettifiedErrorMessage } from "../../error.js"
17
15
  import {
18
16
  prepareSend,
19
17
  TradeType,
@@ -37,6 +35,8 @@ import {
37
35
  useTokenInfo,
38
36
  } from "../../tokens.js"
39
37
  import type { CheckoutOnHandlers } from "./useCheckout.js"
38
+ import { useResolveEnsAddress } from "../../ens.js"
39
+ import * as chains from "viem/chains"
40
40
 
41
41
  export interface Token {
42
42
  id: number
@@ -100,6 +100,7 @@ export type UseSendProps = {
100
100
  toChainId?: number
101
101
  toToken?: string
102
102
  toCalldata?: string
103
+ refundAddress?: string
103
104
  walletClient: WalletClient
104
105
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
105
106
  onError: (error: Error | string | null) => void
@@ -109,12 +110,12 @@ export type UseSendProps = {
109
110
  onSend: (amount: string, recipient: string) => void
110
111
  onConfirm: () => void
111
112
  onComplete: (result: OnCompleteProps) => void
112
- selectedToken: Token
113
+ selectedToken?: Token
113
114
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
114
115
  tradeType?: TradeType
115
116
  quoteProvider?: string
116
- fundMethod?: string | null
117
- mode?: "pay" | "fund" | "earn"
117
+ fundMethod?: string
118
+ mode?: "pay" | "fund" | "earn" | "swap" | "receive"
118
119
  onNavigateToMeshConnect?: (
119
120
  props: {
120
121
  toTokenSymbol: string
@@ -125,6 +126,7 @@ export type UseSendProps = {
125
126
  quote?: PrepareSendQuote | null,
126
127
  ) => void
127
128
  checkoutOnHandlers?: CheckoutOnHandlers
129
+ refetchTrigger?: number
128
130
  }
129
131
 
130
132
  export type UseSendReturn = {
@@ -161,7 +163,7 @@ export type UseSendReturn = {
161
163
  isValidRecipient: boolean
162
164
  destTokenPrices: TokenPrice[] | null
163
165
  sourceTokenPrices: TokenPrice[] | null
164
- selectedToken: Token
166
+ selectedToken?: Token
165
167
  selectedFeeToken: TokenInfo | null
166
168
  setIsChainDropdownOpen: (isOpen: boolean) => void
167
169
  setIsTokenDropdownOpen: (isOpen: boolean) => void
@@ -170,6 +172,9 @@ export type UseSendReturn = {
170
172
  isValidCustomToken: boolean
171
173
  prepareSendQuote: PrepareSendQuote | null
172
174
  toAmountDisplay: string
175
+ quoteError: string | null
176
+ quoteErrorPrettified: string | null
177
+ isSameTokenWithoutCustomCalldata: boolean
173
178
  }
174
179
 
175
180
  export function useSendForm({
@@ -179,6 +184,7 @@ export function useSendForm({
179
184
  toChainId, // Custom specified destination chain id
180
185
  toToken, // Custom specified destination token address or symbol
181
186
  toCalldata, // Custom specified destination calldata
187
+ refundAddress, // Custom specified refund address
182
188
  walletClient,
183
189
  onTransactionStateChange,
184
190
  onError,
@@ -196,24 +202,36 @@ export function useSendForm({
196
202
  mode,
197
203
  onNavigateToMeshConnect,
198
204
  checkoutOnHandlers,
205
+ refetchTrigger = 0,
199
206
  }: UseSendProps): UseSendReturn {
207
+ // Auto-set quoteProvider to "lifi" if either from or to chain is etherlink
208
+ const effectiveQuoteProvider = useMemo(() => {
209
+ if (!quoteProvider || quoteProvider === "auto") {
210
+ if (
211
+ selectedToken?.chainId === chains.etherlink.id ||
212
+ toChainId === chains.etherlink.id
213
+ ) {
214
+ return "lifi"
215
+ }
216
+ }
217
+ return quoteProvider
218
+ }, [quoteProvider, selectedToken?.chainId, toChainId])
219
+
200
220
  const [amount, setAmount] = useState(
201
221
  tradeType === TradeType.EXACT_INPUT ? "" : (toAmount ?? ""),
202
222
  )
203
223
  const [recipientInput, setRecipientInput] = useState(toRecipient ?? "")
204
224
  const [recipient, setRecipient] = useState(toRecipient ?? "")
205
225
  const [error, setError] = useState<string | null>(null)
226
+ const [quoteError, setQuoteError] = useState<string | null>(null)
227
+ const [quoteErrorPrettified, setQuoteErrorPrettified] = useState<
228
+ string | null
229
+ >(null)
206
230
  const { supportedChains } = useSupportedChains()
207
- const { data: ensAddress } = useEnsAddress({
208
- name: recipientInput?.endsWith(".eth") ? recipientInput : undefined,
209
- chainId: mainnet.id,
210
- query: {
211
- enabled: !!recipientInput && recipientInput.endsWith(".eth"),
212
- },
231
+ const { ensAddress } = useResolveEnsAddress({
232
+ textInput: recipientInput,
213
233
  })
214
234
 
215
- console.log("GENERATED CALLLDATA", toCalldata)
216
-
217
235
  useEffect(() => {
218
236
  if (ensAddress) {
219
237
  setRecipient(ensAddress)
@@ -287,13 +305,17 @@ export function useSendForm({
287
305
  }, [errorCustomToken, isCustomToken, isLoadingCustomToken])
288
306
 
289
307
  const defaultDestToken = useMemo(() => {
308
+ if (mode === "swap") {
309
+ return null
310
+ }
311
+
290
312
  if (selectedDestinationChain) {
291
313
  return supportedTokens.find(
292
314
  (token) => token.chainId === selectedDestinationChain.id,
293
315
  )
294
316
  }
295
317
  return supportedTokens?.[0] as TokenInfo
296
- }, [supportedTokens, selectedDestinationChain])
318
+ }, [supportedTokens, selectedDestinationChain, mode])
297
319
 
298
320
  const [isChainDropdownOpen, setIsChainDropdownOpen] = useState(false)
299
321
  const [isTokenDropdownOpen, setIsTokenDropdownOpen] = useState(false)
@@ -366,7 +388,7 @@ export function useSendForm({
366
388
 
367
389
  // Update selectedDestToken when toToken prop changes
368
390
  useEffect(() => {
369
- if (toToken && !isCustomToken) {
391
+ if (toToken && !isCustomToken && selectedDestinationChain?.id) {
370
392
  const isToTokenAddress = isAddress(toToken)
371
393
  const newToken = supportedTokens.find(
372
394
  (token) =>
@@ -385,7 +407,7 @@ export function useSendForm({
385
407
  toToken,
386
408
  supportedTokens,
387
409
  toChainId,
388
- selectedDestinationChain.id,
410
+ selectedDestinationChain?.id,
389
411
  isCustomToken,
390
412
  ])
391
413
 
@@ -406,7 +428,7 @@ export function useSendForm({
406
428
  setRecipient(toRecipient ?? "")
407
429
  }, [toRecipient])
408
430
 
409
- const chainInfo = getChainInfo(selectedToken.chainId)
431
+ const chainInfo = getChainInfo(selectedToken?.chainId)
410
432
  const [isSubmitting, setIsSubmitting] = useState(false)
411
433
  const [isWaitingForWalletConfirm, setIsWaitingForWalletConfirm] =
412
434
  useState(false)
@@ -422,11 +444,13 @@ export function useSendForm({
422
444
  [onTransactionStateChange],
423
445
  )
424
446
 
425
- const balanceFormatted = formatRawAmount(
426
- selectedToken.balance,
427
- selectedToken.contractInfo?.decimals,
428
- )
429
- const balanceUsdDisplay = selectedToken.balanceUsdFormatted ?? ""
447
+ const balanceFormatted = selectedToken
448
+ ? formatRawAmount(
449
+ selectedToken.balance,
450
+ selectedToken.contractInfo?.decimals,
451
+ )
452
+ : "0"
453
+ const balanceUsdDisplay = selectedToken?.balanceUsdFormatted ?? ""
430
454
  const isValidRecipient = Boolean(recipient && isAddress(recipient))
431
455
 
432
456
  // Calculate USD value based on trade type
@@ -460,7 +484,7 @@ export function useSendForm({
460
484
 
461
485
  // Calculate raw amount (in wei/smallest unit)
462
486
  const amountRaw = useMemo(() => {
463
- if (!amount) {
487
+ if (!amount || !selectedToken?.contractInfo?.decimals) {
464
488
  return "0"
465
489
  }
466
490
 
@@ -472,6 +496,12 @@ export function useSendForm({
472
496
  : selectedDestToken?.decimals
473
497
 
474
498
  if (!decimals) {
499
+ console.warn("[trails-sdk] Missing token decimals for quote", {
500
+ decimals,
501
+ selectedToken,
502
+ selectedDestToken,
503
+ tradeType,
504
+ })
475
505
  return "0"
476
506
  }
477
507
 
@@ -482,8 +512,10 @@ export function useSendForm({
482
512
  }
483
513
  }, [
484
514
  amount,
515
+ selectedDestToken,
516
+ selectedToken,
485
517
  selectedDestToken?.decimals,
486
- selectedToken.contractInfo?.decimals,
518
+ selectedToken?.contractInfo?.decimals,
487
519
  tradeType,
488
520
  ])
489
521
 
@@ -498,8 +530,10 @@ export function useSendForm({
498
530
  !selectedDestinationChain ||
499
531
  amount === "0" ||
500
532
  !amountRaw ||
501
- amountRaw === "0"
533
+ amountRaw === "0" ||
534
+ !selectedToken
502
535
  ) {
536
+ setQuoteError(null)
503
537
  setPrepareSendResult(null)
504
538
  return
505
539
  }
@@ -507,6 +541,7 @@ export function useSendForm({
507
541
  try {
508
542
  setIsLoadingQuote(true)
509
543
  setError(null)
544
+ setQuoteError(null)
510
545
 
511
546
  const originRelayer = getRelayer(undefined, selectedToken.chainId)
512
547
  const destinationRelayer = getRelayer(
@@ -602,6 +637,7 @@ export function useSendForm({
602
637
  destinationTokenAddress,
603
638
  swapAmount: amountRaw,
604
639
  tradeType,
640
+ originTokenSymbol: selectedToken.symbol,
605
641
  destinationTokenSymbol: selectedDestToken.symbol,
606
642
  fee: "0",
607
643
  client: walletClient,
@@ -609,6 +645,7 @@ export function useSendForm({
609
645
  originRelayer,
610
646
  destinationRelayer,
611
647
  destinationCalldata: toCalldata,
648
+ refundAddress,
612
649
  dryMode: isDryMode,
613
650
  onTransactionStateChange: handleTransactionStateChange,
614
651
  sourceTokenPriceUsd,
@@ -621,7 +658,8 @@ export function useSendForm({
621
658
  )?.url ?? undefined,
622
659
  gasless,
623
660
  originNativeTokenPriceUsd: nativeTokenPriceUsd,
624
- quoteProvider,
661
+ quoteProvider: effectiveQuoteProvider,
662
+ mode,
625
663
  fundMethod,
626
664
  checkoutOnHandlers,
627
665
  }
@@ -634,6 +672,7 @@ export function useSendForm({
634
672
  setIsLoadingQuote(false)
635
673
  } catch (error) {
636
674
  console.error("[trails-sdk] Error getting quote:", error)
675
+ setQuoteError(getFullErrorMessage(error))
637
676
  setPrepareSendResult(null)
638
677
  setIsLoadingQuote(false)
639
678
  }
@@ -653,6 +692,7 @@ export function useSendForm({
653
692
  selectedToken?.balance,
654
693
  selectedToken?.tokenPriceUsd,
655
694
  toCalldata,
695
+ refundAddress,
656
696
  paymasterUrls,
657
697
  gasless,
658
698
  handleTransactionStateChange,
@@ -662,10 +702,11 @@ export function useSendForm({
662
702
  selectedDestToken,
663
703
  selectedDestinationChain,
664
704
  selectedToken,
665
- quoteProvider,
705
+ effectiveQuoteProvider,
666
706
  fundMethod,
667
707
  amountRaw,
668
708
  checkoutOnHandlers,
709
+ mode,
669
710
  ])
670
711
 
671
712
  // Auto-fetch quotes when inputs change (debounced)
@@ -695,6 +736,7 @@ export function useSendForm({
695
736
  selectedDestToken?.symbol,
696
737
  selectedDestinationChain?.id,
697
738
  toCalldata,
739
+ refetchTrigger,
698
740
  ])
699
741
 
700
742
  // Calculate destination amount from quote if available
@@ -734,7 +776,7 @@ export function useSendForm({
734
776
  quote.originAmountFormatted,
735
777
  ).toString() // MeshConnect will deposit origin token amount
736
778
  const toChainId = quote?.originChain?.id // MeshConnect will deposit to origin chain
737
- const toRecipientAddress = quote.originAddress // MeshConnect will deposit to origin address
779
+ const toRecipientAddress = quote.originDepositAddress // MeshConnect will deposit to origin address
738
780
 
739
781
  console.log("[trails-sdk] Navigating to mesh-connect with props:", {
740
782
  toTokenSymbol,
@@ -865,10 +907,12 @@ export function useSendForm({
865
907
 
866
908
  // Get button text based on recipient and calldata
867
909
  const buttonText = useMemo(() => {
910
+ if (!selectedToken) return "Select a token"
911
+ if (!amount) return "Enter an amount"
912
+ if (!selectedDestToken?.symbol) return "Select a token"
868
913
  if (isWaitingForWalletConfirm) return "Waiting for wallet..."
869
914
  if (isSubmitting) return "Processing..."
870
- if (!amount) return "Enter amount"
871
- if (!isValidRecipient) return "Enter recipient"
915
+ if (!isValidRecipient) return "Enter a recipient"
872
916
  if (isLoadingQuote) return "Getting quote..."
873
917
  if (!prepareSendResult) return "No quote available"
874
918
 
@@ -886,11 +930,15 @@ export function useSendForm({
886
930
  )
887
931
 
888
932
  try {
889
- const isSameChain = selectedToken.chainId === selectedDestinationChain.id
933
+ const isSameChain = selectedToken.chainId === selectedDestinationChain?.id
890
934
  const isSameToken = selectedToken.symbol === selectedDestToken.symbol
891
935
  const checksummedRecipient = getAddress(recipient)
892
936
  const checksummedAccount = getAddress(account.address)
893
937
 
938
+ if (mode === "swap") {
939
+ return `Swap ${amountDisplay} ${tokenSymbol}`
940
+ }
941
+
894
942
  if (fundMethod === "exchange") {
895
943
  return `Continue to Exchange`
896
944
  }
@@ -936,11 +984,36 @@ export function useSendForm({
936
984
  selectedToken,
937
985
  tradeType,
938
986
  prepareSendResult?.quote?.originAmountFormatted,
939
- selectedDestinationChain.id,
987
+ selectedDestinationChain?.id,
940
988
  mode,
941
989
  fundMethod,
942
990
  ])
943
991
 
992
+ useEffect(() => {
993
+ if (quoteError) {
994
+ setQuoteErrorPrettified(getPrettifiedErrorMessage(quoteError))
995
+ } else {
996
+ setQuoteErrorPrettified(null)
997
+ }
998
+ }, [quoteError])
999
+
1000
+ // Check if origin and destination tokens are the same (same contract address on same chain)
1001
+ // Only block same-token transactions when there's no custom calldata
1002
+ const isSameTokenWithoutCustomCalldata = useMemo(() => {
1003
+ if (!selectedToken || !destinationTokenAddress) return false
1004
+ const isSameChainAndToken =
1005
+ selectedToken.contractAddress.toLowerCase() ===
1006
+ destinationTokenAddress.toLowerCase() &&
1007
+ selectedToken.chainId === selectedDestinationChain?.id
1008
+ // Allow same-token transactions if there's custom calldata (e.g., NFT minting)
1009
+ return isSameChainAndToken && !toCalldata
1010
+ }, [
1011
+ selectedToken,
1012
+ destinationTokenAddress,
1013
+ selectedDestinationChain,
1014
+ toCalldata,
1015
+ ])
1016
+
944
1017
  return {
945
1018
  amount,
946
1019
  amountRaw,
@@ -984,5 +1057,8 @@ export function useSendForm({
984
1057
  destinationTokenAddress,
985
1058
  isValidCustomToken,
986
1059
  prepareSendQuote: prepareSendResult?.quote ?? null,
1060
+ quoteError,
1061
+ quoteErrorPrettified,
1062
+ isSameTokenWithoutCustomCalldata,
987
1063
  }
988
1064
  }