0xtrails 0.2.5 → 0.3.0

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 (267) hide show
  1. package/dist/aave.d.ts +2 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/abortController.d.ts +8 -0
  4. package/dist/abortController.d.ts.map +1 -0
  5. package/dist/{ccip-CXlshvBY.js → ccip-BMB3uDZt.js} +1 -1
  6. package/dist/config.d.ts +0 -5
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +4 -4
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/error.d.ts +4 -1
  11. package/dist/error.d.ts.map +1 -1
  12. package/dist/fees.d.ts +19 -0
  13. package/dist/fees.d.ts.map +1 -0
  14. package/dist/{index-_QuyGrjU.js → index-QXPUrZVv.js} +48719 -50852
  15. package/dist/index.d.ts +9 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +811 -784
  18. package/dist/intentReceiptMonitor.d.ts +24 -0
  19. package/dist/intentReceiptMonitor.d.ts.map +1 -0
  20. package/dist/intentReceiptPoller.d.ts +69 -0
  21. package/dist/intentReceiptPoller.d.ts.map +1 -0
  22. package/dist/intents.d.ts +15 -11
  23. package/dist/intents.d.ts.map +1 -1
  24. package/dist/morpho.d.ts +6 -5
  25. package/dist/morpho.d.ts.map +1 -1
  26. package/dist/mutations.d.ts +16 -0
  27. package/dist/mutations.d.ts.map +1 -0
  28. package/dist/preconditions.d.ts +5 -4
  29. package/dist/preconditions.d.ts.map +1 -1
  30. package/dist/prepareSend.d.ts +7 -258
  31. package/dist/prepareSend.d.ts.map +1 -1
  32. package/dist/prices.d.ts +9 -6
  33. package/dist/prices.d.ts.map +1 -1
  34. package/dist/sequenceWallet.d.ts +3 -16
  35. package/dist/sequenceWallet.d.ts.map +1 -1
  36. package/dist/tokenBalances.d.ts +17 -13
  37. package/dist/tokenBalances.d.ts.map +1 -1
  38. package/dist/trails.d.ts +24 -40
  39. package/dist/trails.d.ts.map +1 -1
  40. package/dist/transactionIntent/constants.d.ts +7 -0
  41. package/dist/transactionIntent/constants.d.ts.map +1 -0
  42. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +44 -0
  43. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -0
  44. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +30 -0
  45. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -0
  46. package/dist/transactionIntent/deposits/index.d.ts +4 -0
  47. package/dist/transactionIntent/deposits/index.d.ts.map +1 -0
  48. package/dist/transactionIntent/deposits/standardDeposit.d.ts +30 -0
  49. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -0
  50. package/dist/transactionIntent/execution/index.d.ts +2 -0
  51. package/dist/transactionIntent/execution/index.d.ts.map +1 -0
  52. package/dist/transactionIntent/execution/transactionState.d.ts +5 -0
  53. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -0
  54. package/dist/transactionIntent/handlers/crossChain.d.ts +82 -0
  55. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -0
  56. package/dist/transactionIntent/handlers/index.d.ts +4 -0
  57. package/dist/transactionIntent/handlers/index.d.ts.map +1 -0
  58. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +62 -0
  59. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +1 -0
  60. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +72 -0
  61. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -0
  62. package/dist/transactionIntent/index.d.ts +9 -0
  63. package/dist/transactionIntent/index.d.ts.map +1 -0
  64. package/dist/transactionIntent/quote/feeExtractors.d.ts +17 -0
  65. package/dist/transactionIntent/quote/feeExtractors.d.ts.map +1 -0
  66. package/dist/transactionIntent/quote/index.d.ts +4 -0
  67. package/dist/transactionIntent/quote/index.d.ts.map +1 -0
  68. package/dist/transactionIntent/quote/normalizeQuote.d.ts +34 -0
  69. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -0
  70. package/dist/transactionIntent/quote/quoteHelpers.d.ts +5 -0
  71. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -0
  72. package/dist/transactionIntent/types.d.ts +131 -0
  73. package/dist/transactionIntent/types.d.ts.map +1 -0
  74. package/dist/transactionIntent/utils/balanceChecker.d.ts +18 -0
  75. package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -0
  76. package/dist/transactionIntent/utils/index.d.ts +4 -0
  77. package/dist/transactionIntent/utils/index.d.ts.map +1 -0
  78. package/dist/transactionIntent/utils/lifiHelpers.d.ts +10 -0
  79. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +1 -0
  80. package/dist/transactionIntent/utils/testnetHelpers.d.ts +3 -0
  81. package/dist/transactionIntent/utils/testnetHelpers.d.ts.map +1 -0
  82. package/dist/transactionIntent/validators.d.ts +6 -0
  83. package/dist/transactionIntent/validators.d.ts.map +1 -0
  84. package/dist/transactions.d.ts +6 -3
  85. package/dist/transactions.d.ts.map +1 -1
  86. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  87. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  88. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  89. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  90. package/dist/widget/components/ClassicSwap.d.ts +2 -3
  91. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  92. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  93. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  94. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  95. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  96. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  97. package/dist/widget/components/DynamicSizeInputField.d.ts +13 -0
  98. package/dist/widget/components/DynamicSizeInputField.d.ts.map +1 -0
  99. package/dist/widget/components/Earn.d.ts +2 -3
  100. package/dist/widget/components/Earn.d.ts.map +1 -1
  101. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  102. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  103. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  104. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  105. package/dist/widget/components/FeeOptions.d.ts +5 -13
  106. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  107. package/dist/widget/components/Fund.d.ts +2 -3
  108. package/dist/widget/components/Fund.d.ts.map +1 -1
  109. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  110. package/dist/widget/components/FundSwap.d.ts +2 -3
  111. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  112. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  113. package/dist/widget/components/Identicon.d.ts.map +1 -1
  114. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  115. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  116. package/dist/widget/components/Modal.d.ts.map +1 -1
  117. package/dist/widget/components/Pay.d.ts +2 -3
  118. package/dist/widget/components/Pay.d.ts.map +1 -1
  119. package/dist/widget/components/PoolDeposit.d.ts +3 -3
  120. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  121. package/dist/widget/components/PoolWithdraw.d.ts +3 -20
  122. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  123. package/dist/widget/components/QuoteDetails.d.ts +2 -0
  124. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  125. package/dist/widget/components/Receipt.d.ts.map +1 -1
  126. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  127. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  128. package/dist/widget/components/Swap.d.ts +2 -3
  129. package/dist/widget/components/Swap.d.ts.map +1 -1
  130. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  131. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  132. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  133. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  134. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  135. package/dist/widget/components/Tooltip.d.ts +9 -0
  136. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  137. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  138. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  139. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  140. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  141. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  142. package/dist/widget/css/compiled.css +2 -2
  143. package/dist/widget/hooks/useCheckout.d.ts +17 -4
  144. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  145. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  146. package/dist/widget/hooks/useQuote.d.ts +82 -0
  147. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  148. package/dist/widget/hooks/useSelectedFeeToken.d.ts +1 -0
  149. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  150. package/dist/widget/hooks/useSendForm.d.ts +5 -6
  151. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  152. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  153. package/dist/widget/hooks/useWalletConnectionContext.d.ts +25 -0
  154. package/dist/widget/hooks/useWalletConnectionContext.d.ts.map +1 -0
  155. package/dist/widget/index.js +2 -2
  156. package/dist/widget/widget.d.ts +17 -7
  157. package/dist/widget/widget.d.ts.map +1 -1
  158. package/package.json +19 -21
  159. package/src/aave.ts +54 -1
  160. package/src/abortController.ts +35 -0
  161. package/src/config.ts +57 -58
  162. package/src/constants.ts +11 -9
  163. package/src/error.ts +21 -3
  164. package/src/fees.ts +210 -0
  165. package/src/index.ts +35 -13
  166. package/src/intentReceiptMonitor.ts +102 -0
  167. package/src/intentReceiptPoller.ts +299 -0
  168. package/src/intents.ts +205 -171
  169. package/src/morpho.ts +58 -9
  170. package/src/mutations.ts +129 -0
  171. package/src/preconditions.ts +16 -21
  172. package/src/prepareSend.ts +92 -4699
  173. package/src/prices.ts +26 -22
  174. package/src/relaySdk.ts +2 -2
  175. package/src/sequenceWallet.ts +6 -73
  176. package/src/tokenBalances.ts +175 -69
  177. package/src/trails.ts +230 -722
  178. package/src/transactionIntent/constants.ts +11 -0
  179. package/src/transactionIntent/deposits/depositOrchestrator.ts +210 -0
  180. package/src/transactionIntent/deposits/gaslessDeposit.ts +588 -0
  181. package/src/transactionIntent/deposits/index.ts +3 -0
  182. package/src/transactionIntent/deposits/standardDeposit.ts +379 -0
  183. package/src/transactionIntent/execution/index.ts +1 -0
  184. package/src/transactionIntent/execution/transactionState.ts +35 -0
  185. package/src/transactionIntent/handlers/crossChain.ts +1707 -0
  186. package/src/transactionIntent/handlers/index.ts +3 -0
  187. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +323 -0
  188. package/src/transactionIntent/handlers/sameChainSameToken.ts +712 -0
  189. package/src/transactionIntent/index.ts +9 -0
  190. package/src/transactionIntent/quote/feeExtractors.ts +81 -0
  191. package/src/transactionIntent/quote/index.ts +3 -0
  192. package/src/transactionIntent/quote/normalizeQuote.ts +367 -0
  193. package/src/transactionIntent/quote/quoteHelpers.ts +53 -0
  194. package/src/transactionIntent/types.ts +157 -0
  195. package/src/transactionIntent/utils/balanceChecker.ts +96 -0
  196. package/src/transactionIntent/utils/index.ts +3 -0
  197. package/src/transactionIntent/utils/lifiHelpers.ts +68 -0
  198. package/src/transactionIntent/utils/testnetHelpers.ts +10 -0
  199. package/src/transactionIntent/validators.ts +57 -0
  200. package/src/transactions.ts +98 -71
  201. package/src/widget/compiled.css +2 -2
  202. package/src/widget/components/AccountIntentTransactionHistory.tsx +36 -36
  203. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  204. package/src/widget/components/AccountSettings.tsx +70 -41
  205. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  206. package/src/widget/components/ClassicSwap.tsx +44 -107
  207. package/src/widget/components/ConfigDisplay.tsx +0 -11
  208. package/src/widget/components/ConnectWallet.tsx +4 -1
  209. package/src/widget/components/ConnectedWallets.tsx +51 -25
  210. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  211. package/src/widget/components/DynamicSizeInputField.tsx +109 -0
  212. package/src/widget/components/Earn.tsx +34 -45
  213. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  214. package/src/widget/components/FeeBreakdown.tsx +155 -0
  215. package/src/widget/components/FeeOption.tsx +2 -2
  216. package/src/widget/components/FeeOptions.tsx +151 -112
  217. package/src/widget/components/Fund.tsx +10 -29
  218. package/src/widget/components/FundMethods.tsx +4 -3
  219. package/src/widget/components/FundSwap.tsx +2 -3
  220. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  221. package/src/widget/components/Identicon.tsx +164 -95
  222. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  223. package/src/widget/components/Modal.tsx +0 -12
  224. package/src/widget/components/Pay.tsx +72 -75
  225. package/src/widget/components/PoolDeposit.tsx +221 -242
  226. package/src/widget/components/PoolWithdraw.tsx +347 -469
  227. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  228. package/src/widget/components/QuoteDetails.tsx +906 -484
  229. package/src/widget/components/Receipt.tsx +16 -2
  230. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  231. package/src/widget/components/Recipients.tsx +1 -1
  232. package/src/widget/components/ScreenHeader.tsx +60 -36
  233. package/src/widget/components/Swap.tsx +2 -3
  234. package/src/widget/components/ThemeProvider.tsx +2 -1
  235. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  236. package/src/widget/components/TokenImage.tsx +1 -1
  237. package/src/widget/components/TokenSelector.tsx +62 -53
  238. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  239. package/src/widget/components/Tooltip.tsx +51 -0
  240. package/src/widget/components/TransferPendingVertical.tsx +12 -8
  241. package/src/widget/components/WaasFeeOptions.tsx +139 -4
  242. package/src/widget/components/WalletConfirmation.tsx +23 -13
  243. package/src/widget/components/WalletConnect.tsx +93 -29
  244. package/src/widget/hooks/useAmountUsd.ts +9 -9
  245. package/src/widget/hooks/useCheckout.ts +97 -9
  246. package/src/widget/hooks/useDefaultTokenSelection.tsx +27 -21
  247. package/src/widget/hooks/useQuote.ts +466 -0
  248. package/src/widget/hooks/useSelectedFeeToken.tsx +32 -37
  249. package/src/widget/hooks/useSendForm.ts +45 -51
  250. package/src/widget/hooks/useTokenList.ts +34 -26
  251. package/src/widget/hooks/useWalletConnectionContext.tsx +128 -0
  252. package/src/widget/widget.tsx +365 -390
  253. package/dist/apiClient.d.ts +0 -9
  254. package/dist/apiClient.d.ts.map +0 -1
  255. package/dist/intentEntrypoint.d.ts +0 -114
  256. package/dist/intentEntrypoint.d.ts.map +0 -1
  257. package/dist/metaTxnMonitor.d.ts +0 -15
  258. package/dist/metaTxnMonitor.d.ts.map +0 -1
  259. package/dist/metaTxns.d.ts +0 -11
  260. package/dist/metaTxns.d.ts.map +0 -1
  261. package/dist/relayer.d.ts +0 -43
  262. package/dist/relayer.d.ts.map +0 -1
  263. package/src/apiClient.ts +0 -35
  264. package/src/intentEntrypoint.ts +0 -203
  265. package/src/metaTxnMonitor.ts +0 -171
  266. package/src/metaTxns.ts +0 -45
  267. package/src/relayer.ts +0 -289
@@ -1,5 +1,7 @@
1
1
  import { TokenImage } from "./TokenImage.js"
2
- import { InfoIcon, Tooltip } from "@0xsequence/design-system"
2
+ import { ChainImage } from "./ChainImage.js"
3
+ import { InfoIcon } from "@0xsequence/design-system"
4
+ import { Tooltip } from "./Tooltip.js"
3
5
  import type React from "react"
4
6
  import { getExplorerUrlForAddress } from "../../explorer.js"
5
7
  import type { PrepareSendQuote } from "../../prepareSend.js"
@@ -7,6 +9,7 @@ import { useState, useEffect, useRef } from "react"
7
9
  import { truncateAddress } from "../../utils.js"
8
10
  import { PriceImpactWarning } from "./PriceImpactWarning.js"
9
11
  import { usePriceImpactWarning } from "../hooks/usePriceImpactWarning.js"
12
+ import { formatUsdAmountDisplay } from "../../tokenBalances.js"
10
13
 
11
14
  interface QuoteDetailsProps {
12
15
  quote?: PrepareSendQuote | null
@@ -14,6 +17,8 @@ interface QuoteDetailsProps {
14
17
  children?: React.ReactNode
15
18
  onExpand?: (isExpanded: boolean) => void
16
19
  swapMode?: boolean
20
+ compact?: boolean
21
+ isRefetching?: boolean
17
22
  }
18
23
 
19
24
  // Helper function to format completion time
@@ -44,12 +49,16 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
44
49
  children,
45
50
  onExpand,
46
51
  swapMode,
52
+ compact = false,
53
+ isRefetching = false,
47
54
  }) => {
48
55
  const [showCalldata, setShowCalldata] = useState(false)
49
56
  const [showOriginRate, setShowOriginRate] = useState(true)
50
57
  const [isExpanded, setIsExpanded] = useState(false)
58
+ const [showMoreInfo, setShowMoreInfo] = useState(false)
51
59
  const containerRef = useRef<HTMLDivElement>(null)
52
60
  const calldataRef = useRef<HTMLDivElement>(null)
61
+ const moreInfoRef = useRef<HTMLDivElement>(null)
53
62
 
54
63
  const priceImpactConfig = usePriceImpactWarning()
55
64
 
@@ -72,7 +81,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
72
81
  if (!showContent) return null
73
82
 
74
83
  return (
75
- <div className="relative">
84
+ <div
85
+ className={`relative transition-opacity duration-300 ${isRefetching ? "opacity-50" : ""}`}
86
+ >
76
87
  {/* Price Impact Warning - always visible when conditions are met */}
77
88
  <PriceImpactWarning
78
89
  quote={quote}
@@ -81,6 +92,35 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
81
92
  fallbackBridgeUrl={priceImpactConfig.fallbackBridgeUrl}
82
93
  />
83
94
 
95
+ {/* Insufficient Balance Warning - always visible when conditions are met */}
96
+ {quote?.noSufficientBalance && (
97
+ <div className="flex justify-between items-center mb-2 p-2 bg-amber-50 dark:bg-amber-900/20 rounded-lg">
98
+ <span className="text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1">
99
+ Balance Status:
100
+ <Tooltip message="Your wallet does not have sufficient balance to complete this transaction">
101
+ <InfoIcon className="w-3 h-3 text-amber-500 dark:text-amber-400 cursor-pointer" />
102
+ </Tooltip>
103
+ </span>
104
+ <span className="font-medium text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1">
105
+ <svg
106
+ className="w-3 h-3"
107
+ fill="none"
108
+ stroke="currentColor"
109
+ viewBox="0 0 24 24"
110
+ aria-hidden="true"
111
+ >
112
+ <path
113
+ strokeLinecap="round"
114
+ strokeLinejoin="round"
115
+ strokeWidth={2}
116
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
117
+ />
118
+ </svg>
119
+ Insufficient Balance
120
+ </span>
121
+ </div>
122
+ )}
123
+
84
124
  {/* More Details Button - only visible when collapsed */}
85
125
  {!isExpanded && (
86
126
  <div className={`flex justify-center`}>
@@ -88,7 +128,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
88
128
  <button
89
129
  type="button"
90
130
  onClick={() => setIsExpanded(true)}
91
- className="w-full flex items-center justify-between py-2 px-4 trails-border-radius-button transition-colors cursor-pointer text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-600"
131
+ className="w-full flex items-center justify-between py-2 px-4 trails-border-radius-button transition-colors cursor-pointer text-xs"
92
132
  aria-label="Show more details"
93
133
  >
94
134
  <div className="flex items-center gap-2 text-gray-600 dark:text-gray-400 whitespace-nowrap max-w-48 truncate">
@@ -161,7 +201,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
161
201
  <button
162
202
  type="button"
163
203
  onClick={() => setIsExpanded(true)}
164
- className="w-full max-w-md flex items-center justify-between gap-2 py-1 px-1 trails-border-radius-button transition-colors cursor-pointer text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
204
+ className={`w-full max-w-md flex items-center ${compact ? "justify-center" : "justify-between"} gap-2 py-1 px-1 trails-border-radius-button transition-colors cursor-pointer text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300`}
165
205
  aria-label="Show transaction details"
166
206
  >
167
207
  <span>Transaction details</span>
@@ -195,9 +235,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
195
235
  className={`text-sm ${swapMode ? "p-2 space-y-2" : "p-4 rounded-lg space-y-4 trails-bg-secondary"}`}
196
236
  >
197
237
  {/* Close Button - only visible when expanded, at top center */}
198
- <div
199
- className={`flex justify-center ${swapMode ? "mb-2 -mt-1" : "mb-4 -mt-2"}`}
200
- >
238
+ <div className={`flex justify-center mb-4 -mt-2`}>
201
239
  {swapMode ? (
202
240
  <button
203
241
  type="button"
@@ -246,54 +284,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
246
284
  )}
247
285
  </div>
248
286
  <div className="space-y-3">
249
- {/* Insufficient Balance Warning */}
250
- {quote?.noSufficientBalance && (
251
- <div className="flex justify-between items-center">
252
- <span className="text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1">
253
- Balance Status:
254
- <Tooltip message="Your wallet does not have sufficient balance to complete this transaction">
255
- <InfoIcon className="w-3 h-3 text-amber-500 dark:text-amber-400 cursor-pointer" />
256
- </Tooltip>
257
- </span>
258
- <span className="font-medium text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1">
259
- <svg
260
- className="w-3 h-3"
261
- fill="none"
262
- stroke="currentColor"
263
- viewBox="0 0 24 24"
264
- aria-hidden="true"
265
- >
266
- <path
267
- strokeLinecap="round"
268
- strokeLinejoin="round"
269
- strokeWidth={2}
270
- d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
271
- />
272
- </svg>
273
- Insufficient Balance
274
- </span>
275
- </div>
276
- )}
277
-
278
- {quote?.originTokenRate && quote?.destinationTokenRate && (
279
- <div className="flex justify-between items-center">
280
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
281
- Exchange Rate:
282
- <Tooltip message="The current exchange rate between the origin and destination tokens">
283
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
284
- </Tooltip>
285
- </span>
286
- <button
287
- type="button"
288
- onClick={() => setShowOriginRate(!showOriginRate)}
289
- className="font-medium text-xs hover:underline cursor-pointer text-gray-900 hover:text-gray-700 dark:text-white dark:hover:text-gray-300"
290
- >
291
- {showOriginRate
292
- ? quote.originTokenRate
293
- : quote.destinationTokenRate}
294
- </button>
295
- </div>
296
- )}
287
+ {/* Estimated Time */}
297
288
  {quote?.completionEstimateSeconds != null && (
298
289
  <div className="flex justify-between items-center">
299
290
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
@@ -322,465 +313,896 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
322
313
  </div>
323
314
  )}
324
315
 
325
- {quote?.gasCostUsd != null && quote?.gasCostUsd > 0 && (
326
- <div className="flex justify-between items-center">
327
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
328
- Network Cost:
329
- <Tooltip message="The estimated gas cost for executing this transaction on the origin chain">
330
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
331
- </Tooltip>
332
- </span>
333
- <span className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1">
334
- <svg
335
- aria-hidden="true"
336
- focusable="false"
337
- data-prefix="fas"
338
- data-icon="gas-pump"
339
- className="svg-inline--fa fa-gas-pump "
340
- role="img"
341
- xmlns="http://www.w3.org/2000/svg"
342
- viewBox="0 0 512 512"
343
- width="12"
344
- >
345
- <path
346
- fill="currentColor"
347
- d="M32 64C32 28.7 60.7 0 96 0H256c35.3 0 64 28.7 64 64V256h8c48.6 0 88 39.4 88 88v32c0 13.3 10.7 24 24 24s24-10.7 24-24V222c-27.6-7.1-48-32.2-48-62V96L384 64c-8.8-8.8-8.8-23.2 0-32s23.2-8.8 32 0l77.3 77.3c12 12 18.7 28.3 18.7 45.3V168v24 32V376c0 39.8-32.2 72-72 72s-72-32.2-72-72V344c0-22.1-17.9-40-40-40h-8V448c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32V64zM96 80v96c0 8.8 7.2 16 16 16H240c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H112c-8.8 0-16 7.2-16 16z"
348
- ></path>
349
- </svg>
350
- {quote.gasCostUsdDisplay}
351
- </span>
352
- </div>
353
- )}
316
+ {/* Spacer */}
317
+ <div className="h-2" />
354
318
 
355
- {quote?.originAmount &&
356
- quote?.originToken.symbol &&
357
- quote?.originChain.id && (
358
- <div className="flex justify-between items-start">
359
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
360
- Origin Transfer Amount:
361
- <Tooltip message="The amount of tokens you will send from the origin chain">
362
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
363
- </Tooltip>
364
- </span>
365
- <div className="text-right">
366
- {quote.originToken.contractAddress ===
367
- "0x0000000000000000000000000000000000000000" ? (
368
- <>
369
- <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
370
- <TokenImage
371
- imageUrl={quote.originToken.imageUrl}
372
- symbol={quote.originToken.symbol}
373
- chainId={quote.originChain.id}
374
- contractAddress={quote.originToken.contractAddress}
375
- size={16}
319
+ {/* Gas Costs Section */}
320
+ {quote?.trailsFeeBreakdown &&
321
+ (quote?.trailsFeeBreakdown.originRelayFee ||
322
+ quote?.trailsFeeBreakdown.destinationRelayFee) && (
323
+ <>
324
+ {/* Origin Gas */}
325
+ {quote.trailsFeeBreakdown.originRelayFee && (
326
+ <div className="flex justify-between items-center">
327
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
328
+ Origin Gas
329
+ <ChainImage chainId={quote.originChain.id} size={16} />:
330
+ <Tooltip message="Gas fee for executing the transaction on the origin chain">
331
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
332
+ </Tooltip>
333
+ </span>
334
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
335
+ {quote.trailsFeeBreakdown.originRelayFee.usdValue}
336
+ </span>
337
+ </div>
338
+ )}
339
+
340
+ {/* Destination Gas */}
341
+ {quote.trailsFeeBreakdown.destinationRelayFee && (
342
+ <div className="flex justify-between items-center">
343
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
344
+ Destination Gas
345
+ <ChainImage
346
+ chainId={quote.destinationChain.id}
347
+ size={16}
348
+ />
349
+ :
350
+ <Tooltip message="Gas fee for executing the transaction on the destination chain">
351
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
352
+ </Tooltip>
353
+ </span>
354
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
355
+ {quote.trailsFeeBreakdown.destinationRelayFee.usdValue}
356
+ </span>
357
+ </div>
358
+ )}
359
+
360
+ {/* All Gas Costs */}
361
+ {(quote.trailsFeeBreakdown.originRelayFee ||
362
+ quote.trailsFeeBreakdown.destinationRelayFee) && (
363
+ <div className="flex justify-between items-center">
364
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
365
+ All gas costs:
366
+ <Tooltip message="Total gas costs for executing transactions on both origin and destination chains">
367
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
368
+ </Tooltip>
369
+ </span>
370
+ <span className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 pt-2 border-t border-gray-200 dark:border-gray-700">
371
+ <svg
372
+ aria-hidden="true"
373
+ focusable="false"
374
+ data-prefix="fas"
375
+ data-icon="gas-pump"
376
+ className="svg-inline--fa fa-gas-pump"
377
+ role="img"
378
+ xmlns="http://www.w3.org/2000/svg"
379
+ viewBox="0 0 512 512"
380
+ width="12"
381
+ >
382
+ <path
383
+ fill="currentColor"
384
+ d="M32 64C32 28.7 60.7 0 96 0H256c35.3 0 64 28.7 64 64V256h8c48.6 0 88 39.4 88 88v32c0 13.3 10.7 24 24 24s24-10.7 24-24V222c-27.6-7.1-48-32.2-48-62V96L384 64c-8.8-8.8-8.8-23.2 0-32s23.2-8.8 32 0l77.3 77.3c12 12 18.7 28.3 18.7 45.3V168v24 32V376c0 39.8-32.2 72-72 72s-72-32.2-72-72V344c0-22.1-17.9-40-40-40h-8V448c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32V64zM96 80v96c0 8.8 7.2 16 16 16H240c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H112c-8.8 0-16 7.2-16 16z"
376
385
  />
377
- {quote.originAmountDisplay} {quote.originToken.symbol}
386
+ </svg>
387
+ {(() => {
388
+ const originGasUsd = parseFloat(
389
+ quote.trailsFeeBreakdown.originRelayFee?.usdValue?.replace(
390
+ /[^0-9.-]/g,
391
+ "",
392
+ ) || "0",
393
+ )
394
+ const destGasUsd = parseFloat(
395
+ quote.trailsFeeBreakdown.destinationRelayFee?.usdValue?.replace(
396
+ /[^0-9.-]/g,
397
+ "",
398
+ ) || "0",
399
+ )
400
+ const totalGasUsd = originGasUsd + destGasUsd
401
+ return formatUsdAmountDisplay(totalGasUsd)
402
+ })()}
403
+ </span>
404
+ </div>
405
+ )}
406
+
407
+ {/* Spacer */}
408
+ <div className="h-2" />
409
+ </>
410
+ )}
411
+
412
+ {/* Provider Costs Section */}
413
+ {(() => {
414
+ if (!quote?.trailsFeeBreakdown) return null
415
+
416
+ // Check if provider fee is non-zero
417
+ const hasProviderFee =
418
+ quote.trailsFeeBreakdown.providerFee &&
419
+ parseFloat(
420
+ quote.trailsFeeBreakdown.providerFee.usdValue?.replace(
421
+ /[^0-9.-]/g,
422
+ "",
423
+ ) || "0",
424
+ ) !== 0
425
+
426
+ // Check if trails fee is non-zero
427
+ const hasTrailsFee =
428
+ quote.trailsFeeBreakdown.trailsFee &&
429
+ parseFloat(
430
+ quote.trailsFeeBreakdown.trailsFee.usdValue?.replace(
431
+ /[^0-9.-]/g,
432
+ "",
433
+ ) || "0",
434
+ ) !== 0
435
+
436
+ if (!hasProviderFee && !hasTrailsFee) return null
437
+
438
+ return (
439
+ <>
440
+ {/* Bridge/Swap Provider Fee */}
441
+ {hasProviderFee &&
442
+ quote.trailsFeeBreakdown.providerFee &&
443
+ (() => {
444
+ const providerFee = quote.trailsFeeBreakdown.providerFee
445
+ const isCrossChain =
446
+ quote.originChain.id !== quote.destinationChain.id
447
+ const providerName = quote.quoteProvider?.name || ""
448
+
449
+ return (
450
+ <div className="flex justify-between items-center">
451
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
452
+ {isCrossChain ? (
453
+ providerName ? (
454
+ <>
455
+ Bridge (
456
+ <a
457
+ href={quote.quoteProvider?.url}
458
+ target="_blank"
459
+ rel="noopener noreferrer"
460
+ className="hover:underline flex items-center gap-0.5"
461
+ >
462
+ {providerName}
463
+ <svg
464
+ className="w-3 h-3"
465
+ fill="none"
466
+ stroke="currentColor"
467
+ viewBox="0 0 24 24"
468
+ aria-hidden="true"
469
+ >
470
+ <path
471
+ strokeLinecap="round"
472
+ strokeLinejoin="round"
473
+ strokeWidth={2}
474
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
475
+ />
476
+ </svg>
477
+ </a>
478
+ ):
479
+ </>
480
+ ) : (
481
+ <>Bridge:</>
482
+ )
483
+ ) : providerName ? (
484
+ <>
485
+ Liquidity Provider (
486
+ <a
487
+ href={quote.quoteProvider?.url}
488
+ target="_blank"
489
+ rel="noopener noreferrer"
490
+ className="hover:underline flex items-center gap-0.5"
491
+ >
492
+ {providerName}
493
+ <svg
494
+ className="w-3 h-3"
495
+ fill="none"
496
+ stroke="currentColor"
497
+ viewBox="0 0 24 24"
498
+ aria-hidden="true"
499
+ >
500
+ <path
501
+ strokeLinecap="round"
502
+ strokeLinejoin="round"
503
+ strokeWidth={2}
504
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
505
+ />
506
+ </svg>
507
+ </a>
508
+ ):
509
+ </>
510
+ ) : (
511
+ <>Liquidity Provider:</>
512
+ )}
513
+ <Tooltip message="Fee charged by the bridge/swap provider for executing the transaction">
514
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
515
+ </Tooltip>
516
+ </span>
517
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
518
+ {providerFee.usdValue}
519
+ </span>
378
520
  </div>
379
- {quote.originAmountUsdDisplay && (
380
- <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
381
- ≈ {quote.originAmountUsdDisplay}
382
- </div>
383
- )}
384
- </>
385
- ) : (
386
- <>
387
- <a
388
- href={getExplorerUrlForAddress({
389
- address: quote.originToken.contractAddress,
390
- chainId: quote.originChain.id,
391
- })}
392
- target="_blank"
393
- rel="noopener noreferrer"
394
- className="hover:underline cursor-pointer"
521
+ )
522
+ })()}
523
+
524
+ {/* Swap Fees (implicit costs) */}
525
+ {(() => {
526
+ // Calculate swap fees: (originAmount - destinationAmount) - totalFees
527
+ const originUsd = parseFloat(
528
+ quote.originAmountUsdDisplay?.replace(/[^0-9.-]/g, "") ||
529
+ "0",
530
+ )
531
+ const destUsd = parseFloat(
532
+ quote.destinationAmountUsdDisplay?.replace(
533
+ /[^0-9.-]/g,
534
+ "",
535
+ ) || "0",
536
+ )
537
+ const totalFeesUsd = parseFloat(
538
+ quote.fees?.totalFeeAmountUsdDisplay?.replace(
539
+ /[^0-9.-]/g,
540
+ "",
541
+ ) || "0",
542
+ )
543
+
544
+ const swapFeesUsd = originUsd - destUsd - totalFeesUsd
545
+
546
+ // Only show if swap fees > 0
547
+ if (swapFeesUsd <= 0) return null
548
+
549
+ return (
550
+ <div className="flex justify-between items-center">
551
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
552
+ Swap:
553
+ <Tooltip message="Implicit costs including DEX fees, liquidity provider fees, and routing costs">
554
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
555
+ </Tooltip>
556
+ </span>
557
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
558
+ {formatUsdAmountDisplay(swapFeesUsd)}
559
+ </span>
560
+ </div>
561
+ )
562
+ })()}
563
+
564
+ {/* Trails Platform Fee */}
565
+ {hasTrailsFee &&
566
+ quote.trailsFeeBreakdown.trailsFee &&
567
+ (() => {
568
+ const trailsFee = quote.trailsFeeBreakdown.trailsFee
569
+
570
+ return (
571
+ <div className="flex justify-between items-center">
572
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
573
+ Trails:
574
+ <Tooltip message="Platform fee for using the Trails service">
575
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
576
+ </Tooltip>
577
+ </span>
578
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
579
+ {trailsFee.usdValue}
580
+ </span>
581
+ </div>
582
+ )
583
+ })()}
584
+
585
+ {/* All Provider Costs */}
586
+ {(hasProviderFee || hasTrailsFee) &&
587
+ (() => {
588
+ const providerUsd = parseFloat(
589
+ quote.trailsFeeBreakdown.providerFee?.usdValue?.replace(
590
+ /[^0-9.-]/g,
591
+ "",
592
+ ) || "0",
593
+ )
594
+ const trailsUsd = parseFloat(
595
+ quote.trailsFeeBreakdown.trailsFee?.usdValue?.replace(
596
+ /[^0-9.-]/g,
597
+ "",
598
+ ) || "0",
599
+ )
600
+
601
+ // Calculate swap fees
602
+ const originUsd = parseFloat(
603
+ quote.originAmountUsdDisplay?.replace(
604
+ /[^0-9.-]/g,
605
+ "",
606
+ ) || "0",
607
+ )
608
+ const destUsd = parseFloat(
609
+ quote.destinationAmountUsdDisplay?.replace(
610
+ /[^0-9.-]/g,
611
+ "",
612
+ ) || "0",
613
+ )
614
+ const totalFeesUsd = parseFloat(
615
+ quote.fees?.totalFeeAmountUsdDisplay?.replace(
616
+ /[^0-9.-]/g,
617
+ "",
618
+ ) || "0",
619
+ )
620
+ const swapFeesUsd = Math.max(
621
+ 0,
622
+ originUsd - destUsd - totalFeesUsd,
623
+ )
624
+
625
+ const totalProviderUsd =
626
+ providerUsd + trailsUsd + swapFeesUsd
627
+
628
+ return (
629
+ <div className="flex justify-between items-center">
630
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
631
+ All provider costs:
632
+ <Tooltip message="Total fees charged by providers, including bridge/swap, platform fees, and implicit swap costs">
633
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
634
+ </Tooltip>
635
+ </span>
636
+ <span className="font-medium text-xs text-gray-900 dark:text-white pt-2 border-t border-gray-200 dark:border-gray-700">
637
+ {formatUsdAmountDisplay(totalProviderUsd)}
638
+ </span>
639
+ </div>
640
+ )
641
+ })()}
642
+
643
+ {/* Spacer */}
644
+ <div className="h-2" />
645
+
646
+ {/* Total Fees */}
647
+ {(() => {
648
+ // Calculate total gas costs
649
+ const originGasUsd = parseFloat(
650
+ quote.trailsFeeBreakdown?.originRelayFee?.usdValue?.replace(
651
+ /[^0-9.-]/g,
652
+ "",
653
+ ) || "0",
654
+ )
655
+ const destGasUsd = parseFloat(
656
+ quote.trailsFeeBreakdown?.destinationRelayFee?.usdValue?.replace(
657
+ /[^0-9.-]/g,
658
+ "",
659
+ ) || "0",
660
+ )
661
+ const totalGasUsd = originGasUsd + destGasUsd
662
+
663
+ // Calculate total provider costs (including swap fees)
664
+ const providerUsd = parseFloat(
665
+ quote.trailsFeeBreakdown?.providerFee?.usdValue?.replace(
666
+ /[^0-9.-]/g,
667
+ "",
668
+ ) || "0",
669
+ )
670
+ const trailsUsd = parseFloat(
671
+ quote.trailsFeeBreakdown?.trailsFee?.usdValue?.replace(
672
+ /[^0-9.-]/g,
673
+ "",
674
+ ) || "0",
675
+ )
676
+ const originUsd = parseFloat(
677
+ quote.originAmountUsdDisplay?.replace(/[^0-9.-]/g, "") ||
678
+ "0",
679
+ )
680
+ const destUsd = parseFloat(
681
+ quote.destinationAmountUsdDisplay?.replace(
682
+ /[^0-9.-]/g,
683
+ "",
684
+ ) || "0",
685
+ )
686
+ const totalFeesUsd = parseFloat(
687
+ quote.fees?.totalFeeAmountUsdDisplay?.replace(
688
+ /[^0-9.-]/g,
689
+ "",
690
+ ) || "0",
691
+ )
692
+ const swapFeesUsd = Math.max(
693
+ 0,
694
+ originUsd - destUsd - totalFeesUsd,
695
+ )
696
+ const totalProviderUsd =
697
+ providerUsd + trailsUsd + swapFeesUsd
698
+
699
+ // Sum of all gas costs and all provider costs
700
+ const grandTotalUsd = totalGasUsd + totalProviderUsd
701
+
702
+ if (grandTotalUsd <= 0) return null
703
+
704
+ return (
705
+ <div className="flex justify-between items-center">
706
+ <span className="text-xs font-semibold text-gray-700 dark:text-gray-300 flex items-center gap-1">
707
+ Total fees:
708
+ <Tooltip message="The total fees for this transaction, including gas costs and all provider fees">
709
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
710
+ </Tooltip>
711
+ </span>
712
+ <span className="font-semibold text-xs text-gray-900 dark:text-white">
713
+ ≈ {formatUsdAmountDisplay(grandTotalUsd)}
714
+ </span>
715
+ </div>
716
+ )
717
+ })()}
718
+
719
+ {/* Spacer */}
720
+ <div className="h-2" />
721
+ </>
722
+ )
723
+ })()}
724
+
725
+ {/* Transaction Details Section */}
726
+ {(() => {
727
+ const hasSlippage =
728
+ quote?.slippageTolerance != null &&
729
+ Number(quote.slippageTolerance) !== 0
730
+ const hasPriceImpact =
731
+ quote?.priceImpact != null && Number(quote.priceImpact) !== 0
732
+ const showSection = hasSlippage || hasPriceImpact
733
+
734
+ if (!showSection) return null
735
+
736
+ return (
737
+ <>
738
+ <div className="text-xs font-semibold text-gray-700 dark:text-gray-300 mb-2 text-center">
739
+ Transaction details
740
+ </div>
741
+
742
+ {/* Max Slippage */}
743
+ {hasSlippage && (
744
+ <div className="flex justify-between items-center">
745
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
746
+ Max slippage:
747
+ <Tooltip message="The maximum percentage by which the exchange rate can change before the transaction fails. Higher slippage means more tolerance for price changes but potentially worse rates.">
748
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
749
+ </Tooltip>
750
+ </span>
751
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
752
+ {quote.slippageTolerance}%
753
+ </span>
754
+ </div>
755
+ )}
756
+
757
+ {/* Price Impact */}
758
+ {hasPriceImpact && (
759
+ <div className="space-y-1">
760
+ <div className="flex justify-between items-center">
761
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
762
+ Price impact:
763
+ <Tooltip message="The percentage change in the token price caused by your trade. Higher impact means your trade affects the market price more, potentially resulting in worse rates.">
764
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
765
+ </Tooltip>
766
+ </span>
767
+ <span
768
+ className={`font-medium text-xs flex items-center gap-1 ${
769
+ Math.abs(Number(quote.priceImpact)) > 5
770
+ ? "text-red-600 dark:text-red-400"
771
+ : Math.abs(Number(quote.priceImpact)) > 0.5
772
+ ? "text-orange-600 dark:text-orange-400"
773
+ : "text-gray-900 dark:text-white"
774
+ }`}
395
775
  >
396
- <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
397
- <TokenImage
398
- imageUrl={quote.originToken.imageUrl}
399
- symbol={quote.originToken.symbol}
400
- chainId={quote.originChain.id}
401
- contractAddress={
402
- quote.originToken.contractAddress
403
- }
404
- size={16}
405
- />
406
- {quote.originAmountDisplay}{" "}
407
- {quote.originToken.symbol}
408
- </div>
409
- </a>
410
- {quote.originAmountUsdDisplay && (
411
- <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
412
- ≈ {quote.originAmountUsdDisplay}
413
- </div>
414
- )}
415
- </>
776
+ {Math.abs(Number(quote.priceImpact)) > 0.5 && (
777
+ <span title="High price impact">
778
+ <svg
779
+ className="w-3 h-3"
780
+ fill="none"
781
+ stroke="currentColor"
782
+ viewBox="0 0 24 24"
783
+ aria-hidden="true"
784
+ >
785
+ <path
786
+ strokeLinecap="round"
787
+ strokeLinejoin="round"
788
+ strokeWidth={2}
789
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
790
+ />
791
+ </svg>
792
+ </span>
793
+ )}
794
+ {quote.priceImpact}%
795
+ </span>
796
+ </div>
797
+ {quote.priceImpactUsdDisplay && (
798
+ <div className="text-right">
799
+ <span className="text-xs text-gray-500 dark:text-gray-400">
800
+ ≈ {quote.priceImpactUsdDisplay}
801
+ </span>
802
+ </div>
803
+ )}
804
+ </div>
805
+ )}
806
+ </>
807
+ )
808
+ })()}
809
+
810
+ {/* More Info Section */}
811
+ <div className="space-y-2">
812
+ <button
813
+ type="button"
814
+ onClick={() => {
815
+ setShowMoreInfo(!showMoreInfo)
816
+ if (!showMoreInfo) {
817
+ setTimeout(() => {
818
+ if (moreInfoRef.current) {
819
+ moreInfoRef.current.scrollIntoView({
820
+ behavior: "smooth",
821
+ block: "nearest",
822
+ })
823
+ }
824
+ }, 150)
825
+ }
826
+ }}
827
+ className="flex items-center gap-1 text-xs hover:underline cursor-pointer transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
828
+ aria-label={showMoreInfo ? "Hide more info" : "Show more info"}
829
+ >
830
+ <span>More</span>
831
+ <svg
832
+ className={`w-3 h-3 transition-transform duration-300 ease-out ${
833
+ showMoreInfo ? "rotate-180" : ""
834
+ }`}
835
+ fill="none"
836
+ stroke="currentColor"
837
+ viewBox="0 0 24 24"
838
+ aria-hidden="true"
839
+ >
840
+ <path
841
+ strokeLinecap="round"
842
+ strokeLinejoin="round"
843
+ strokeWidth={2}
844
+ d="M19 9l-7 7-7-7"
845
+ />
846
+ </svg>
847
+ </button>
848
+
849
+ <div
850
+ ref={moreInfoRef}
851
+ className={`overflow-hidden transition-all duration-300 ease-out ${
852
+ showMoreInfo
853
+ ? "max-h-[500px] opacity-100"
854
+ : "max-h-0 opacity-0"
855
+ }`}
856
+ >
857
+ <div className="space-y-3 pt-2">
858
+ {/* Exchange Rate */}
859
+ {quote?.originTokenRate && quote?.destinationTokenRate && (
860
+ <div className="flex justify-between items-center">
861
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
862
+ Exchange Rate:
863
+ <Tooltip message="The current exchange rate between the origin and destination tokens">
864
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
865
+ </Tooltip>
866
+ </span>
867
+ <button
868
+ type="button"
869
+ onClick={() => setShowOriginRate(!showOriginRate)}
870
+ className="font-medium text-xs hover:underline cursor-pointer text-gray-900 hover:text-gray-700 dark:text-white dark:hover:text-gray-300"
871
+ >
872
+ {showOriginRate
873
+ ? quote.originTokenRate
874
+ : quote.destinationTokenRate}
875
+ </button>
876
+ </div>
877
+ )}
878
+
879
+ {/* Origin Transfer Amount */}
880
+ {quote?.originAmount &&
881
+ quote?.originToken.symbol &&
882
+ quote?.originChain.id && (
883
+ <div className="flex justify-between items-start">
884
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
885
+ Origin Transfer Amount:
886
+ <Tooltip message="The amount of tokens you will send from the origin chain">
887
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
888
+ </Tooltip>
889
+ </span>
890
+ <div className="text-right">
891
+ {quote.originToken.contractAddress ===
892
+ "0x0000000000000000000000000000000000000000" ? (
893
+ <>
894
+ <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
895
+ <TokenImage
896
+ imageUrl={quote.originToken.imageUrl}
897
+ symbol={quote.originToken.symbol}
898
+ chainId={quote.originChain.id}
899
+ contractAddress={
900
+ quote.originToken.contractAddress
901
+ }
902
+ size={16}
903
+ />
904
+ {quote.originAmountDisplay}{" "}
905
+ {quote.originToken.symbol}
906
+ </div>
907
+ {quote.originAmountUsdDisplay && (
908
+ <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
909
+ ≈ {quote.originAmountUsdDisplay}
910
+ </div>
911
+ )}
912
+ </>
913
+ ) : (
914
+ <>
915
+ <a
916
+ href={getExplorerUrlForAddress({
917
+ address: quote.originToken.contractAddress,
918
+ chainId: quote.originChain.id,
919
+ })}
920
+ target="_blank"
921
+ rel="noopener noreferrer"
922
+ className="hover:underline cursor-pointer"
923
+ >
924
+ <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
925
+ <TokenImage
926
+ imageUrl={quote.originToken.imageUrl}
927
+ symbol={quote.originToken.symbol}
928
+ chainId={quote.originChain.id}
929
+ contractAddress={
930
+ quote.originToken.contractAddress
931
+ }
932
+ size={16}
933
+ />
934
+ {quote.originAmountDisplay}{" "}
935
+ {quote.originToken.symbol}
936
+ </div>
937
+ </a>
938
+ {quote.originAmountUsdDisplay && (
939
+ <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
940
+ ≈ {quote.originAmountUsdDisplay}
941
+ </div>
942
+ )}
943
+ </>
944
+ )}
945
+ </div>
946
+ </div>
416
947
  )}
417
- </div>
418
- </div>
419
- )}
420
948
 
421
- {quote?.destinationToken.symbol &&
422
- quote?.destinationAmount &&
423
- quote?.destinationAmountUsdDisplay &&
424
- quote?.destinationChain.id && (
425
- <div className="flex justify-between items-start">
426
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
427
- Destination Target Amount:
428
- <Tooltip message="The amount of tokens you will receive on the destination chain after the swap and/or bridge">
429
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
430
- </Tooltip>
431
- </span>
432
- <div className="text-right">
433
- {quote.destinationToken.contractAddress ===
434
- "0x0000000000000000000000000000000000000000" ? (
435
- <>
436
- <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
437
- <TokenImage
438
- imageUrl={quote.destinationToken.imageUrl}
439
- symbol={quote.destinationToken.symbol}
440
- chainId={quote.destinationChain.id}
441
- contractAddress={
442
- quote.destinationToken.contractAddress
443
- }
444
- size={16}
445
- />
446
- {quote.destinationAmountDisplay}{" "}
447
- {quote.destinationToken.symbol}
949
+ {/* Destination Target Amount */}
950
+ {quote?.destinationToken.symbol &&
951
+ quote?.destinationAmount &&
952
+ quote?.destinationAmountUsdDisplay &&
953
+ quote?.destinationChain.id && (
954
+ <div className="flex justify-between items-start">
955
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
956
+ Destination Target Amount:
957
+ <Tooltip message="The amount of tokens you will receive on the destination chain after the swap and/or bridge">
958
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
959
+ </Tooltip>
960
+ </span>
961
+ <div className="text-right">
962
+ {quote.destinationToken.contractAddress ===
963
+ "0x0000000000000000000000000000000000000000" ? (
964
+ <>
965
+ <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
966
+ <TokenImage
967
+ imageUrl={quote.destinationToken.imageUrl}
968
+ symbol={quote.destinationToken.symbol}
969
+ chainId={quote.destinationChain.id}
970
+ contractAddress={
971
+ quote.destinationToken.contractAddress
972
+ }
973
+ size={16}
974
+ />
975
+ {quote.destinationAmountDisplay}{" "}
976
+ {quote.destinationToken.symbol}
977
+ </div>
978
+ {quote.destinationAmountUsdDisplay && (
979
+ <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
980
+ ≈ {quote.destinationAmountUsdDisplay}
981
+ </div>
982
+ )}
983
+ </>
984
+ ) : (
985
+ <>
986
+ <a
987
+ href={getExplorerUrlForAddress({
988
+ address:
989
+ quote.destinationToken.contractAddress,
990
+ chainId: quote.destinationChain.id,
991
+ })}
992
+ target="_blank"
993
+ rel="noopener noreferrer"
994
+ className="hover:underline cursor-pointer"
995
+ >
996
+ <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
997
+ <TokenImage
998
+ imageUrl={quote.destinationToken.imageUrl}
999
+ symbol={quote.destinationToken.symbol}
1000
+ chainId={quote.destinationChain.id}
1001
+ contractAddress={
1002
+ quote.destinationToken.contractAddress
1003
+ }
1004
+ size={16}
1005
+ />
1006
+ {quote.destinationAmountDisplay}{" "}
1007
+ {quote.destinationToken.symbol}
1008
+ </div>
1009
+ </a>
1010
+ {quote.destinationAmountUsdDisplay && (
1011
+ <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
1012
+ ≈ {quote.destinationAmountUsdDisplay}
1013
+ </div>
1014
+ )}
1015
+ </>
1016
+ )}
448
1017
  </div>
449
- {quote.destinationAmountUsdDisplay && (
450
- <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
451
- ≈ {quote.destinationAmountUsdDisplay}
452
- </div>
453
- )}
454
- </>
455
- ) : (
456
- <>
1018
+ </div>
1019
+ )}
1020
+
1021
+ {/* Origin Deposit Address */}
1022
+ {quote?.originDepositAddress && (
1023
+ <div className="flex justify-between items-center">
1024
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
1025
+ Origin Deposit Address:
1026
+ <Tooltip message="This is the intent address to deposit to that will then execute the swap and/or bridge">
1027
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
1028
+ </Tooltip>
1029
+ </span>
1030
+ <a
1031
+ href={getExplorerUrlForAddress({
1032
+ address: quote.originDepositAddress,
1033
+ chainId: quote.originChain.id,
1034
+ })}
1035
+ target="_blank"
1036
+ rel="noopener noreferrer"
1037
+ className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
1038
+ >
1039
+ {truncateAddress(quote.originDepositAddress)}
1040
+ <svg
1041
+ className="w-3 h-3"
1042
+ fill="none"
1043
+ stroke="currentColor"
1044
+ viewBox="0 0 24 24"
1045
+ aria-hidden="true"
1046
+ >
1047
+ <path
1048
+ strokeLinecap="round"
1049
+ strokeLinejoin="round"
1050
+ strokeWidth={2}
1051
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
1052
+ />
1053
+ </svg>
1054
+ </a>
1055
+ </div>
1056
+ )}
1057
+
1058
+ {/* Destination Deposit Address */}
1059
+ {quote?.destinationDepositAddress &&
1060
+ quote?.destinationDepositAddress !==
1061
+ quote?.originDepositAddress && (
1062
+ <div className="flex justify-between items-center">
1063
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
1064
+ Destination Deposit Address:
1065
+ <Tooltip message="This is the address that will receive the tokens after any swap and/or bridge from the origin chain">
1066
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
1067
+ </Tooltip>
1068
+ </span>
457
1069
  <a
458
1070
  href={getExplorerUrlForAddress({
459
- address: quote.destinationToken.contractAddress,
1071
+ address: quote.destinationDepositAddress,
460
1072
  chainId: quote.destinationChain.id,
461
1073
  })}
462
1074
  target="_blank"
463
1075
  rel="noopener noreferrer"
464
- className="hover:underline cursor-pointer"
1076
+ className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
465
1077
  >
466
- <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
467
- <TokenImage
468
- imageUrl={quote.destinationToken.imageUrl}
469
- symbol={quote.destinationToken.symbol}
470
- chainId={quote.destinationChain.id}
471
- contractAddress={
472
- quote.destinationToken.contractAddress
473
- }
474
- size={16}
1078
+ {truncateAddress(quote.destinationDepositAddress)}
1079
+ <svg
1080
+ className="w-3 h-3"
1081
+ fill="none"
1082
+ stroke="currentColor"
1083
+ viewBox="0 0 24 24"
1084
+ aria-hidden="true"
1085
+ >
1086
+ <path
1087
+ strokeLinecap="round"
1088
+ strokeLinejoin="round"
1089
+ strokeWidth={2}
1090
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
475
1091
  />
476
- {quote.destinationAmountDisplay}{" "}
477
- {quote.destinationToken.symbol}
478
- </div>
1092
+ </svg>
479
1093
  </a>
480
- {quote.destinationAmountUsdDisplay && (
481
- <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
482
- ≈ {quote.destinationAmountUsdDisplay}
483
- </div>
484
- )}
485
- </>
1094
+ </div>
486
1095
  )}
487
- </div>
488
- </div>
489
- )}
490
-
491
- {quote?.originDepositAddress && (
492
- <div className="flex justify-between items-center">
493
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
494
- Origin Deposit Address:
495
- <Tooltip message="This is the intent address to deposit to that will then execute the swap and/or bridge">
496
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
497
- </Tooltip>
498
- </span>
499
- <a
500
- href={getExplorerUrlForAddress({
501
- address: quote.originDepositAddress,
502
- chainId: quote.originChain.id,
503
- })}
504
- target="_blank"
505
- rel="noopener noreferrer"
506
- className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
507
- >
508
- {truncateAddress(quote.originDepositAddress)}
509
- <svg
510
- className="w-3 h-3"
511
- fill="none"
512
- stroke="currentColor"
513
- viewBox="0 0 24 24"
514
- aria-hidden="true"
515
- >
516
- <path
517
- strokeLinecap="round"
518
- strokeLinejoin="round"
519
- strokeWidth={2}
520
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
521
- />
522
- </svg>
523
- </a>
524
- </div>
525
- )}
526
-
527
- {quote?.destinationDepositAddress &&
528
- quote?.destinationDepositAddress !==
529
- quote?.originDepositAddress && (
530
- <div className="flex justify-between items-center">
531
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
532
- Destination Deposit Address:
533
- <Tooltip message="This is the address that will receive the tokens after any swap and/or bridge from the origin chain">
534
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
535
- </Tooltip>
536
- </span>
537
- <a
538
- href={getExplorerUrlForAddress({
539
- address: quote.destinationDepositAddress,
540
- chainId: quote.destinationChain.id,
541
- })}
542
- target="_blank"
543
- rel="noopener noreferrer"
544
- className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
545
- >
546
- {truncateAddress(quote.destinationDepositAddress)}
547
- <svg
548
- className="w-3 h-3"
549
- fill="none"
550
- stroke="currentColor"
551
- viewBox="0 0 24 24"
552
- aria-hidden="true"
553
- >
554
- <path
555
- strokeLinecap="round"
556
- strokeLinejoin="round"
557
- strokeWidth={2}
558
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
559
- />
560
- </svg>
561
- </a>
562
- </div>
563
- )}
564
-
565
- {quote?.destinationAddress && (
566
- <div className="flex justify-between items-center">
567
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
568
- Destination Target Address:
569
- <Tooltip message="This is the final execution address or recipient address">
570
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
571
- </Tooltip>
572
- </span>
573
- <a
574
- href={getExplorerUrlForAddress({
575
- address: quote.destinationAddress,
576
- chainId: quote.destinationChain.id,
577
- })}
578
- target="_blank"
579
- rel="noopener noreferrer"
580
- className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
581
- >
582
- {truncateAddress(quote.destinationAddress)}
583
- <svg
584
- className="w-3 h-3"
585
- fill="none"
586
- stroke="currentColor"
587
- viewBox="0 0 24 24"
588
- aria-hidden="true"
589
- >
590
- <path
591
- strokeLinecap="round"
592
- strokeLinejoin="round"
593
- strokeWidth={2}
594
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
595
- />
596
- </svg>
597
- </a>
598
- </div>
599
- )}
600
1096
 
601
- {quote?.slippageTolerance != null && (
602
- <div className="flex justify-between items-center">
603
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
604
- Max Slippage:
605
- <Tooltip message="The maximum percentage by which the exchange rate can change before the transaction fails. Higher slippage means more tolerance for price changes but potentially worse rates.">
606
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
607
- </Tooltip>
608
- </span>
609
- <span className="font-medium text-xs text-gray-900 dark:text-white">
610
- {quote.slippageTolerance}%
611
- </span>
612
- </div>
613
- )}
614
-
615
- {quote?.priceImpact != null &&
616
- (() => {
617
- const priceImpactNum = Math.abs(Number(quote.priceImpact))
618
- return (
619
- <div className="space-y-1">
1097
+ {/* Destination Target Address */}
1098
+ {quote?.destinationAddress && (
620
1099
  <div className="flex justify-between items-center">
621
1100
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
622
- Price Impact:
623
- <Tooltip message="The percentage change in the token price caused by your trade. Higher impact means your trade affects the market price more, potentially resulting in worse rates.">
1101
+ Destination Target Address:
1102
+ <Tooltip message="This is the final execution address or recipient address">
624
1103
  <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
625
1104
  </Tooltip>
626
1105
  </span>
627
- <span
628
- className={`font-medium text-xs flex items-center gap-1 ${
629
- priceImpactNum > 5
630
- ? "text-red-600 dark:text-red-400"
631
- : priceImpactNum > 0.5
632
- ? "text-orange-600 dark:text-orange-400"
633
- : "text-gray-900 dark:text-white"
634
- }`}
1106
+ <a
1107
+ href={getExplorerUrlForAddress({
1108
+ address: quote.destinationAddress,
1109
+ chainId: quote.destinationChain.id,
1110
+ })}
1111
+ target="_blank"
1112
+ rel="noopener noreferrer"
1113
+ className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
635
1114
  >
636
- {priceImpactNum > 0.5 && (
637
- <span title="High price impact">
638
- <svg
639
- className="w-3 h-3"
640
- fill="none"
641
- stroke="currentColor"
642
- viewBox="0 0 24 24"
643
- aria-hidden="true"
644
- >
645
- <path
646
- strokeLinecap="round"
647
- strokeLinejoin="round"
648
- strokeWidth={2}
649
- d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
650
- />
651
- </svg>
652
- </span>
653
- )}
654
- {quote.priceImpact}%
655
- </span>
1115
+ {truncateAddress(quote.destinationAddress)}
1116
+ <svg
1117
+ className="w-3 h-3"
1118
+ fill="none"
1119
+ stroke="currentColor"
1120
+ viewBox="0 0 24 24"
1121
+ aria-hidden="true"
1122
+ >
1123
+ <path
1124
+ strokeLinecap="round"
1125
+ strokeLinejoin="round"
1126
+ strokeWidth={2}
1127
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
1128
+ />
1129
+ </svg>
1130
+ </a>
656
1131
  </div>
657
- {quote.priceImpactUsdDisplay && (
658
- <div className="text-right">
659
- <span className="text-xs text-gray-500 dark:text-gray-400">
660
- {quote.priceImpactUsdDisplay}
1132
+ )}
1133
+
1134
+ {/* Custom Calldata */}
1135
+ {quote?.destinationCalldata && (
1136
+ <div className="space-y-2">
1137
+ <button
1138
+ type="button"
1139
+ onClick={() => {
1140
+ setShowCalldata(!showCalldata)
1141
+ // Auto-scroll to calldata when expanding
1142
+ if (!showCalldata) {
1143
+ setTimeout(() => {
1144
+ if (calldataRef.current) {
1145
+ calldataRef.current.scrollIntoView({
1146
+ behavior: "smooth",
1147
+ block: "nearest",
1148
+ })
1149
+ }
1150
+ }, 150) // Delay to let the expansion animation start
1151
+ }
1152
+ }}
1153
+ className="flex items-center gap-1 text-xs hover:underline cursor-pointer transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
1154
+ aria-label={
1155
+ showCalldata
1156
+ ? "Hide custom calldata"
1157
+ : "Show custom calldata"
1158
+ }
1159
+ >
1160
+ <span className="text-[10px]">
1161
+ Includes custom destination calldata
661
1162
  </span>
1163
+ <svg
1164
+ className={`w-3 h-3 transition-transform duration-300 ease-out ${
1165
+ showCalldata ? "rotate-180" : ""
1166
+ }`}
1167
+ fill="none"
1168
+ stroke="currentColor"
1169
+ viewBox="0 0 24 24"
1170
+ aria-hidden="true"
1171
+ >
1172
+ <path
1173
+ strokeLinecap="round"
1174
+ strokeLinejoin="round"
1175
+ strokeWidth={2}
1176
+ d="M19 9l-7 7-7-7"
1177
+ />
1178
+ </svg>
1179
+ </button>
1180
+ <div
1181
+ ref={calldataRef}
1182
+ className={`overflow-hidden transition-all duration-300 ease-out ${
1183
+ showCalldata
1184
+ ? "max-h-32 opacity-100"
1185
+ : "max-h-0 opacity-0"
1186
+ }`}
1187
+ >
1188
+ <div className="mt-2">
1189
+ <textarea
1190
+ value={quote.destinationCalldata}
1191
+ readOnly
1192
+ className={`w-full p-2 text-xs font-mono rounded border border-solid resize-none transition-all duration-200 bg-gray-100 dark:bg-gray-700 border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-200`}
1193
+ rows={4}
1194
+ placeholder="No custom calldata"
1195
+ />
1196
+ </div>
662
1197
  </div>
663
- )}
664
- </div>
665
- )
666
- })()}
667
-
668
- {quote?.fees?.totalFeeAmountUsd != null && (
669
- <div className="flex justify-between items-center">
670
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
671
- Total Fees:
672
- <Tooltip message="The total fees charged for this transaction, including gas fees, bridge fees, and any platform fees. These fees are deducted from your transaction.">
673
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
674
- </Tooltip>
675
- </span>
676
- <span className="font-medium text-xs text-gray-900 dark:text-white">
677
- {quote.fees.totalFeeAmountUsdDisplay}
678
- </span>
679
- </div>
680
- )}
681
-
682
- {quote?.quoteProvider?.name && (
683
- <div className="flex justify-between items-center">
684
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
685
- {quote.originChain.id === quote.destinationChain.id
686
- ? "Swap Provider"
687
- : "Bridge"}
688
- :
689
- <Tooltip message="The service that fullfils the swap and/or bridge">
690
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
691
- </Tooltip>
692
- </span>
693
- <a
694
- href={quote.quoteProvider?.url}
695
- target="_blank"
696
- rel="noopener noreferrer"
697
- className="font-medium text-xs text-gray-900 dark:text-white hover:underline flex items-center gap-1"
698
- >
699
- {quote.quoteProvider?.name}
700
- <svg
701
- className="w-3 h-3"
702
- fill="none"
703
- stroke="currentColor"
704
- viewBox="0 0 24 24"
705
- aria-hidden="true"
706
- >
707
- <path
708
- strokeLinecap="round"
709
- strokeLinejoin="round"
710
- strokeWidth={2}
711
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
712
- />
713
- </svg>
714
- </a>
715
- </div>
716
- )}
1198
+ </div>
1199
+ )}
717
1200
 
718
- {quote?.destinationCalldata && (
719
- <div className="space-y-2">
720
- <button
721
- type="button"
722
- onClick={() => {
723
- setShowCalldata(!showCalldata)
724
- // Auto-scroll to calldata when expanding
725
- if (!showCalldata) {
726
- setTimeout(() => {
727
- if (calldataRef.current) {
728
- calldataRef.current.scrollIntoView({
729
- behavior: "smooth",
730
- block: "nearest",
731
- })
732
- }
733
- }, 150) // Delay to let the expansion animation start
734
- }
735
- }}
736
- className="flex items-center gap-1 text-xs hover:underline cursor-pointer transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
737
- aria-label={
738
- showCalldata
739
- ? "Hide custom calldata"
740
- : "Show custom calldata"
741
- }
742
- >
743
- <span className="text-[10px]">
744
- Includes custom destination calldata
745
- </span>
746
- <svg
747
- className={`w-3 h-3 transition-transform duration-300 ease-out ${
748
- showCalldata ? "rotate-180" : ""
749
- }`}
750
- fill="none"
751
- stroke="currentColor"
752
- viewBox="0 0 24 24"
753
- aria-hidden="true"
754
- >
755
- <path
756
- strokeLinecap="round"
757
- strokeLinejoin="round"
758
- strokeWidth={2}
759
- d="M19 9l-7 7-7-7"
760
- />
761
- </svg>
762
- </button>
763
- <div
764
- ref={calldataRef}
765
- className={`overflow-hidden transition-all duration-300 ease-out ${
766
- showCalldata ? "max-h-32 opacity-100" : "max-h-0 opacity-0"
767
- }`}
768
- >
769
- <div className="mt-2">
770
- <textarea
771
- value={quote.destinationCalldata}
772
- readOnly
773
- className={`w-full p-2 text-xs font-mono rounded border border-solid resize-none transition-all duration-200 bg-gray-100 dark:bg-gray-700 border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-200`}
774
- rows={4}
775
- placeholder="No custom calldata"
776
- />
777
- </div>
1201
+ {/* Children content */}
1202
+ {children && <div className="mb-0">{children}</div>}
778
1203
  </div>
779
1204
  </div>
780
- )}
781
-
782
- {/* Children content */}
783
- {children && <div className="mb-0">{children}</div>}
1205
+ </div>
784
1206
  </div>
785
1207
  </div>
786
1208
  </div>