0xtrails 0.2.0 → 0.2.1

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 (113) hide show
  1. package/dist/analytics.d.ts +1 -0
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-D6ToCrWc.js → ccip-BbfANth7.js} +1 -1
  4. package/dist/chains.d.ts.map +1 -1
  5. package/dist/config.d.ts +1 -2
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/constants.d.ts +2 -2
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/gasless.d.ts +19 -7
  10. package/dist/gasless.d.ts.map +1 -1
  11. package/dist/{index-BqgeTLL8.js → index-WpIVoh3X.js} +27626 -26572
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +68 -68
  15. package/dist/indexerClient.d.ts +10 -0
  16. package/dist/indexerClient.d.ts.map +1 -1
  17. package/dist/intentEntrypoint.d.ts +40 -14
  18. package/dist/intentEntrypoint.d.ts.map +1 -1
  19. package/dist/intents.d.ts.map +1 -1
  20. package/dist/prepareSend.d.ts +11 -8
  21. package/dist/prepareSend.d.ts.map +1 -1
  22. package/dist/relayer.d.ts.map +1 -1
  23. package/dist/trails.d.ts.map +1 -1
  24. package/dist/trailsClient.d.ts.map +1 -1
  25. package/dist/trailsRouter.d.ts +22 -0
  26. package/dist/trailsRouter.d.ts.map +1 -0
  27. package/dist/transactions.d.ts +0 -1
  28. package/dist/transactions.d.ts.map +1 -1
  29. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  30. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  31. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  32. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  33. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  34. package/dist/widget/components/Earn.d.ts.map +1 -1
  35. package/dist/widget/components/FeeOption.d.ts +22 -0
  36. package/dist/widget/components/FeeOption.d.ts.map +1 -0
  37. package/dist/widget/components/FeeOptions.d.ts +13 -17
  38. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  39. package/dist/widget/components/Fund.d.ts.map +1 -1
  40. package/dist/widget/components/FundMethods.d.ts +1 -1
  41. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  42. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  43. package/dist/widget/components/MeshConnectExchanges.d.ts +5 -2
  44. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  45. package/dist/widget/components/MeshConnectFlow.d.ts +2 -0
  46. package/dist/widget/components/MeshConnectFlow.d.ts.map +1 -1
  47. package/dist/widget/components/NativeGasOption.d.ts +12 -0
  48. package/dist/widget/components/NativeGasOption.d.ts.map +1 -0
  49. package/dist/widget/components/Pay.d.ts.map +1 -1
  50. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  51. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  52. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  53. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  54. package/dist/widget/hooks/useBack.d.ts +2 -0
  55. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  56. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  57. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  58. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  59. package/dist/widget/hooks/useSelectedFeeToken.d.ts +32 -0
  60. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -0
  61. package/dist/widget/hooks/useSelectedMeshExchange.d.ts +14 -0
  62. package/dist/widget/hooks/useSelectedMeshExchange.d.ts.map +1 -0
  63. package/dist/widget/hooks/useSendForm.d.ts +8 -13
  64. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  65. package/dist/widget/index.js +1 -1
  66. package/dist/widget/widget.d.ts.map +1 -1
  67. package/package.json +29 -28
  68. package/src/analytics.ts +6 -0
  69. package/src/chains.ts +10 -0
  70. package/src/config.ts +25 -10
  71. package/src/constants.ts +11 -10
  72. package/src/gasless.ts +162 -109
  73. package/src/index.ts +1 -1
  74. package/src/indexerClient.ts +73 -1
  75. package/src/intentEntrypoint.ts +66 -101
  76. package/src/intents.ts +0 -2
  77. package/src/prepareSend.ts +1409 -887
  78. package/src/relayer.ts +4 -3
  79. package/src/trails.ts +1 -3
  80. package/src/trailsClient.ts +4 -1
  81. package/src/{balanceInjector.ts → trailsRouter.ts} +14 -14
  82. package/src/transactions.ts +4 -54
  83. package/src/widget/compiled.css +1 -1
  84. package/src/widget/components/AccountSettings.tsx +7 -1
  85. package/src/widget/components/ClassicSwap.tsx +173 -175
  86. package/src/widget/components/ConfigDisplay.tsx +34 -1
  87. package/src/widget/components/ConnectWallet.tsx +168 -11
  88. package/src/widget/components/ConnectedWallets.tsx +184 -102
  89. package/src/widget/components/DebugToast.tsx +3 -3
  90. package/src/widget/components/Earn.tsx +4 -27
  91. package/src/widget/components/FeeOption.tsx +78 -0
  92. package/src/widget/components/FeeOptions.tsx +192 -127
  93. package/src/widget/components/Fund.tsx +18 -27
  94. package/src/widget/components/FundMethods.tsx +3 -3
  95. package/src/widget/components/FundSendForm.tsx +0 -33
  96. package/src/widget/components/MeshConnectExchanges.tsx +32 -3
  97. package/src/widget/components/MeshConnectFlow.tsx +23 -4
  98. package/src/widget/components/NativeGasOption.tsx +99 -0
  99. package/src/widget/components/Pay.tsx +36 -32
  100. package/src/widget/components/PaySendForm.tsx +0 -37
  101. package/src/widget/components/QuoteDetails.tsx +0 -29
  102. package/src/widget/components/TokenSelector.tsx +11 -0
  103. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  104. package/src/widget/components/UserPreferences.tsx +3 -4
  105. package/src/widget/hooks/useBack.tsx +4 -0
  106. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  107. package/src/widget/hooks/useDefaultTokenSelection.tsx +3 -7
  108. package/src/widget/hooks/useSelectedFeeToken.tsx +299 -0
  109. package/src/widget/hooks/useSelectedMeshExchange.tsx +46 -0
  110. package/src/widget/hooks/useSendForm.ts +78 -23
  111. package/src/widget/widget.tsx +173 -111
  112. package/dist/balanceInjector.d.ts +0 -22
  113. package/dist/balanceInjector.d.ts.map +0 -1
@@ -0,0 +1,78 @@
1
+ import type React from "react"
2
+ import { TokenImage } from "./TokenImage.js"
3
+
4
+ export type FeeOption = {
5
+ tokenAddress: string
6
+ tokenSymbol: string
7
+ tokenDecimals: number
8
+ amount: string
9
+ amountUSD: number
10
+ amountFormatted: string
11
+ amountUsdDisplay: string
12
+ tokenImageUrl?: string
13
+ notEnoughBalance?: boolean
14
+ chainId?: number
15
+ }
16
+
17
+ interface FeeOptionProps {
18
+ option: FeeOption
19
+ isSelected: boolean
20
+ onClick: () => void
21
+ chainId?: number
22
+ }
23
+
24
+ export const FeeOption: React.FC<FeeOptionProps> = ({
25
+ option,
26
+ isSelected,
27
+ onClick,
28
+ chainId,
29
+ }) => {
30
+ return (
31
+ <button
32
+ type="button"
33
+ className={`w-full flex items-center justify-between p-1.5 trails-border-radius-input border transition-colors ${
34
+ option.notEnoughBalance
35
+ ? "cursor-not-allowed opacity-50 bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-600"
36
+ : isSelected
37
+ ? "cursor-pointer trails-bg-primary/10 border-blue-500 bg-blue-50 dark:bg-blue-900/20"
38
+ : "cursor-pointer trails-bg-card trails-border-primary hover:trails-hover-bg"
39
+ }`}
40
+ onClick={option.notEnoughBalance ? undefined : onClick}
41
+ disabled={option.notEnoughBalance}
42
+ >
43
+ <div className="flex items-center space-x-2">
44
+ <TokenImage
45
+ imageUrl={option.tokenImageUrl}
46
+ symbol={option.tokenSymbol}
47
+ chainId={option.chainId || chainId}
48
+ size={16}
49
+ />
50
+ <div className="text-left">
51
+ <div
52
+ className={`text-xs font-medium ${
53
+ option.notEnoughBalance
54
+ ? "text-gray-400 dark:text-gray-500"
55
+ : "trails-text-primary"
56
+ }`}
57
+ >
58
+ {option.amountFormatted}
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ <div className="text-right">
64
+ <div
65
+ className={`text-xs font-medium ${
66
+ option.notEnoughBalance
67
+ ? "text-gray-400 dark:text-gray-500"
68
+ : "trails-text-primary"
69
+ }`}
70
+ >
71
+ {option.amountUsdDisplay}
72
+ </div>
73
+ </div>
74
+ </button>
75
+ )
76
+ }
77
+
78
+ export default FeeOption
@@ -1,150 +1,215 @@
1
- import { TokenImage } from "./TokenImage.js"
2
1
  import { ChevronDown } from "lucide-react"
3
2
  import type React from "react"
4
- import { useEffect, useMemo, useRef, useState } from "react"
5
- import { formatRawAmount } from "../../tokenBalances.js"
6
-
7
- export type FeeOption = {
8
- token: {
9
- chainId: number
10
- name: string
11
- symbol: string
12
- type: string
13
- decimals: number
14
- logoURL: string
15
- contractAddress: string | null
16
- tokenID: string | null
17
- }
18
- to: string
19
- value: string
20
- gasLimit: number
3
+ import { useRef, useState } from "react"
4
+ import { formatUsdAmountDisplay, formatRawAmount } from "../../tokenBalances.js"
5
+ import {
6
+ FeeOption,
7
+ type FeeOption as EnhancedFeeOptionType,
8
+ } from "./FeeOption.js"
9
+ import { logger } from "../../logger.js"
10
+ import { getChainInfo } from "../../chains.js"
11
+ import { TokenImage } from "./TokenImage.js"
12
+ import { getTokenImageUrl } from "../../tokens.js"
13
+ import { zeroAddress } from "viem"
14
+
15
+ // Original FeeOption type from useSendForm hook
16
+ type OriginalFeeOption = {
17
+ tokenAddress: string
18
+ tokenSymbol: string
19
+ tokenDecimals: number
20
+ amount: string
21
+ amountUSD: number
22
+ notEnoughBalance?: boolean
23
+ tokenImageUrl?: string
24
+ chainId?: number
21
25
  }
22
26
 
23
27
  interface FeeOptionsProps {
24
- options: FeeOption[]
25
- selectedOption?: FeeOption
26
- onSelect: (option: FeeOption) => void
28
+ feeOptions: OriginalFeeOption[]
29
+ selectedFeeToken: OriginalFeeOption | null
30
+ setSelectedFeeToken: (token: OriginalFeeOption | null) => void
31
+ chainId?: number
27
32
  }
28
33
 
29
- // Enable once we have a way to pay fees with other tokens
30
- const DISABLED_FEE_OPTIONS = true
31
-
32
34
  export const FeeOptions: React.FC<FeeOptionsProps> = ({
33
- options,
34
- selectedOption,
35
- onSelect,
35
+ feeOptions,
36
+ selectedFeeToken,
37
+ setSelectedFeeToken,
38
+ chainId,
36
39
  }) => {
37
- if (DISABLED_FEE_OPTIONS) return null
38
- const [isDropdownOpen, setIsDropdownOpen] = useState(false)
39
- const dropdownRef = useRef<HTMLDivElement>(null)
40
-
41
- const formattedFeeAmount = useMemo(() => {
42
- if (!selectedOption) return "0.00"
43
- return formatRawAmount(selectedOption.value, selectedOption.token.decimals)
44
- }, [selectedOption])
45
-
46
- // Calculate USD value of fee
47
- const feeUsdValue = useMemo(() => {
48
- if (!selectedOption) return ""
49
- return "" // TODO
50
- }, [selectedOption])
51
-
52
- // Set first option as default if none selected
53
- useEffect(() => {
54
- if (!selectedOption && options.length > 0) {
55
- onSelect(options[0]!)
56
- }
57
- }, [selectedOption, options, onSelect])
40
+ const [isOpen, setIsOpen] = useState(false)
41
+ const accordionRef = useRef<HTMLDivElement>(null)
58
42
 
59
- if (!options?.length) {
43
+ // Don't render if no fee options available
44
+ if (!feeOptions || feeOptions.length === 0) {
60
45
  return null
61
46
  }
62
47
 
48
+ // Enhance ALL fee options with formatted values and image URLs (including native gas)
49
+ const enhancedFeeOptions: EnhancedFeeOptionType[] = feeOptions.map(
50
+ (option) => ({
51
+ ...option,
52
+ amountFormatted: `${formatRawAmount(option.amount, option.tokenDecimals)} ${option.tokenSymbol}`,
53
+ amountUsdDisplay: formatUsdAmountDisplay(option.amountUSD),
54
+ tokenImageUrl: getTokenImageUrl({
55
+ chainId: option.chainId || chainId, // Use fee option's chain ID, fallback to prop
56
+ contractAddress: option.tokenAddress,
57
+ symbol: option.tokenSymbol,
58
+ }),
59
+ }),
60
+ )
61
+
62
+ // Find native gas option (zero address) for header display
63
+ const nativeGasOption = enhancedFeeOptions.find(
64
+ (opt) => opt.tokenAddress.toLowerCase() === zeroAddress.toLowerCase(),
65
+ )
66
+
67
+ const handleFeeTokenSelect = (option: EnhancedFeeOptionType) => {
68
+ // Convert back to original format for the parent component
69
+ const originalOption: OriginalFeeOption = {
70
+ tokenAddress: option.tokenAddress,
71
+ tokenSymbol: option.tokenSymbol,
72
+ tokenDecimals: option.tokenDecimals,
73
+ amount: option.amount,
74
+ amountUSD: option.amountUSD,
75
+ }
76
+
77
+ // For native gas (zero address), set to null to trigger non-gasless flow
78
+ if (option.tokenAddress.toLowerCase() === zeroAddress.toLowerCase()) {
79
+ setSelectedFeeToken(null)
80
+ logger.console.log(
81
+ "[trails-sdk] [FEE-SELECT] Selected native gas fee option",
82
+ )
83
+ } else {
84
+ setSelectedFeeToken(originalOption)
85
+ logger.console.log(
86
+ "[trails-sdk] [FEE-SELECT] Selected ERC20 fee option:",
87
+ originalOption,
88
+ )
89
+ }
90
+
91
+ setIsOpen(false)
92
+ }
93
+
94
+ // Get display text and image for selected option
95
+ const getSelectedDisplayText = () => {
96
+ if (
97
+ selectedFeeToken === null ||
98
+ selectedFeeToken?.tokenAddress?.toLowerCase() ===
99
+ zeroAddress.toLowerCase()
100
+ ) {
101
+ // Native gas selected - use native gas option data
102
+ const nativeSymbol =
103
+ nativeGasOption?.tokenSymbol ||
104
+ (chainId ? getNativeSymbol(chainId) : "ETH")
105
+ const nativeImageUrl = getTokenImageUrl({
106
+ chainId,
107
+ contractAddress: zeroAddress,
108
+ symbol: nativeSymbol,
109
+ })
110
+ const amountDisplay = nativeGasOption
111
+ ? `${formatRawAmount(nativeGasOption.amount, nativeGasOption.tokenDecimals)} ${nativeGasOption.tokenSymbol}`
112
+ : ""
113
+ const usdDisplay = nativeGasOption
114
+ ? formatUsdAmountDisplay(nativeGasOption.amountUSD)
115
+ : ""
116
+ return {
117
+ title: nativeSymbol,
118
+ amountDisplay,
119
+ usdDisplay,
120
+ symbol: nativeSymbol,
121
+ imageUrl: nativeImageUrl,
122
+ }
123
+ } else {
124
+ const tokenImageUrl = getTokenImageUrl({
125
+ chainId: selectedFeeToken.chainId || chainId, // Use fee token's chain ID, fallback to prop
126
+ contractAddress: selectedFeeToken.tokenAddress,
127
+ symbol: selectedFeeToken.tokenSymbol,
128
+ })
129
+ const amountDisplay = `${formatRawAmount(selectedFeeToken.amount, selectedFeeToken.tokenDecimals)} ${selectedFeeToken.tokenSymbol}`
130
+ const usdDisplay = formatUsdAmountDisplay(selectedFeeToken.amountUSD)
131
+ return {
132
+ title: selectedFeeToken.tokenSymbol,
133
+ amountDisplay,
134
+ usdDisplay,
135
+ symbol: selectedFeeToken.tokenSymbol,
136
+ imageUrl: tokenImageUrl,
137
+ }
138
+ }
139
+ }
140
+
141
+ // Helper function to get native symbol for a chain
142
+ const getNativeSymbol = (chainId: number) => {
143
+ const chainInfo = getChainInfo(chainId)
144
+ return chainInfo?.nativeCurrency.symbol || "ETH"
145
+ }
146
+
147
+ const selectedDisplay = getSelectedDisplayText()
148
+
63
149
  return (
64
- <div className="space-y-1" ref={dropdownRef}>
65
- <div className="flex items-center justify-between">
66
- <div className="block text-xs font-medium text-gray-700 dark:text-gray-300">
67
- Pay fee with
68
- </div>
69
- {selectedOption && (
70
- <div className="text-xs text-gray-500 dark:text-gray-400">
71
- Fee {formattedFeeAmount} {selectedOption.token.symbol}
72
- <div>
73
- {feeUsdValue && (
74
- <span className="ml-1 text-xs text-gray-500 dark:text-gray-400">
75
- ≈ {feeUsdValue}
76
- </span>
77
- )}
78
- </div>
150
+ <div className="space-y-1" ref={accordionRef}>
151
+ <div className="p-2">
152
+ <div className="flex justify-between items-center mb-2">
153
+ <div className="text-xs font-medium trails-text-primary">
154
+ Gas Fee Options
79
155
  </div>
80
- )}
81
- </div>
82
- <div className="relative flex justify-start">
156
+ <div className="text-xs trails-text-muted">
157
+ Choose how to pay for gas for transfer
158
+ </div>
159
+ </div>
160
+
161
+ {/* Accordion Header */}
83
162
  <button
84
163
  type="button"
85
- onClick={() => setIsDropdownOpen(!isDropdownOpen)}
86
- className="w-32 flex items-center px-3 py-2 border border-solid trails-border-radius-container hover:border-gray-400 cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm bg-white border-gray-300 text-gray-900 dark:bg-gray-800 dark:border-gray-700 dark:text-white"
164
+ onClick={() => setIsOpen(!isOpen)}
165
+ className="w-full flex items-center justify-between p-1.5 trails-border-radius-input border trails-border-primary hover:trails-hover-bg transition-colors cursor-pointer"
87
166
  >
88
- {selectedOption ? (
89
- <>
90
- <TokenImage
91
- symbol={selectedOption.token.symbol}
92
- imageUrl={selectedOption.token.logoURL}
93
- size={16}
94
- />
95
- <span className="ml-1.5 flex-1 text-left">
96
- {selectedOption.token.symbol}
97
- </span>
98
- </>
99
- ) : (
100
- <span className="flex-1 text-left text-gray-400">
101
- Select Fee Token
102
- </span>
103
- )}
104
- <ChevronDown
105
- className={`h-4 w-4 ${
106
- isDropdownOpen ? "transform rotate-180" : ""
107
- }`}
108
- />
167
+ <div className="flex items-center space-x-2">
168
+ <TokenImage
169
+ imageUrl={selectedDisplay.imageUrl}
170
+ symbol={selectedDisplay.symbol}
171
+ chainId={selectedFeeToken?.chainId || chainId}
172
+ size={16}
173
+ />
174
+ <div className="text-left">
175
+ <div className="text-xs font-medium trails-text-primary">
176
+ {selectedDisplay.amountDisplay}
177
+ </div>
178
+ </div>
179
+ </div>
180
+ <div className="flex items-center space-x-2">
181
+ <div className="text-right">
182
+ <div className="text-xs font-medium trails-text-primary">
183
+ {selectedDisplay.usdDisplay}
184
+ </div>
185
+ </div>
186
+ <ChevronDown
187
+ className={`w-3 h-3 trails-text-muted transition-transform ${
188
+ isOpen ? "transform rotate-180" : ""
189
+ }`}
190
+ />
191
+ </div>
109
192
  </button>
110
193
 
111
- {isDropdownOpen && (
112
- <div className="absolute z-10 mt-1 border border-solid trails-border-radius-container shadow-lg w-32 bg-white border-gray-200 dark:bg-gray-800 dark:border-gray-700">
113
- {options.map((option) => (
114
- <button
115
- key={option.token.symbol}
116
- type="button"
117
- onClick={() => {
118
- console.log("[trails] selected fee option", option)
119
- onSelect(option)
120
- setIsDropdownOpen(false)
121
- }}
122
- className={`w-full flex items-center px-3 py-2 cursor-pointer text-sm ${
123
- selectedOption?.token.symbol === option.token.symbol
124
- ? "bg-gray-100 text-gray-900 dark:bg-gray-700 dark:text-white"
125
- : "text-gray-900 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg"
126
- }`}
127
- >
128
- <div
129
- className={`w-4 h-4 rounded-full flex items-center justify-center ${
130
- selectedOption?.token.symbol === option.token.symbol
131
- ? "bg-gray-200 dark:bg-gray-700"
132
- : "bg-gray-100 dark:bg-gray-600"
133
- }`}
134
- >
135
- <TokenImage
136
- symbol={option.token.symbol}
137
- imageUrl={option.token.logoURL}
138
- size={16}
139
- />
140
- </div>
141
- <span className="ml-1.5">{option.token.symbol}</span>
142
- {selectedOption?.token.symbol === option.token.symbol && (
143
- <span className="ml-auto text-gray-900 dark:text-white">
144
-
145
- </span>
146
- )}
147
- </button>
194
+ {/* Accordion Content */}
195
+ {isOpen && (
196
+ <div className="mt-2 space-y-1">
197
+ {/* All Fee Options (including native gas) */}
198
+ {enhancedFeeOptions.map((option, index) => (
199
+ <FeeOption
200
+ key={`${option.tokenAddress}-${index}`}
201
+ option={option}
202
+ isSelected={
203
+ option.tokenAddress.toLowerCase() ===
204
+ zeroAddress.toLowerCase()
205
+ ? selectedFeeToken === null ||
206
+ selectedFeeToken?.tokenAddress?.toLowerCase() ===
207
+ zeroAddress.toLowerCase()
208
+ : selectedFeeToken?.tokenAddress === option.tokenAddress
209
+ }
210
+ onClick={() => handleFeeTokenSelect(option)}
211
+ chainId={option.chainId || chainId}
212
+ />
148
213
  ))}
149
214
  </div>
150
215
  )}
@@ -21,6 +21,7 @@ import { useMode } from "../hooks/useMode.js"
21
21
  import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
22
22
  import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
23
23
  import { useDefaultTokenSelection } from "../hooks/useDefaultTokenSelection.js" // Context version
24
+ import { useTargetAmount } from "../hooks/useTargetAmount.js"
24
25
  import { TokenImage } from "./TokenImage.js"
25
26
  import { Identicon } from "./Identicon.js"
26
27
  import { TokenSelector } from "./TokenSelector.js"
@@ -29,8 +30,6 @@ import { ChainList } from "./ChainList.js"
29
30
  import { QuoteDetails } from "./QuoteDetails.js"
30
31
  import { ErrorDisplay } from "./ErrorDisplay.js"
31
32
  import { getChainInfo } from "../../chains.js"
32
- import { formatUsdAmountDisplay } from "../../tokenBalances.js"
33
- import { MINIMUM_USD_AMOUNT_FOR_SWAP } from "../../constants.js"
34
33
  import { truncateAddress } from "../../utils.js"
35
34
  import type { PrepareSendQuote } from "../../prepareSend.js"
36
35
  import type { SupportedToken } from "../../tokens.js"
@@ -108,6 +107,7 @@ export const Fund: React.FC<FundProps> = ({
108
107
  const { selectedRecipient, setSelectedRecipient } = useSelectedRecipient()
109
108
  const { amount: globalAmount, setAmount: setGlobalAmount } = useSwapAmount()
110
109
  const { setCurrentScreen } = useCurrentScreen()
110
+ const { targetAmountUsd } = useTargetAmount()
111
111
 
112
112
  // Use new default token selection hook
113
113
  const {
@@ -639,6 +639,7 @@ export const Fund: React.FC<FundProps> = ({
639
639
  />
640
640
  <TokenSelector
641
641
  onTokenSelect={handleOriginTokenSelect}
642
+ targetAmountUsd={targetAmountUsd}
642
643
  onError={onError}
643
644
  fundMethod={fundMethod}
644
645
  showContinueButton={false}
@@ -665,6 +666,9 @@ export const Fund: React.FC<FundProps> = ({
665
666
  setShowOriginTokenSelector(false)
666
667
  setShowOriginChainList(true)
667
668
  }}
669
+ onNavigateToFundMethods={() => {
670
+ setCurrentScreen("fund-methods")
671
+ }}
668
672
  />
669
673
  </div>
670
674
  )
@@ -682,6 +686,7 @@ export const Fund: React.FC<FundProps> = ({
682
686
  />
683
687
  <TokenSelector
684
688
  onTokenSelect={handleDestinationTokenSelect}
689
+ targetAmountUsd={targetAmountUsd}
685
690
  onError={onError}
686
691
  fundMethod={fundMethod}
687
692
  showContinueButton={false}
@@ -767,6 +772,13 @@ export const Fund: React.FC<FundProps> = ({
767
772
  <div className="flex justify-between items-center mb-2">
768
773
  <div className="text-sm font-medium trails-text-secondary text-left">
769
774
  Deposit
775
+ {fundMethod === "qr-code" ? (
776
+ <span className="text-xs"> with QR Code</span>
777
+ ) : fundMethod === "exchange" ? (
778
+ <span className="text-xs"> with Exchange</span>
779
+ ) : (
780
+ ""
781
+ )}
770
782
  </div>
771
783
 
772
784
  {/* Percentage Buttons */}
@@ -807,7 +819,10 @@ export const Fund: React.FC<FundProps> = ({
807
819
  <div className="flex items-center space-x-2">
808
820
  {/* Amount Input */}
809
821
  <div className="flex-1">
810
- <div className="flex items-center justify-start">
822
+ <div
823
+ className="flex items-center justify-start cursor-text"
824
+ onClick={() => inputRef.current?.focus()}
825
+ >
811
826
  <div className="flex items-center">
812
827
  <input
813
828
  ref={inputRef}
@@ -1166,30 +1181,6 @@ export const Fund: React.FC<FundProps> = ({
1166
1181
  </p>
1167
1182
  </div>
1168
1183
  </div>
1169
- ) : prepareSendQuote?.minimumNotMet ? (
1170
- <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
1171
- <div className="flex items-center space-x-2">
1172
- <svg
1173
- className="w-4 h-4 text-amber-500 flex-shrink-0"
1174
- fill="none"
1175
- stroke="currentColor"
1176
- viewBox="0 0 24 24"
1177
- aria-hidden="true"
1178
- >
1179
- <path
1180
- strokeLinecap="round"
1181
- strokeLinejoin="round"
1182
- strokeWidth={2}
1183
- 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"
1184
- />
1185
- </svg>
1186
- <p className="text-sm text-amber-600 dark:text-amber-400">
1187
- Please enter an amount above{" "}
1188
- {formatUsdAmountDisplay(MINIMUM_USD_AMOUNT_FOR_SWAP)} otherwise
1189
- transfer may fail
1190
- </p>
1191
- </div>
1192
- </div>
1193
1184
  ) : null}
1194
1185
 
1195
1186
  {/* Fund Button */}
@@ -10,7 +10,7 @@ import { ScreenHeader } from "./ScreenHeader.js"
10
10
  export interface FundMethodsProps {
11
11
  onBack?: () => void
12
12
  onSelectWalletConnect: () => void
13
- onSelectExchange: () => void
13
+ onSelectExchangeList: () => void
14
14
  onSelectConnectedAccount: () => void
15
15
  onSelectQrCode: () => void
16
16
  }
@@ -18,7 +18,7 @@ export interface FundMethodsProps {
18
18
  const FundMethods: React.FC<FundMethodsProps> = ({
19
19
  onBack,
20
20
  onSelectWalletConnect,
21
- onSelectExchange,
21
+ onSelectExchangeList,
22
22
  onSelectConnectedAccount,
23
23
  onSelectQrCode,
24
24
  }) => {
@@ -158,7 +158,7 @@ const FundMethods: React.FC<FundMethodsProps> = ({
158
158
 
159
159
  <button
160
160
  type="button"
161
- onClick={onSelectExchange}
161
+ onClick={onSelectExchangeList}
162
162
  className="w-full flex items-center justify-between cursor-pointer font-semibold py-4 px-6 trails-border-radius-large-button transition-all duration-200 trails-bg-secondary trails-hover-bg trails-text-primary"
163
163
  >
164
164
  <div className="flex items-center space-x-3 flex-1">
@@ -20,7 +20,6 @@ import { ErrorDisplay } from "./ErrorDisplay.js"
20
20
  import { useMode } from "../hooks/useMode.js"
21
21
  import { getExplorerUrlForAddress } from "../../explorer.js"
22
22
  import { logger } from "../../logger.js"
23
- import { FeeOptions } from "./FeeOptions.js"
24
23
  import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
25
24
 
26
25
  interface FundSendFormProps {
@@ -138,9 +137,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
138
137
  quoteErrorPrettified,
139
138
  isSameTokenWithoutCustomCalldata,
140
139
  destinationTokenAddress,
141
- feeOptions,
142
- selectedFeeToken,
143
- setSelectedFeeToken,
144
140
  isRecipientContract,
145
141
  } = useSendForm({
146
142
  account,
@@ -825,13 +821,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
825
821
  chainId={selectedDestinationChain.id}
826
822
  /> */}
827
823
 
828
- {/* Fee Options */}
829
- <FeeOptions
830
- options={feeOptions}
831
- selectedOption={selectedFeeToken ?? undefined}
832
- onSelect={setSelectedFeeToken}
833
- />
834
-
835
824
  {/* Warning Messages - Show only one at a time */}
836
825
  {isSameTokenWithoutCustomCalldata ? (
837
826
  <ErrorDisplay
@@ -867,28 +856,6 @@ export const FundSendForm: React.FC<FundSendFormProps> = ({
867
856
  </p>
868
857
  </div>
869
858
  </div>
870
- ) : prepareSendQuote?.minimumNotMet ? (
871
- <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
872
- <div className="flex items-center space-x-2">
873
- <svg
874
- className="w-4 h-4 text-amber-500 flex-shrink-0"
875
- fill="none"
876
- stroke="currentColor"
877
- viewBox="0 0 24 24"
878
- aria-hidden="true"
879
- >
880
- <path
881
- strokeLinecap="round"
882
- strokeLinejoin="round"
883
- strokeWidth={2}
884
- 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"
885
- />
886
- </svg>
887
- <p className="text-sm text-amber-600 dark:text-amber-400">
888
- Please enter an amount above $0.15 otherwise transfer may fail
889
- </p>
890
- </div>
891
- </div>
892
859
  ) : null}
893
860
 
894
861
  {/* Continue Button */}
@@ -11,16 +11,26 @@ import BinanceLogo from "../assets/Binance_Icon_Logo.svg"
11
11
  import CoinbaseLogo from "../assets/Coinbase_Icon_Logo.svg"
12
12
  import { ScreenHeader } from "./ScreenHeader.js"
13
13
  import { logger } from "../../logger.js"
14
+ import { useSelectedMeshExchange } from "../hooks/useSelectedMeshExchange.js"
15
+ import { useMode } from "../hooks/useMode.js"
16
+ import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
17
+ import type { Mode } from "../../mode.js"
18
+ import type { Screen } from "../hooks/useCurrentScreen.js"
14
19
 
15
20
  export interface MeshConnectExchangesProps {
16
- onBack: () => void
17
- onSelectExchange: (integrationId: string, exchangeName: string) => void
21
+ onBack?: () => void
22
+ onSelectExchange?: (integrationId: string, exchangeName: string) => void
23
+ getInitialScreenForMode?: (mode: Mode) => Screen
18
24
  }
19
25
 
20
26
  export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
21
27
  onBack,
22
28
  onSelectExchange,
29
+ getInitialScreenForMode,
23
30
  }) => {
31
+ const { setSelectedExchange } = useSelectedMeshExchange()
32
+ const { mode } = useMode()
33
+ const { setCurrentScreen } = useCurrentScreen()
24
34
  const [coinbaseId, setCoinbaseId] = useState<string | null>(null)
25
35
  const [binanceId, setBinanceId] = useState<string | null>(null)
26
36
  const [bitfinexId, setBitfinexId] = useState<string | null>(null)
@@ -73,7 +83,26 @@ export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
73
83
  logger.console.log(
74
84
  `[trails-sdk] Selected exchange: ${exchangeName} (${integrationId})`,
75
85
  )
76
- onSelectExchange(integrationId, exchangeName)
86
+
87
+ // If a callback is provided (MeshConnectFlow), use it
88
+ if (onSelectExchange) {
89
+ onSelectExchange(integrationId, exchangeName)
90
+ return
91
+ }
92
+
93
+ // Otherwise, use the hook-based approach (main widget flow)
94
+ // Store the selected exchange
95
+ setSelectedExchange({ integrationId, exchangeName })
96
+
97
+ // Navigate to the initial screen for the current mode
98
+ if (getInitialScreenForMode) {
99
+ const currentMode = (mode || "pay") as Mode
100
+ const initialScreen = getInitialScreenForMode(currentMode)
101
+ logger.console.log(
102
+ `[trails-sdk] Navigating to initial screen for mode ${currentMode}: ${initialScreen}`,
103
+ )
104
+ setCurrentScreen(initialScreen)
105
+ }
77
106
  }
78
107
 
79
108
  if (loading) {