0xtrails 0.6.0 → 0.6.2

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 (88) hide show
  1. package/dist/{ccip-Dw5AN7oU.js → ccip-CZfykYU7.js} +4 -4
  2. package/dist/chains.d.ts +9 -2
  3. package/dist/chains.d.ts.map +1 -1
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/contractUtils.d.ts +2 -1
  7. package/dist/contractUtils.d.ts.map +1 -1
  8. package/dist/{index-BtVUTbEZ.js → index-S9pphnT9.js} +29732 -36767
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +300 -242
  12. package/dist/prepareSend.d.ts.map +1 -1
  13. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  14. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  15. package/dist/transactionIntent/types.d.ts +3 -1
  16. package/dist/transactionIntent/types.d.ts.map +1 -1
  17. package/dist/widget/components/ChainImage.d.ts.map +1 -1
  18. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  19. package/dist/widget/components/DepositTracker.d.ts +12 -0
  20. package/dist/widget/components/DepositTracker.d.ts.map +1 -0
  21. package/dist/widget/components/Disconnect.d.ts.map +1 -1
  22. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -1
  23. package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
  24. package/dist/widget/components/QRCodeOptions.d.ts +9 -0
  25. package/dist/widget/components/QRCodeOptions.d.ts.map +1 -0
  26. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  27. package/dist/widget/components/Receipt.d.ts.map +1 -1
  28. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  29. package/dist/widget/components/Toast.d.ts.map +1 -1
  30. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  31. package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -1
  32. package/dist/widget/css/compiled.css +1 -1
  33. package/dist/widget/css/index.css +103 -38
  34. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  35. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  36. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  37. package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
  38. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  39. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  40. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  41. package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -1
  42. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  43. package/dist/widget/index.js +1 -1
  44. package/dist/widget/widget.d.ts.map +1 -1
  45. package/package.json +6 -9
  46. package/src/chains.ts +37 -4
  47. package/src/constants.ts +1 -0
  48. package/src/contractUtils.ts +8 -7
  49. package/src/estimate.ts +2 -2
  50. package/src/index.ts +2 -0
  51. package/src/intents.ts +2 -2
  52. package/src/paymasterSend.ts +2 -2
  53. package/src/prepareSend.ts +34 -3
  54. package/src/sendUserOp.ts +2 -2
  55. package/src/tokens.ts +2 -2
  56. package/src/transactionIntent/deposits/gaslessDeposit.ts +2 -2
  57. package/src/transactionIntent/handlers/crossChain.ts +51 -2
  58. package/src/transactionIntent/handlers/sameChainSameToken.ts +52 -2
  59. package/src/transactionIntent/quote/normalizeQuote.ts +2 -2
  60. package/src/transactionIntent/types.ts +9 -1
  61. package/src/widget/compiled.css +1 -1
  62. package/src/widget/components/ChainImage.tsx +10 -7
  63. package/src/widget/components/ChainList.tsx +1 -1
  64. package/src/widget/components/ClassicSwap.tsx +8 -4
  65. package/src/widget/components/ConnectedWallets.tsx +1 -1
  66. package/src/widget/components/DepositTracker.tsx +298 -0
  67. package/src/widget/components/Disconnect.tsx +24 -3
  68. package/src/widget/components/FeeBreakdown.tsx +3 -3
  69. package/src/widget/components/QRCodeDeposit.tsx +29 -19
  70. package/src/widget/components/QRCodeOptions.tsx +65 -0
  71. package/src/widget/components/QuoteDetails.tsx +694 -803
  72. package/src/widget/components/Receipt.tsx +76 -40
  73. package/src/widget/components/ThemeProvider.tsx +7 -12
  74. package/src/widget/components/Toast.tsx +3 -2
  75. package/src/widget/components/TokenSelector.tsx +1 -1
  76. package/src/widget/components/Tooltip.tsx +1 -1
  77. package/src/widget/components/TransferPendingVertical.tsx +11 -2
  78. package/src/widget/components/WalletConnectionPending.tsx +28 -5
  79. package/src/widget/hooks/useCheckout.ts +10 -2
  80. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  81. package/src/widget/hooks/useDebugScreens.ts +1 -0
  82. package/src/widget/hooks/useIntentTransactionHistory.ts +114 -143
  83. package/src/widget/hooks/useQuote.ts +92 -6
  84. package/src/widget/hooks/useSelectedFeeOption.tsx +86 -29
  85. package/src/widget/hooks/useSendForm.ts +43 -7
  86. package/src/widget/index.css +103 -38
  87. package/src/widget/widget.tsx +48 -5
  88. package/dist/0xtrails.css +0 -1
@@ -0,0 +1,298 @@
1
+ import type React from "react"
2
+ import { useMemo, useEffect } from "react"
3
+ import { CheckCircle, Clock } from "lucide-react"
4
+ import { formatUnits, zeroAddress } from "viem"
5
+ import { TokenImage } from "./TokenImage.js"
6
+ import {
7
+ formatAmountDisplay,
8
+ useTokenBalances,
9
+ isNativeToken,
10
+ type TokenBalanceWithPrice,
11
+ } from "../../tokenBalances.js"
12
+ import { useQueryClient } from "@tanstack/react-query"
13
+
14
+ export interface DepositTrackerProps {
15
+ depositAddress: string
16
+ requiredAmount: string
17
+ tokenAddress: string
18
+ tokenSymbol: string
19
+ tokenDecimals: number
20
+ chainId: number
21
+ }
22
+
23
+ export const DepositTracker: React.FC<DepositTrackerProps> = ({
24
+ depositAddress,
25
+ requiredAmount,
26
+ tokenAddress,
27
+ tokenSymbol,
28
+ tokenDecimals,
29
+ chainId,
30
+ }) => {
31
+ const queryClient = useQueryClient()
32
+
33
+ // Fetch all token balances on the deposit address
34
+ const { sortedTokens } = useTokenBalances(depositAddress as `0x${string}`)
35
+
36
+ // Force refresh every 3 seconds to match backend polling
37
+ useEffect(() => {
38
+ const interval = setInterval(() => {
39
+ queryClient.invalidateQueries({
40
+ queryKey: ["getTokenBalancesWithPrices", "summary", depositAddress],
41
+ })
42
+ }, 3000)
43
+
44
+ return () => clearInterval(interval)
45
+ }, [depositAddress, queryClient])
46
+
47
+ // Find the required token deposit
48
+ const requiredTokenDeposit = useMemo(() => {
49
+ if (!sortedTokens || sortedTokens.length === 0) return null
50
+
51
+ const normalizedTokenAddress = tokenAddress.toLowerCase()
52
+ const isNativeDeposit = normalizedTokenAddress === zeroAddress.toLowerCase()
53
+
54
+ return sortedTokens.find((token) => {
55
+ const tokenIsNative = isNativeToken(token)
56
+ const tokenChainId = tokenIsNative
57
+ ? token.chainId
58
+ : token.contractInfo?.chainId
59
+
60
+ if (tokenChainId !== chainId) return false
61
+
62
+ if (isNativeDeposit) {
63
+ return tokenIsNative
64
+ } else {
65
+ return (
66
+ !tokenIsNative &&
67
+ token.contractAddress?.toLowerCase() === normalizedTokenAddress
68
+ )
69
+ }
70
+ })
71
+ }, [sortedTokens, tokenAddress, chainId])
72
+
73
+ // Get all deposits (including other tokens)
74
+ const allDeposits = useMemo(() => {
75
+ if (!sortedTokens || sortedTokens.length === 0) return []
76
+
77
+ return sortedTokens.filter((token) => {
78
+ const tokenIsNative = isNativeToken(token)
79
+ const tokenChainId = tokenIsNative
80
+ ? token.chainId
81
+ : token.contractInfo?.chainId
82
+
83
+ // Only show tokens from the same chain with balance > 0
84
+ if (tokenChainId !== chainId) return false
85
+
86
+ try {
87
+ return BigInt(token.balance) > 0n
88
+ } catch {
89
+ return false
90
+ }
91
+ })
92
+ }, [sortedTokens, chainId])
93
+
94
+ // Determine if we should show the "Detected Deposits" section
95
+ // Only show if there's more than 1 deposit OR if there's a wrong token deposited
96
+ const shouldShowDetectedDeposits = useMemo(() => {
97
+ if (allDeposits.length === 0) return false
98
+ if (allDeposits.length > 1) return true
99
+
100
+ // If only 1 deposit, check if it's the required token
101
+ const singleDeposit = allDeposits[0]
102
+ if (!singleDeposit) return false
103
+
104
+ const normalizedTokenAddress = tokenAddress.toLowerCase()
105
+ const isNativeDeposit = normalizedTokenAddress === zeroAddress.toLowerCase()
106
+ const depositIsNative = isNativeToken(singleDeposit)
107
+
108
+ // Get contract address for ERC20 tokens
109
+ const depositTokenAddress = depositIsNative
110
+ ? undefined
111
+ : (singleDeposit as TokenBalanceWithPrice).contractAddress?.toLowerCase()
112
+
113
+ // If both are native, don't show (it's the correct token)
114
+ if (isNativeDeposit && depositIsNative) return false
115
+
116
+ // If one is native and other isn't, show (wrong token)
117
+ if (isNativeDeposit !== depositIsNative) return true
118
+
119
+ // If both are ERC20, check if addresses match
120
+ if (!isNativeDeposit && depositTokenAddress === normalizedTokenAddress) {
121
+ return false // Same token, don't show
122
+ }
123
+
124
+ // Different token, show
125
+ return true
126
+ }, [allDeposits, tokenAddress])
127
+
128
+ // Calculate deposit progress
129
+ const depositStats = useMemo(() => {
130
+ const requiredBigInt = BigInt(requiredAmount)
131
+ const currentBalance = requiredTokenDeposit?.balance
132
+ ? BigInt(requiredTokenDeposit.balance)
133
+ : 0n
134
+
135
+ const percentage =
136
+ requiredBigInt > 0n
137
+ ? Number((currentBalance * 10000n) / requiredBigInt) / 100
138
+ : 0
139
+
140
+ const isComplete = currentBalance >= requiredBigInt
141
+ const isPartial = currentBalance > 0n && currentBalance < requiredBigInt
142
+ const isOverpaid = currentBalance > requiredBigInt
143
+
144
+ const currentFormatted = formatUnits(currentBalance, tokenDecimals)
145
+ const requiredFormatted = formatUnits(requiredBigInt, tokenDecimals)
146
+
147
+ return {
148
+ currentBalance,
149
+ percentage: Math.min(percentage, 100),
150
+ isComplete,
151
+ isPartial,
152
+ isOverpaid,
153
+ currentFormatted,
154
+ requiredFormatted,
155
+ }
156
+ }, [requiredAmount, requiredTokenDeposit, tokenDecimals])
157
+
158
+ // Determine status icon and color
159
+ const getStatusDisplay = () => {
160
+ if (depositStats.isComplete) {
161
+ return {
162
+ icon: <CheckCircle className="w-4 h-4 text-green-500" />,
163
+ text: "Deposit Complete",
164
+ progressColor: "bg-green-500",
165
+ }
166
+ }
167
+ if (depositStats.isPartial) {
168
+ return {
169
+ icon: <Clock className="w-4 h-4 text-yellow-500" />,
170
+ text: "Deposit Detected",
171
+ progressColor: "bg-yellow-500",
172
+ }
173
+ }
174
+ return {
175
+ icon: <Clock className="w-4 h-4 text-gray-400" />,
176
+ text: "Waiting for deposit...",
177
+ progressColor: "bg-gray-300",
178
+ }
179
+ }
180
+
181
+ const statusDisplay = getStatusDisplay()
182
+
183
+ return (
184
+ <div className="space-y-3">
185
+ {/* Status Header */}
186
+ <div className="flex items-center gap-2">
187
+ {statusDisplay.icon}
188
+ <p className="text-xs font-medium text-gray-600 dark:text-gray-400">
189
+ {statusDisplay.text}
190
+ </p>
191
+ </div>
192
+
193
+ {/* Progress Bar */}
194
+ <div className="space-y-2">
195
+ <div className="flex justify-between items-center text-xs">
196
+ <span className="text-gray-600 dark:text-gray-400">
197
+ Deposit Progress
198
+ </span>
199
+ <span className="font-semibold text-gray-900 dark:text-white">
200
+ {depositStats.percentage.toFixed(1)}%
201
+ </span>
202
+ </div>
203
+ <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5 overflow-hidden">
204
+ <div
205
+ className={`${statusDisplay.progressColor} h-full transition-all duration-500 ease-out`}
206
+ style={{ width: `${Math.min(depositStats.percentage, 100)}%` }}
207
+ />
208
+ </div>
209
+ <div className="flex justify-between items-center text-xs">
210
+ <div className="flex items-center gap-1">
211
+ <TokenImage
212
+ imageUrl={requiredTokenDeposit?.imageUrl}
213
+ symbol={tokenSymbol}
214
+ chainId={chainId}
215
+ contractAddress={tokenAddress}
216
+ size={16}
217
+ />
218
+ <span className="text-gray-600 dark:text-gray-400">
219
+ {formatAmountDisplay(depositStats.currentFormatted, {
220
+ maxFractionDigits: 6,
221
+ minFractionDigits: 2,
222
+ })}{" "}
223
+ /{" "}
224
+ {formatAmountDisplay(depositStats.requiredFormatted, {
225
+ maxFractionDigits: 6,
226
+ minFractionDigits: 2,
227
+ })}{" "}
228
+ {tokenSymbol}
229
+ </span>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ {/* Current Deposits List */}
235
+ {shouldShowDetectedDeposits && (
236
+ <div className="space-y-1.5">
237
+ <p className="text-xs text-gray-600 dark:text-gray-400">
238
+ Detected Deposits:
239
+ </p>
240
+ <div className="trails-border-radius-container border trails-border-primary">
241
+ {allDeposits.map((token, index) => {
242
+ const tokenIsNative = isNativeToken(token)
243
+ const symbol = tokenIsNative
244
+ ? token.symbol || "ETH"
245
+ : token.contractInfo?.symbol
246
+ const decimals = tokenIsNative
247
+ ? token.decimals || 18
248
+ : token.contractInfo?.decimals || 18
249
+ const balance = formatUnits(BigInt(token.balance), decimals)
250
+ const tokenChainId = tokenIsNative
251
+ ? token.chainId
252
+ : token.contractInfo?.chainId
253
+
254
+ const tokenKey = tokenIsNative
255
+ ? `native-${tokenChainId}`
256
+ : `${token.contractAddress}-${tokenChainId}`
257
+
258
+ return (
259
+ <div key={tokenKey}>
260
+ <div className="px-2 py-1.5 flex items-center gap-2">
261
+ <TokenImage
262
+ imageUrl={token.imageUrl}
263
+ symbol={symbol}
264
+ chainId={tokenChainId || chainId}
265
+ contractAddress={
266
+ tokenIsNative ? undefined : token.contractAddress
267
+ }
268
+ size={16}
269
+ />
270
+ <div className="flex flex-col flex-1 min-w-0">
271
+ <span className="text-xs text-gray-900 dark:text-white truncate">
272
+ {formatAmountDisplay(balance, {
273
+ maxFractionDigits: 6,
274
+ minFractionDigits: 2,
275
+ })}{" "}
276
+ {symbol}
277
+ </span>
278
+ {token.balanceUsdFormatted && (
279
+ <span className="text-xs text-gray-500 dark:text-gray-400">
280
+ {token.balanceUsdFormatted}
281
+ </span>
282
+ )}
283
+ </div>
284
+ </div>
285
+ {index < allDeposits.length - 1 && (
286
+ <div className="border-t trails-border-primary" />
287
+ )}
288
+ </div>
289
+ )
290
+ })}
291
+ </div>
292
+ </div>
293
+ )}
294
+ </div>
295
+ )
296
+ }
297
+
298
+ export default DepositTracker
@@ -1,4 +1,4 @@
1
- import { useEffect } from "react"
1
+ import { useEffect, useRef } from "react"
2
2
  import type React from "react"
3
3
  import { ScreenHeader } from "./ScreenHeader.js"
4
4
  import { logger } from "../../logger.js"
@@ -12,18 +12,39 @@ export const Disconnect: React.FC<DisconnectProps> = ({
12
12
  onBack,
13
13
  disconnectHandler,
14
14
  }) => {
15
+ const hasDisconnectedRef = useRef(false)
16
+ const disconnectHandlerRef = useRef(disconnectHandler)
17
+
18
+ // Keep the ref updated with the latest handler
19
+ useEffect(() => {
20
+ disconnectHandlerRef.current = disconnectHandler
21
+ }, [disconnectHandler])
22
+
15
23
  useEffect(() => {
24
+ // Only run once when component mounts
25
+ if (hasDisconnectedRef.current) {
26
+ return
27
+ }
28
+
16
29
  const handleDisconnect = async () => {
30
+ hasDisconnectedRef.current = true
17
31
  try {
18
32
  logger.console.log("[trails-sdk] Disconnecting user...")
19
- await disconnectHandler()
33
+ // Ensure minimum 1 second delay to show disconnecting state
34
+ await Promise.all([
35
+ disconnectHandlerRef.current(),
36
+ new Promise((resolve) => setTimeout(resolve, 1000)),
37
+ ])
20
38
  } catch (error) {
39
+ // Still wait for minimum delay even if disconnect fails
40
+ await new Promise((resolve) => setTimeout(resolve, 1000))
21
41
  logger.console.error("[trails-sdk] Failed to disconnect:", error)
22
42
  }
23
43
  }
24
44
 
25
45
  handleDisconnect()
26
- }, [disconnectHandler])
46
+ // eslint-disable-next-line react-hooks/exhaustive-deps
47
+ }, []) // Empty dependency array - only run once on mount
27
48
 
28
49
  return (
29
50
  <div className="space-y-6">
@@ -1,9 +1,9 @@
1
1
  import type React from "react"
2
2
  import { useState } from "react"
3
- import { InfoIcon } from "@0xsequence/design-system"
4
3
  import { Tooltip } from "./Tooltip.js"
5
4
  import { TokenImage } from "./TokenImage.js"
6
5
  import type { TrailsFeeBreakdown, FeeItem } from "../../fees.js"
6
+ import { Info } from "lucide-react"
7
7
 
8
8
  interface FeeBreakdownProps {
9
9
  feeBreakdown: TrailsFeeBreakdown
@@ -22,7 +22,7 @@ const FeeRow: React.FC<FeeRowProps> = ({ label, feeItem, tooltip }) => (
22
22
  {label}
23
23
  {tooltip && (
24
24
  <Tooltip message={tooltip}>
25
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
25
+ <Info className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
26
26
  </Tooltip>
27
27
  )}
28
28
  </span>
@@ -75,7 +75,7 @@ export const FeeBreakdown: React.FC<FeeBreakdownProps> = ({
75
75
  children
76
76
  ) : (
77
77
  <Tooltip message="Detailed breakdown of the Trails platform fees">
78
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
78
+ <Info className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
79
79
  </Tooltip>
80
80
  )}
81
81
  </span>
@@ -10,6 +10,7 @@ import { formatUnits, getAddress, zeroAddress } from "viem"
10
10
  import { getExplorerUrlForAddress } from "../../explorer.js"
11
11
  import { ScreenHeader } from "./ScreenHeader.js"
12
12
  import { useQRCodeWallets, type QRCodeWalletOption } from "../../wallets.js"
13
+ import { DepositTracker } from "./DepositTracker.js"
13
14
 
14
15
  interface QRCodeDepositProps {
15
16
  onBack?: () => void
@@ -102,18 +103,19 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
102
103
  </div>
103
104
 
104
105
  {quote?.originAmount && (
105
- <div className="flex flex-col items-center justify-center gap-1 pt-2">
106
- <div className="flex items-center gap-2">
107
- <span className="text-sm text-gray-600 dark:text-gray-300">
108
- deposit exactly
109
- </span>
106
+ <div className="flex flex-col items-center justify-center gap-2 pt-2">
107
+ <div className="text-xs text-gray-500 dark:text-gray-400">
108
+ Deposit exactly
109
+ </div>
110
+ <div className="flex items-center gap-2 px-3 py-2 bg-white dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600">
110
111
  <TokenImage
111
112
  imageUrl={quote.originToken.imageUrl}
112
113
  symbol={quote.originToken.symbol}
113
114
  chainId={quote.originChain.id}
114
- size={20}
115
+ contractAddress={quote.originToken.contractAddress}
116
+ size={24}
115
117
  />
116
- <span className="text-sm font-bold text-gray-900 dark:text-white">
118
+ <span className="text-base font-bold text-gray-900 dark:text-white">
117
119
  {formatUnits(
118
120
  BigInt(quote.originAmount),
119
121
  quote.originToken.decimals,
@@ -121,17 +123,11 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
121
123
  {quote.originToken.symbol}
122
124
  </span>
123
125
  </div>
124
- <div className="flex items-center gap-1">
125
- <span className="text-xs text-gray-500 dark:text-gray-400">
126
- on
127
- </span>
128
- <ChainImage chainId={quote.originChain.id} size={16} />
129
- <span className="text-xs font-bold text-gray-500 dark:text-gray-400">
130
- {quote.originChain.name}
131
- </span>
132
- <span className="text-xs text-gray-500 dark:text-gray-400">
133
- to
134
- </span>
126
+ <div className="flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400">
127
+ <span>on</span>
128
+ <ChainImage chainId={quote.originChain.id} size={14} />
129
+ <span className="font-medium">{quote.originChain.name}</span>
130
+ <span>to</span>
135
131
  <a
136
132
  href={getExplorerUrlForAddress({
137
133
  address: quote.originDepositAddress,
@@ -139,7 +135,7 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
139
135
  })}
140
136
  target="_blank"
141
137
  rel="noopener noreferrer"
142
- className="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400 hover:underline transition-all"
138
+ className="flex items-center gap-1 font-medium hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer"
143
139
  >
144
140
  <span>
145
141
  {quote.originDepositAddress.slice(0, 6)}...
@@ -152,6 +148,20 @@ export const QRCodeDeposit: React.FC<QRCodeDepositProps> = ({
152
148
  )}
153
149
  </div>
154
150
 
151
+ {/* Deposit Tracker */}
152
+ {quote && (
153
+ <div className="space-y-2">
154
+ <DepositTracker
155
+ depositAddress={quote.originDepositAddress}
156
+ requiredAmount={quote.originAmount}
157
+ tokenAddress={quote.originToken.contractAddress}
158
+ tokenSymbol={quote.originToken.symbol}
159
+ tokenDecimals={quote.originToken.decimals}
160
+ chainId={quote.originChain.id}
161
+ />
162
+ </div>
163
+ )}
164
+
155
165
  {/* Quote Details */}
156
166
  {quote && (
157
167
  <div className="space-y-2">
@@ -0,0 +1,65 @@
1
+ import type React from "react"
2
+ import { QrCode, Wallet, ChevronRight } from "lucide-react"
3
+ import { ScreenHeader } from "./ScreenHeader.js"
4
+
5
+ export interface QRCodeOptionsProps {
6
+ onBack?: () => void
7
+ onSelectWalletConnect: () => void
8
+ onSelectQRCode: () => void
9
+ }
10
+
11
+ const QRCodeOptions: React.FC<QRCodeOptionsProps> = ({
12
+ onBack,
13
+ onSelectWalletConnect,
14
+ onSelectQRCode,
15
+ }) => {
16
+ return (
17
+ <div className="flex flex-col h-full">
18
+ <ScreenHeader
19
+ onBack={onBack}
20
+ headerContent="QR Code Options"
21
+ headerContentAlign="left"
22
+ rightSideContent={<div className="w-9" />}
23
+ />
24
+
25
+ <div className="flex-1">
26
+ <div className="space-y-4">
27
+ {/* QR Code Options */}
28
+ <div className="trails-border-radius-container border trails-border-primary">
29
+ <button
30
+ type="button"
31
+ onClick={onSelectWalletConnect}
32
+ className="w-full text-left px-3 py-4 text-sm flex items-center justify-between cursor-pointer transition-colors trails-text-primary trails-hover-bg"
33
+ >
34
+ <div className="flex items-center gap-3">
35
+ <Wallet className="w-4 h-4" />
36
+ <span className="text-sm font-bold">Via WalletConnect</span>
37
+ <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
38
+ Recommended
39
+ </span>
40
+ </div>
41
+ <ChevronRight className="w-5 h-5 text-gray-400" />
42
+ </button>
43
+
44
+ {/* Divider between buttons */}
45
+ <div className="border-b border-gray-200 dark:border-gray-700"></div>
46
+
47
+ <button
48
+ type="button"
49
+ onClick={onSelectQRCode}
50
+ className="w-full text-left px-3 py-4 text-sm flex items-center justify-between cursor-pointer transition-colors trails-text-primary trails-hover-bg"
51
+ >
52
+ <div className="flex items-center gap-3">
53
+ <QrCode className="w-4 h-4" />
54
+ <span className="text-sm font-bold">Via Deposit QR Code</span>
55
+ </div>
56
+ <ChevronRight className="w-5 h-5 text-gray-400" />
57
+ </button>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ )
63
+ }
64
+
65
+ export default QRCodeOptions