0xtrails 0.2.6 → 0.4.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 (218) hide show
  1. package/dist/aave.d.ts +2 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/{ccip-Xjh9d1gb.js → ccip-BpQGQiWq.js} +7 -7
  4. package/dist/config.d.ts +0 -5
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/constants.d.ts +2 -4
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/error.d.ts +4 -1
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/fees.d.ts +2 -2
  11. package/dist/fees.d.ts.map +1 -1
  12. package/dist/{index-BnhdZ8Ho.js → index-DsJM5F-V.js} +46084 -48697
  13. package/dist/index.d.ts +9 -8
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +741 -923
  16. package/dist/intentReceiptMonitor.d.ts +24 -0
  17. package/dist/intentReceiptMonitor.d.ts.map +1 -0
  18. package/dist/intentReceiptPoller.d.ts +69 -0
  19. package/dist/intentReceiptPoller.d.ts.map +1 -0
  20. package/dist/intents.d.ts +15 -11
  21. package/dist/intents.d.ts.map +1 -1
  22. package/dist/morpho.d.ts +6 -5
  23. package/dist/morpho.d.ts.map +1 -1
  24. package/dist/mutations.d.ts +16 -0
  25. package/dist/mutations.d.ts.map +1 -0
  26. package/dist/preconditions.d.ts +5 -4
  27. package/dist/preconditions.d.ts.map +1 -1
  28. package/dist/prepareSend.d.ts +5 -190
  29. package/dist/prepareSend.d.ts.map +1 -1
  30. package/dist/prices.d.ts +9 -6
  31. package/dist/prices.d.ts.map +1 -1
  32. package/dist/sequenceWallet.d.ts +3 -16
  33. package/dist/sequenceWallet.d.ts.map +1 -1
  34. package/dist/tokenBalances.d.ts +17 -13
  35. package/dist/tokenBalances.d.ts.map +1 -1
  36. package/dist/trails.d.ts +24 -40
  37. package/dist/trails.d.ts.map +1 -1
  38. package/dist/trailsClient.d.ts +5 -6
  39. package/dist/trailsClient.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 +2 -1
  85. package/dist/transactions.d.ts.map +1 -1
  86. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  87. package/dist/widget/components/ClassicSwap.d.ts +0 -1
  88. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  89. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  90. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  91. package/dist/widget/components/DynamicSizeInputField.d.ts +13 -0
  92. package/dist/widget/components/DynamicSizeInputField.d.ts.map +1 -0
  93. package/dist/widget/components/Earn.d.ts +0 -1
  94. package/dist/widget/components/Earn.d.ts.map +1 -1
  95. package/dist/widget/components/FeeOptions.d.ts +5 -13
  96. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  97. package/dist/widget/components/Fund.d.ts +0 -1
  98. package/dist/widget/components/Fund.d.ts.map +1 -1
  99. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  100. package/dist/widget/components/FundSwap.d.ts +0 -1
  101. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  102. package/dist/widget/components/Pay.d.ts +0 -1
  103. package/dist/widget/components/Pay.d.ts.map +1 -1
  104. package/dist/widget/components/PoolDeposit.d.ts +0 -1
  105. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  106. package/dist/widget/components/PoolWithdraw.d.ts +0 -18
  107. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  108. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  109. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  110. package/dist/widget/components/Swap.d.ts +0 -1
  111. package/dist/widget/components/Swap.d.ts.map +1 -1
  112. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  113. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  114. package/dist/widget/css/compiled.css +2 -2
  115. package/dist/widget/hooks/useCheckout.d.ts +17 -4
  116. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  117. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  118. package/dist/widget/hooks/useQuote.d.ts +3 -4
  119. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  120. package/dist/widget/hooks/useSelectedFeeToken.d.ts +1 -0
  121. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  122. package/dist/widget/hooks/useSendForm.d.ts +3 -4
  123. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  124. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  125. package/dist/widget/hooks/useWalletConnectionContext.d.ts +25 -0
  126. package/dist/widget/hooks/useWalletConnectionContext.d.ts.map +1 -0
  127. package/dist/widget/index.js +1 -1
  128. package/dist/widget/widget.d.ts +12 -7
  129. package/dist/widget/widget.d.ts.map +1 -1
  130. package/package.json +21 -23
  131. package/src/aave.ts +54 -1
  132. package/src/config.ts +57 -58
  133. package/src/constants.ts +8 -9
  134. package/src/error.ts +21 -3
  135. package/src/fees.ts +53 -42
  136. package/src/index.ts +35 -13
  137. package/src/intentReceiptMonitor.ts +102 -0
  138. package/src/intentReceiptPoller.ts +299 -0
  139. package/src/intents.ts +206 -172
  140. package/src/morpho.ts +58 -9
  141. package/src/mutations.ts +129 -0
  142. package/src/preconditions.ts +16 -21
  143. package/src/prepareSend.ts +80 -4514
  144. package/src/prices.ts +26 -22
  145. package/src/relaySdk.ts +2 -2
  146. package/src/sequenceWallet.ts +6 -73
  147. package/src/tokenBalances.ts +175 -69
  148. package/src/trails.ts +230 -722
  149. package/src/trailsClient.ts +10 -23
  150. package/src/transactionIntent/constants.ts +11 -0
  151. package/src/transactionIntent/deposits/depositOrchestrator.ts +210 -0
  152. package/src/transactionIntent/deposits/gaslessDeposit.ts +588 -0
  153. package/src/transactionIntent/deposits/index.ts +3 -0
  154. package/src/transactionIntent/deposits/standardDeposit.ts +379 -0
  155. package/src/transactionIntent/execution/index.ts +1 -0
  156. package/src/transactionIntent/execution/transactionState.ts +35 -0
  157. package/src/transactionIntent/handlers/crossChain.ts +1707 -0
  158. package/src/transactionIntent/handlers/index.ts +3 -0
  159. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +323 -0
  160. package/src/transactionIntent/handlers/sameChainSameToken.ts +712 -0
  161. package/src/transactionIntent/index.ts +9 -0
  162. package/src/transactionIntent/quote/feeExtractors.ts +81 -0
  163. package/src/transactionIntent/quote/index.ts +3 -0
  164. package/src/transactionIntent/quote/normalizeQuote.ts +367 -0
  165. package/src/transactionIntent/quote/quoteHelpers.ts +53 -0
  166. package/src/transactionIntent/types.ts +157 -0
  167. package/src/transactionIntent/utils/balanceChecker.ts +96 -0
  168. package/src/transactionIntent/utils/index.ts +3 -0
  169. package/src/transactionIntent/utils/lifiHelpers.ts +68 -0
  170. package/src/transactionIntent/utils/testnetHelpers.ts +10 -0
  171. package/src/transactionIntent/validators.ts +57 -0
  172. package/src/transactions.ts +36 -53
  173. package/src/widget/compiled.css +2 -2
  174. package/src/widget/components/AccountIntentTransactionHistory.tsx +36 -36
  175. package/src/widget/components/AccountSettings.tsx +23 -6
  176. package/src/widget/components/ClassicSwap.tsx +28 -53
  177. package/src/widget/components/ConfigDisplay.tsx +0 -11
  178. package/src/widget/components/ConnectedWallets.tsx +30 -4
  179. package/src/widget/components/DynamicSizeInputField.tsx +109 -0
  180. package/src/widget/components/Earn.tsx +0 -16
  181. package/src/widget/components/FeeBreakdown.tsx +3 -3
  182. package/src/widget/components/FeeOption.tsx +2 -2
  183. package/src/widget/components/FeeOptions.tsx +151 -112
  184. package/src/widget/components/Fund.tsx +0 -3
  185. package/src/widget/components/FundMethods.tsx +4 -3
  186. package/src/widget/components/FundSwap.tsx +0 -1
  187. package/src/widget/components/Pay.tsx +11 -16
  188. package/src/widget/components/PoolDeposit.tsx +35 -32
  189. package/src/widget/components/PoolWithdraw.tsx +153 -256
  190. package/src/widget/components/QuoteDetails.tsx +899 -494
  191. package/src/widget/components/Swap.tsx +0 -1
  192. package/src/widget/components/TransferPendingVertical.tsx +12 -8
  193. package/src/widget/components/WaasFeeOptions.tsx +23 -7
  194. package/src/widget/components/WalletConfirmation.tsx +1 -1
  195. package/src/widget/hooks/useAmountUsd.ts +9 -9
  196. package/src/widget/hooks/useCheckout.ts +97 -9
  197. package/src/widget/hooks/useDefaultTokenSelection.tsx +27 -21
  198. package/src/widget/hooks/useQuote.ts +86 -33
  199. package/src/widget/hooks/useSelectedFeeToken.tsx +32 -37
  200. package/src/widget/hooks/useSendForm.ts +37 -47
  201. package/src/widget/hooks/useTokenList.ts +34 -26
  202. package/src/widget/hooks/useWalletConnectionContext.tsx +128 -0
  203. package/src/widget/widget.tsx +197 -207
  204. package/dist/apiClient.d.ts +0 -9
  205. package/dist/apiClient.d.ts.map +0 -1
  206. package/dist/intentEntrypoint.d.ts +0 -114
  207. package/dist/intentEntrypoint.d.ts.map +0 -1
  208. package/dist/metaTxnMonitor.d.ts +0 -15
  209. package/dist/metaTxnMonitor.d.ts.map +0 -1
  210. package/dist/metaTxns.d.ts +0 -11
  211. package/dist/metaTxns.d.ts.map +0 -1
  212. package/dist/relayer.d.ts +0 -43
  213. package/dist/relayer.d.ts.map +0 -1
  214. package/src/apiClient.ts +0 -35
  215. package/src/intentEntrypoint.ts +0 -203
  216. package/src/metaTxnMonitor.ts +0 -171
  217. package/src/metaTxns.ts +0 -45
  218. package/src/relayer.ts +0 -289
@@ -1,34 +1,76 @@
1
1
  import { ChevronDown } from "lucide-react"
2
2
  import type React from "react"
3
- import { useRef, useState } from "react"
3
+ import { useRef, useState, useEffect } from "react"
4
4
  import { formatUsdAmountDisplay, formatRawAmount } from "../../tokenBalances.js"
5
5
  import {
6
6
  FeeOption,
7
7
  type FeeOption as EnhancedFeeOptionType,
8
8
  } from "./FeeOption.js"
9
9
  import { logger } from "../../logger.js"
10
- import { getChainInfo } from "../../chains.js"
11
- import { TokenImage } from "./TokenImage.js"
12
10
  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
11
+ import { TokenImage } from "./TokenImage.js"
12
+ import { ethAddress, zeroAddress } from "viem"
13
+ import type { FeeOption as APIFeeOption } from "../../widget/hooks/useSelectedFeeToken.js"
14
+
15
+ const ZERO_ADDRESS = zeroAddress.toLowerCase()
16
+ const ETH_ADDRESS = ethAddress.toLowerCase()
17
+
18
+ const normalizeAddress = (address?: string | null): string =>
19
+ (address ?? "").toLowerCase()
20
+
21
+ const isNativeTokenAddress = (address?: string | null): boolean => {
22
+ const normalized = normalizeAddress(address)
23
+ return normalized === ZERO_ADDRESS || normalized === ETH_ADDRESS
24
+ }
25
+
26
+ const safeFormatAmountWithSymbol = (option: APIFeeOption): string => {
27
+ const tokenDecimals =
28
+ typeof option.tokenDecimals === "number" &&
29
+ option.tokenDecimals > 0 &&
30
+ option.tokenDecimals <= 18
31
+ ? option.tokenDecimals
32
+ : isNativeTokenAddress(option.tokenAddress)
33
+ ? 18
34
+ : undefined
35
+
36
+ if (tokenDecimals === undefined) {
37
+ logger.console.warn("[trails-sdk] [FEE-OPTIONS] Missing token decimals", {
38
+ tokenAddress: option.tokenAddress,
39
+ tokenSymbol: option.tokenSymbol,
40
+ })
41
+ return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
42
+ }
43
+
44
+ try {
45
+ const formattedAmount = formatRawAmount(option.amount, tokenDecimals)
46
+ return option.tokenSymbol
47
+ ? `${formattedAmount} ${option.tokenSymbol}`
48
+ : formattedAmount
49
+ } catch (error) {
50
+ logger.console.warn(
51
+ "[trails-sdk] [FEE-OPTIONS] Failed to format fee option amount",
52
+ {
53
+ option,
54
+ tokenDecimals,
55
+ error,
56
+ },
57
+ )
58
+ return option.tokenSymbol ? `-- ${option.tokenSymbol}` : "--"
59
+ }
60
+ }
61
+
62
+ const safeFormatUsdDisplay = (option: APIFeeOption): string => {
63
+ const usdValue =
64
+ (option as { amountUsd?: number }).amountUsd ?? option.amountUSD ?? 0
65
+ return formatUsdAmountDisplay(usdValue)
25
66
  }
26
67
 
27
68
  interface FeeOptionsProps {
28
- feeOptions: OriginalFeeOption[]
29
- selectedFeeToken: OriginalFeeOption | null
30
- setSelectedFeeToken: (token: OriginalFeeOption | null) => void
69
+ feeOptions: APIFeeOption[]
70
+ selectedFeeToken: APIFeeOption | null
71
+ setSelectedFeeToken: (token: APIFeeOption | null) => void
31
72
  chainId?: number
73
+ isRefetching?: boolean // When true, the fee quote is stale and being refreshed, so hide the component
32
74
  }
33
75
 
34
76
  export const FeeOptions: React.FC<FeeOptionsProps> = ({
@@ -36,23 +78,53 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
36
78
  selectedFeeToken,
37
79
  setSelectedFeeToken,
38
80
  chainId,
81
+ isRefetching,
39
82
  }) => {
40
- const [isOpen, setIsOpen] = useState(false)
41
- const accordionRef = useRef<HTMLDivElement>(null)
83
+ // Early returns BEFORE any hooks - this prevents "Rendered more hooks" error
84
+ // Hide component when fee quote is stale and being refreshed
85
+ if (isRefetching) {
86
+ return null
87
+ }
42
88
 
43
89
  // Don't render if no fee options available
44
90
  if (!feeOptions || feeOptions.length === 0) {
45
91
  return null
46
92
  }
47
93
 
94
+ // Check if there are non-native options before calling hooks
95
+ const hasNonNativeOptions = feeOptions.some(
96
+ (opt) => !isNativeTokenAddress(opt.tokenAddress),
97
+ )
98
+
99
+ // Don't render if only native token is available
100
+ if (!hasNonNativeOptions) {
101
+ return null
102
+ }
103
+
104
+ // NOW we can safely call hooks after all early returns
105
+ const [isOpen, setIsOpen] = useState(false)
106
+ const accordionRef = useRef<HTMLDivElement>(null)
107
+
108
+ // Clear selected fee token when feeOptions change (stale quote)
109
+ // This resets the user's fee selection when they pick a different token
110
+ // Use comprehensive key that includes token addresses and amounts
111
+ const feeOptionsKey = feeOptions
112
+ .map((opt) => `${opt.tokenAddress}-${opt.tokenSymbol}-${opt.amount}`)
113
+ .join("|")
114
+
115
+ // biome-ignore lint/correctness/useExhaustiveDependencies: setSelectedFeeToken is stable
116
+ useEffect(() => {
117
+ setSelectedFeeToken(null)
118
+ }, [feeOptionsKey])
119
+
48
120
  // Enhance ALL fee options with formatted values and image URLs (including native gas)
49
121
  const enhancedFeeOptions: EnhancedFeeOptionType[] = feeOptions.map(
50
122
  (option) => ({
51
123
  ...option,
52
- amountFormatted: `${formatRawAmount(option.amount, option.tokenDecimals)} ${option.tokenSymbol}`,
53
- amountUsdDisplay: formatUsdAmountDisplay(option.amountUSD),
124
+ amountFormatted: safeFormatAmountWithSymbol(option),
125
+ amountUsdDisplay: safeFormatUsdDisplay(option),
54
126
  tokenImageUrl: getTokenImageUrl({
55
- chainId: option.chainId || chainId, // Use fee option's chain ID, fallback to prop
127
+ chainId: option.chainId || chainId,
56
128
  contractAddress: option.tokenAddress,
57
129
  symbol: option.tokenSymbol,
58
130
  }),
@@ -60,92 +132,54 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
60
132
  )
61
133
 
62
134
  // Find native gas option (zero address) for header display
63
- const nativeGasOption = enhancedFeeOptions.find(
64
- (opt) => opt.tokenAddress.toLowerCase() === zeroAddress.toLowerCase(),
135
+ const nativeGasOption = enhancedFeeOptions.find((opt) =>
136
+ isNativeTokenAddress(opt.tokenAddress),
65
137
  )
66
138
 
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,
139
+ // Find the currently selected option from enhancedFeeOptions for header display
140
+ const getSelectedOption = (): EnhancedFeeOptionType | undefined => {
141
+ if (
142
+ selectedFeeToken === null ||
143
+ isNativeTokenAddress(selectedFeeToken?.tokenAddress)
144
+ ) {
145
+ return nativeGasOption
146
+ }
147
+
148
+ if (!selectedFeeToken) {
149
+ return undefined
75
150
  }
76
151
 
77
- // For native gas (zero address), set to null to trigger non-gasless flow
78
- if (option.tokenAddress.toLowerCase() === zeroAddress.toLowerCase()) {
152
+ const selectedAddress = normalizeAddress(selectedFeeToken.tokenAddress)
153
+
154
+ return enhancedFeeOptions.find(
155
+ (opt) => normalizeAddress(opt.tokenAddress) === selectedAddress,
156
+ )
157
+ }
158
+
159
+ const selectedOption = getSelectedOption()
160
+
161
+ // Use nativeGasOption as fallback when selectedOption is null/undefined
162
+ const displayOption = selectedOption || nativeGasOption
163
+
164
+ const handleFeeTokenSelect = (option: EnhancedFeeOptionType) => {
165
+ // For native gas (zero/ETH address), set to null to trigger non-gasless flow
166
+ if (isNativeTokenAddress(option.tokenAddress)) {
79
167
  setSelectedFeeToken(null)
80
168
  logger.console.log(
81
169
  "[trails-sdk] [FEE-SELECT] Selected native gas fee option",
82
170
  )
83
171
  } else {
84
- setSelectedFeeToken(originalOption)
172
+ // Use the option directly - it already has all required fields
173
+ setSelectedFeeToken(option as APIFeeOption)
85
174
  logger.console.log(
86
175
  "[trails-sdk] [FEE-SELECT] Selected ERC20 fee option:",
87
- originalOption,
176
+ option,
88
177
  )
89
178
  }
90
179
 
91
180
  setIsOpen(false)
92
181
  }
93
182
 
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
-
149
183
  return (
150
184
  <div className="space-y-1" ref={accordionRef}>
151
185
  <div className="p-2">
@@ -165,24 +199,30 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
165
199
  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"
166
200
  >
167
201
  <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>
202
+ {displayOption && (
203
+ <>
204
+ <TokenImage
205
+ imageUrl={displayOption.tokenImageUrl}
206
+ symbol={displayOption.tokenSymbol}
207
+ chainId={displayOption.chainId || chainId}
208
+ size={16}
209
+ />
210
+ <div className="ml-2 text-left">
211
+ <div className="text-xs font-medium trails-text-primary">
212
+ {displayOption.amountFormatted}
213
+ </div>
214
+ </div>
215
+ </>
216
+ )}
179
217
  </div>
180
218
  <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}
219
+ {displayOption && (
220
+ <div className="text-right">
221
+ <div className="text-xs font-medium trails-text-primary">
222
+ ≈ {displayOption.amountUsdDisplay}
223
+ </div>
184
224
  </div>
185
- </div>
225
+ )}
186
226
  <ChevronDown
187
227
  className={`w-3 h-3 trails-text-muted transition-transform ${
188
228
  isOpen ? "transform rotate-180" : ""
@@ -200,12 +240,11 @@ export const FeeOptions: React.FC<FeeOptionsProps> = ({
200
240
  key={`${option.tokenAddress}-${index}`}
201
241
  option={option}
202
242
  isSelected={
203
- option.tokenAddress.toLowerCase() ===
204
- zeroAddress.toLowerCase()
243
+ isNativeTokenAddress(option.tokenAddress)
205
244
  ? selectedFeeToken === null ||
206
- selectedFeeToken?.tokenAddress?.toLowerCase() ===
207
- zeroAddress.toLowerCase()
208
- : selectedFeeToken?.tokenAddress === option.tokenAddress
245
+ isNativeTokenAddress(selectedFeeToken?.tokenAddress)
246
+ : normalizeAddress(selectedFeeToken?.tokenAddress) ===
247
+ normalizeAddress(option.tokenAddress)
209
248
  }
210
249
  onClick={() => handleFeeTokenSelect(option)}
211
250
  chainId={option.chainId || chainId}
@@ -45,7 +45,6 @@ interface FundProps {
45
45
  onComplete: (result: OnCompleteProps) => void
46
46
  onSend: (amount: string, recipient: string) => void
47
47
  paymasterUrls?: Array<{ chainId: number; url: string }>
48
- gasless?: boolean
49
48
  isSequenceWallet?: boolean
50
49
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
51
50
  quoteProvider?: string
@@ -81,7 +80,6 @@ export const Fund: React.FC<FundProps> = ({
81
80
  onComplete,
82
81
  onSend,
83
82
  paymasterUrls,
84
- gasless,
85
83
  isSequenceWallet = false,
86
84
  setWalletConfirmRetryHandler,
87
85
  quoteProvider,
@@ -161,7 +159,6 @@ export const Fund: React.FC<FundProps> = ({
161
159
  onError,
162
160
  onWaitingForWalletConfirm,
163
161
  paymasterUrls,
164
- gasless,
165
162
  onConfirm,
166
163
  onComplete,
167
164
  onSend,
@@ -1,5 +1,5 @@
1
1
  import type React from "react"
2
- import { QrCode, Send, ChevronRight } from "lucide-react"
2
+ import { QrCode, ArrowLeftRight, ChevronRight } from "lucide-react"
3
3
  import { useSwitchAccount } from "wagmi"
4
4
  import { ScreenHeader } from "./ScreenHeader.js"
5
5
  import { ConnectedWallets } from "./ConnectedWallets.js"
@@ -103,10 +103,11 @@ const FundMethods: React.FC<FundMethodsProps> = ({
103
103
  <button
104
104
  type="button"
105
105
  onClick={onSelectExchangeList}
106
- 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"
106
+ disabled
107
+ className="w-full text-left px-3 py-4 text-sm flex items-center justify-between cursor-not-allowed transition-colors opacity-50"
107
108
  >
108
109
  <div className="flex items-center gap-3">
109
- <Send className="w-4 h-4" />
110
+ <ArrowLeftRight className="w-4 h-4" />
110
111
  <span className="text-sm font-bold">Send from Exchange</span>
111
112
  </div>
112
113
  <ChevronRight className="w-5 h-5 text-gray-400" />
@@ -24,7 +24,6 @@ interface FundProps {
24
24
  onError: (error: Error | string | null) => void
25
25
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
26
26
  paymasterUrls?: Array<{ chainId: number; url: string }>
27
- gasless?: boolean
28
27
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
29
28
  quoteProvider?: string
30
29
  fundMethod?: string
@@ -37,6 +37,7 @@ import { RequiredPropsError } from "./RequiredPropsError.js"
37
37
  import { FundingMethodSelectorButton } from "./FundingMethodSelectorButton.js"
38
38
  import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
39
39
  import { useDynamicInputStyles } from "./DynamicInputStyles.js"
40
+ import { DynamicSizeInputField } from "./DynamicSizeInputField.js"
40
41
 
41
42
  interface PayProps {
42
43
  selectedToken?: any // Origin token (optional - user can select)
@@ -50,7 +51,6 @@ interface PayProps {
50
51
  onComplete: (result: OnCompleteProps) => void
51
52
  onSend: (amount: string, recipient: string) => void
52
53
  paymasterUrls?: Array<{ chainId: number; url: string }>
53
- gasless?: boolean
54
54
  isSequenceWallet?: boolean
55
55
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
56
56
  quoteProvider?: string
@@ -88,7 +88,6 @@ export const Pay: React.FC<PayProps> = ({
88
88
  onComplete,
89
89
  onSend,
90
90
  paymasterUrls,
91
- gasless,
92
91
  isSequenceWallet = false,
93
92
  setWalletConfirmRetryHandler,
94
93
  quoteProvider,
@@ -210,7 +209,6 @@ export const Pay: React.FC<PayProps> = ({
210
209
  onError,
211
210
  onWaitingForWalletConfirm,
212
211
  paymasterUrls,
213
- gasless,
214
212
  onConfirm,
215
213
  onComplete,
216
214
  onSend,
@@ -457,11 +455,6 @@ export const Pay: React.FC<PayProps> = ({
457
455
  // Dynamic font size based on input length for destination amount
458
456
  const inputStyles = useDynamicInputStyles({ inputValue: displayAmount })
459
457
 
460
- // Dynamic font size based on input length for origin amount (Pay with section)
461
- const originInputStyles = useDynamicInputStyles({
462
- inputValue: prepareSendQuote?.originAmountFormatted || "",
463
- })
464
-
465
458
  const handleOriginTokenSelect = useCallback(
466
459
  (token: any) => {
467
460
  const formattedToken = {
@@ -705,16 +698,12 @@ export const Pay: React.FC<PayProps> = ({
705
698
  {/* Amount Display - Non-editable */}
706
699
  <div className="flex-1">
707
700
  <div className="flex items-center space-x-2">
708
- <input
701
+ <DynamicSizeInputField
709
702
  ref={paymentRequestInputRef}
710
- type="text"
711
703
  value={prepareSendQuote?.originAmountFormatted || ""}
712
- placeholder={"0"}
713
704
  readOnly={true}
714
- className={`w-full bg-transparent font-bold trails-text-primary border-none outline-none ${
715
- isLoadingQuote ? "animate-pulse" : ""
716
- }`}
717
- style={originInputStyles}
705
+ isLoading={isLoadingQuote}
706
+ variant="default"
718
707
  />
719
708
  {isLoadingQuote && (
720
709
  <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
@@ -951,6 +940,7 @@ export const Pay: React.FC<PayProps> = ({
951
940
  onChange={(e) => handleAmountChange(e.target.value)}
952
941
  placeholder={`0 ${selectedDestToken?.symbol || ""}`}
953
942
  readOnly={!!toAmount}
943
+ autoComplete="off"
954
944
  className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
955
945
  isLoadingQuote ? "animate-pulse" : ""
956
946
  }`}
@@ -1034,12 +1024,17 @@ export const Pay: React.FC<PayProps> = ({
1034
1024
  selectedFeeToken={selectedFeeToken}
1035
1025
  setSelectedFeeToken={(token) => setSelectedFeeToken(token as any)}
1036
1026
  chainId={originToken?.chainId}
1027
+ isRefetching={isLoadingQuote}
1037
1028
  />
1038
1029
 
1039
1030
  {/* Quote Details */}
1040
1031
  {prepareSendQuote && (
1041
1032
  <div className="mb-4">
1042
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
1033
+ <QuoteDetails
1034
+ quote={prepareSendQuote}
1035
+ showContent={true}
1036
+ isRefetching={isLoadingQuote}
1037
+ />
1043
1038
  </div>
1044
1039
  )}
1045
1040
 
@@ -13,7 +13,6 @@ import type { TransactionState } from "../../transactions.js"
13
13
  import type { OnCompleteProps } from "../hooks/useSendForm.js"
14
14
  import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
15
15
  import { useSendForm } from "../hooks/useSendForm.js"
16
- import { TradeType } from "../../prepareSend.js"
17
16
  import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
18
17
  import { useEarnPool } from "../hooks/useEarnPool.js"
19
18
  import { useTokenList } from "../hooks/useTokenList.js"
@@ -32,10 +31,11 @@ import { useAmountUsd } from "../hooks/useAmountUsd.js"
32
31
  import { getExplorerUrlForAddress } from "../../explorer.js"
33
32
  import aaveLogo from "../assets/aave.svg"
34
33
  import morphoLogo from "../assets/morpho.svg"
35
- import type { PrepareSendQuote } from "../../prepareSend.js"
34
+ import { TradeType, type PrepareSendQuote } from "../../prepareSend.js"
36
35
  import type { SupportedToken } from "../../tokens.js"
37
36
  import { logger } from "../../logger.js"
38
- import { useDynamicInputStyles } from "./DynamicInputStyles.js"
37
+ import { DynamicSizeInputField } from "./DynamicSizeInputField.js"
38
+ import { FeeOptions } from "./FeeOptions.js"
39
39
 
40
40
  interface PoolDepositProps {
41
41
  account?: Account
@@ -47,7 +47,6 @@ interface PoolDepositProps {
47
47
  onComplete: (result: OnCompleteProps) => void
48
48
  onSend: (amount: string, recipient: string) => void
49
49
  paymasterUrls?: Array<{ chainId: number; url: string }>
50
- gasless?: boolean
51
50
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
52
51
  quoteProvider?: string
53
52
  fundMethod?: string
@@ -77,7 +76,6 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
77
76
  onComplete,
78
77
  onSend,
79
78
  paymasterUrls,
80
- gasless,
81
79
  setWalletConfirmRetryHandler,
82
80
  quoteProvider,
83
81
  fundMethod,
@@ -118,6 +116,9 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
118
116
  isSubmitting,
119
117
  buttonText,
120
118
  isValidRecipient,
119
+ feeOptions,
120
+ selectedFeeToken,
121
+ setSelectedFeeToken,
121
122
  } = useSendForm({
122
123
  account,
123
124
  toAmount: undefined,
@@ -130,10 +131,9 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
130
131
  onError,
131
132
  onWaitingForWalletConfirm,
132
133
  paymasterUrls,
133
- gasless,
134
+ onSend,
134
135
  onConfirm,
135
136
  onComplete,
136
- onSend,
137
137
  selectedToken: originToken as any,
138
138
  setWalletConfirmRetryHandler,
139
139
  tradeType: TradeType.EXACT_INPUT,
@@ -183,20 +183,22 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
183
183
  }
184
184
  }, [])
185
185
 
186
- // Notify parent component about pool selector visibility
186
+ // Notify parent component about pool selector and token selector visibility
187
187
  useEffect(() => {
188
188
  if (onPoolSelectorStateChange) {
189
- // Only hide tabs when showing EarnPools selector
190
- onPoolSelectorStateChange(showEarnPools)
189
+ // Hide tabs when showing EarnPools selector, token selector, or chain list
190
+ onPoolSelectorStateChange(
191
+ showEarnPools || showOriginTokenSelector || showOriginChainList,
192
+ )
191
193
  }
192
- }, [showEarnPools, onPoolSelectorStateChange])
194
+ }, [
195
+ showEarnPools,
196
+ showOriginTokenSelector,
197
+ showOriginChainList,
198
+ onPoolSelectorStateChange,
199
+ ])
193
200
 
194
201
  const handleAmountChange = (value: string) => {
195
- // Validate decimal places (max 8 decimals)
196
- const decimalMatch = value.match(/^\d*\.?\d{0,8}$/)
197
- if (!decimalMatch && value !== "") {
198
- return // Don't update if more than 8 decimals
199
- }
200
202
  setAmount(value)
201
203
  setSendFormAmount(value) // Sync with useSendForm for quote functionality
202
204
  }
@@ -231,12 +233,6 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
231
233
  setSendFormAmount(selectedAmount)
232
234
  }
233
235
 
234
- // Dynamic font size based on input length
235
- const inputStyles = useDynamicInputStyles({
236
- inputValue: amount,
237
- variant: "default",
238
- })
239
-
240
236
  if (showOriginChainList) {
241
237
  return (
242
238
  <ChainList
@@ -295,18 +291,12 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
295
291
  {/* Amount Input */}
296
292
  <div className="flex-1">
297
293
  <div className="flex items-center space-x-2">
298
- <input
294
+ <DynamicSizeInputField
299
295
  ref={inputRef}
300
- id="amount"
301
- type="text"
302
296
  value={amount}
303
297
  onChange={(e) => handleAmountChange(e.target.value)}
304
- placeholder={"0"}
305
- className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
306
- isLoadingQuote ? "animate-pulse" : ""
307
- }`}
308
- style={inputStyles}
309
- inputMode="decimal"
298
+ isLoading={isLoadingQuote}
299
+ variant="default"
310
300
  />
311
301
  {isLoadingQuote && (
312
302
  <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
@@ -528,10 +518,23 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
528
518
  </div>
529
519
  ) : null}
530
520
 
521
+ {/* Fee Options */}
522
+ <FeeOptions
523
+ feeOptions={feeOptions || []}
524
+ selectedFeeToken={selectedFeeToken}
525
+ setSelectedFeeToken={(token) => setSelectedFeeToken(token as any)}
526
+ chainId={originToken?.chainId}
527
+ isRefetching={isLoadingQuote}
528
+ />
529
+
531
530
  {/* Quote Details */}
532
531
  {prepareSendQuote && (
533
532
  <div className="mb-4">
534
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
533
+ <QuoteDetails
534
+ quote={prepareSendQuote}
535
+ showContent={true}
536
+ isRefetching={isLoadingQuote}
537
+ />
535
538
  </div>
536
539
  )}
537
540