0xtrails 0.12.0 → 0.12.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 (205) hide show
  1. package/dist/analytics.d.ts +65 -50
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-DtfgR432.js → ccip-62W6LwH2.js} +28 -28
  4. package/dist/chains.d.ts.map +1 -1
  5. package/dist/error.d.ts +2 -0
  6. package/dist/error.d.ts.map +1 -1
  7. package/dist/estimate.d.ts.map +1 -1
  8. package/dist/fees.d.ts.map +1 -1
  9. package/dist/{index-CHiCSmCD.js → index-C0QTNYIA.js} +43750 -41806
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +199 -171
  13. package/dist/localeUtils.d.ts.map +1 -1
  14. package/dist/meld/components/MeldCountriesList.d.ts +0 -2
  15. package/dist/meld/components/MeldCountriesList.d.ts.map +1 -1
  16. package/dist/meld/components/MeldFundMethods.d.ts.map +1 -1
  17. package/dist/meld/components/MeldTokensList.d.ts.map +1 -1
  18. package/dist/meld/utils/meld.d.ts +2 -52
  19. package/dist/meld/utils/meld.d.ts.map +1 -1
  20. package/dist/poolUtils.d.ts.map +1 -1
  21. package/dist/prepareSend.d.ts.map +1 -1
  22. package/dist/prices.d.ts +1 -2
  23. package/dist/prices.d.ts.map +1 -1
  24. package/dist/query/balance.fetchers.d.ts +2 -2
  25. package/dist/query/balance.fetchers.d.ts.map +1 -1
  26. package/dist/query/fiat.fetchers.d.ts +11 -0
  27. package/dist/query/fiat.fetchers.d.ts.map +1 -0
  28. package/dist/query/fiat.hooks.d.ts +18 -0
  29. package/dist/query/fiat.hooks.d.ts.map +1 -0
  30. package/dist/query/fiat.queries.d.ts +24 -0
  31. package/dist/query/fiat.queries.d.ts.map +1 -0
  32. package/dist/query/meld.fetchers.d.ts +19 -0
  33. package/dist/query/meld.fetchers.d.ts.map +1 -0
  34. package/dist/query/meld.hooks.d.ts +4 -0
  35. package/dist/query/meld.hooks.d.ts.map +1 -0
  36. package/dist/query/meld.queries.d.ts +61 -0
  37. package/dist/query/meld.queries.d.ts.map +1 -0
  38. package/dist/recover.d.ts.map +1 -1
  39. package/dist/tokens.d.ts.map +1 -1
  40. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  41. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  42. package/dist/transactionIntent/deposits/standardDeposit.d.ts +7 -1
  43. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  44. package/dist/transactionIntent/handlers/intentHandler.d.ts +2 -0
  45. package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
  46. package/dist/transactionIntent/quote/normalizeQuote.d.ts +2 -2
  47. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  48. package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
  49. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  50. package/dist/transactionIntent/types.d.ts +2 -0
  51. package/dist/transactionIntent/types.d.ts.map +1 -1
  52. package/dist/transactionIntent/utils/balanceChecker.d.ts +3 -1
  53. package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -1
  54. package/dist/transactions.d.ts +2 -9
  55. package/dist/transactions.d.ts.map +1 -1
  56. package/dist/umd/trails.min.js +206 -152
  57. package/dist/utils/fiat.d.ts +8 -0
  58. package/dist/utils/fiat.d.ts.map +1 -0
  59. package/dist/utils/format.d.ts.map +1 -1
  60. package/dist/utils/passthrough.d.ts +5 -2
  61. package/dist/utils/passthrough.d.ts.map +1 -1
  62. package/dist/utils/validation.d.ts +33 -0
  63. package/dist/utils/validation.d.ts.map +1 -1
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/walletUtils.d.ts +1 -1
  66. package/dist/walletUtils.d.ts.map +1 -1
  67. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  68. package/dist/widget/components/DepositTracker.d.ts.map +1 -1
  69. package/dist/widget/components/Earn.d.ts +2 -0
  70. package/dist/widget/components/Earn.d.ts.map +1 -1
  71. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  72. package/dist/widget/components/Fund.d.ts.map +1 -1
  73. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  74. package/dist/widget/components/HookModalContent.d.ts.map +1 -1
  75. package/dist/widget/components/MeldForm.d.ts.map +1 -1
  76. package/dist/widget/components/MeldHistory.d.ts.map +1 -1
  77. package/dist/widget/components/MeldStepsFlow.d.ts.map +1 -1
  78. package/dist/widget/components/OFTProgressBar.d.ts +2 -0
  79. package/dist/widget/components/OFTProgressBar.d.ts.map +1 -1
  80. package/dist/widget/components/OnRampProviderSelector.d.ts.map +1 -1
  81. package/dist/widget/components/OnrampHistoryRow.d.ts.map +1 -1
  82. package/dist/widget/components/Pay.d.ts.map +1 -1
  83. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -1
  84. package/dist/widget/components/PoolDeposit.d.ts +2 -0
  85. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  86. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  87. package/dist/widget/components/Receipt.d.ts.map +1 -1
  88. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  89. package/dist/widget/components/Swap.d.ts +2 -0
  90. package/dist/widget/components/Swap.d.ts.map +1 -1
  91. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  92. package/dist/widget/components/Withdraw.d.ts.map +1 -1
  93. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  94. package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -1
  95. package/dist/widget/hooks/useDisplayCurrencyPreference.d.ts.map +1 -1
  96. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts +3 -21
  97. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts.map +1 -1
  98. package/dist/widget/hooks/useMeldTransactionHistory.d.ts.map +1 -1
  99. package/dist/widget/hooks/useOnRampCountryDefaults.d.ts +0 -18
  100. package/dist/widget/hooks/useOnRampCountryDefaults.d.ts.map +1 -1
  101. package/dist/widget/hooks/useOnRampPaymentMethods.d.ts +2 -18
  102. package/dist/widget/hooks/useOnRampPaymentMethods.d.ts.map +1 -1
  103. package/dist/widget/hooks/useOnRampQuote.d.ts.map +1 -1
  104. package/dist/widget/hooks/useQuote.d.ts +5 -1
  105. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  106. package/dist/widget/hooks/useSendForm.d.ts +3 -1
  107. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  108. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  109. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts +3 -2
  110. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts.map +1 -1
  111. package/dist/widget/index.js +1 -1
  112. package/dist/widget/types/commonProps.d.ts +2 -0
  113. package/dist/widget/types/commonProps.d.ts.map +1 -1
  114. package/dist/widget/utils/transactionFailure.d.ts +20 -0
  115. package/dist/widget/utils/transactionFailure.d.ts.map +1 -0
  116. package/dist/widget/widget.d.ts +44 -3
  117. package/dist/widget/widget.d.ts.map +1 -1
  118. package/dist/widget/workers/intentExecutionWorker.d.ts.map +1 -1
  119. package/package.json +22 -22
  120. package/src/analytics.ts +115 -79
  121. package/src/chains.ts +0 -1
  122. package/src/error.ts +11 -0
  123. package/src/estimate.ts +12 -7
  124. package/src/fees.ts +0 -1
  125. package/src/index.ts +11 -0
  126. package/src/localeUtils.ts +3 -1
  127. package/src/meld/components/MeldCountriesList.tsx +30 -15
  128. package/src/meld/components/MeldFundMethods.tsx +8 -4
  129. package/src/meld/components/MeldTokensList.tsx +90 -2
  130. package/src/meld/utils/meld.ts +3 -400
  131. package/src/poolUtils.ts +5 -19
  132. package/src/prepareSend.ts +32 -5
  133. package/src/prices.ts +7 -33
  134. package/src/query/balance.fetchers.ts +128 -168
  135. package/src/query/fiat.fetchers.ts +33 -0
  136. package/src/query/fiat.hooks.ts +71 -0
  137. package/src/query/fiat.queries.ts +67 -0
  138. package/src/query/meld.fetchers.ts +97 -0
  139. package/src/query/meld.hooks.ts +18 -0
  140. package/src/query/meld.queries.ts +184 -0
  141. package/src/recover.ts +6 -1
  142. package/src/tokens.ts +31 -6
  143. package/src/transactionIntent/deposits/depositOrchestrator.ts +2 -0
  144. package/src/transactionIntent/deposits/gaslessDeposit.ts +9 -2
  145. package/src/transactionIntent/deposits/standardDeposit.ts +35 -14
  146. package/src/transactionIntent/handlers/intentHandler.ts +134 -138
  147. package/src/transactionIntent/quote/normalizeQuote.ts +31 -22
  148. package/src/transactionIntent/quote/quoteHelpers.ts +24 -7
  149. package/src/transactionIntent/types.ts +2 -0
  150. package/src/transactionIntent/utils/balanceChecker.ts +10 -4
  151. package/src/transactions.ts +22 -13
  152. package/src/umd.tsx +1 -1
  153. package/src/utils/fiat.ts +32 -0
  154. package/src/utils/format.ts +1 -3
  155. package/src/utils/passthrough.ts +19 -3
  156. package/src/utils/validation.ts +88 -0
  157. package/src/utils.ts +2 -1
  158. package/src/walletUtils.ts +2 -2
  159. package/src/widget/components/AccountIntentTransactionHistory.tsx +2 -2
  160. package/src/widget/components/ClassicSwap.tsx +10 -4
  161. package/src/widget/components/DepositTracker.tsx +2 -5
  162. package/src/widget/components/Earn.tsx +6 -0
  163. package/src/widget/components/FeeOption.tsx +15 -8
  164. package/src/widget/components/Fund.tsx +16 -11
  165. package/src/widget/components/FundMethods.tsx +255 -192
  166. package/src/widget/components/HookModalContent.tsx +4 -0
  167. package/src/widget/components/MeldForm.tsx +44 -42
  168. package/src/widget/components/MeldHistory.tsx +4 -3
  169. package/src/widget/components/MeldStepsFlow.tsx +33 -71
  170. package/src/widget/components/OFTProgressBar.tsx +32 -12
  171. package/src/widget/components/OnRampProviderSelector.tsx +2 -1
  172. package/src/widget/components/OnrampHistoryRow.tsx +2 -1
  173. package/src/widget/components/Pay.tsx +8 -2
  174. package/src/widget/components/PercentageMaxButtons.tsx +5 -3
  175. package/src/widget/components/PoolDeposit.tsx +6 -0
  176. package/src/widget/components/PoolWithdraw.tsx +1 -1
  177. package/src/widget/components/QuoteDetails.tsx +5 -4
  178. package/src/widget/components/Receipt.tsx +4 -3
  179. package/src/widget/components/SlippageToleranceSettings.tsx +3 -2
  180. package/src/widget/components/Swap.tsx +2 -0
  181. package/src/widget/components/TransferPendingVertical.tsx +21 -28
  182. package/src/widget/components/UserPreferences.tsx +1 -1
  183. package/src/widget/components/Withdraw.tsx +20 -14
  184. package/src/widget/hooks/useAmountUsd.ts +3 -15
  185. package/src/widget/hooks/useCustomTokenSearch.tsx +2 -6
  186. package/src/widget/hooks/useDisplayCurrencyPreference.tsx +1 -2
  187. package/src/widget/hooks/useFiatOnRampCurrencies.ts +11 -76
  188. package/src/widget/hooks/useMeldTransactionHistory.ts +24 -89
  189. package/src/widget/hooks/useOnRampCountryDefaults.ts +3 -49
  190. package/src/widget/hooks/useOnRampPaymentMethods.ts +21 -100
  191. package/src/widget/hooks/useOnRampQuote.ts +2 -5
  192. package/src/widget/hooks/useQuote.ts +10 -12
  193. package/src/widget/hooks/useSendForm.ts +6 -0
  194. package/src/widget/hooks/useTokenList.ts +3 -6
  195. package/src/widget/hooks/useTokenWithFreshBalance.ts +141 -11
  196. package/src/widget/types/commonProps.ts +2 -0
  197. package/src/widget/utils/transactionFailure.ts +52 -0
  198. package/src/widget/widget.tsx +137 -59
  199. package/src/widget/workers/intentExecutionWorker.ts +3 -1
  200. package/dist/widget/hooks/useExchangeRate.d.ts +0 -31
  201. package/dist/widget/hooks/useExchangeRate.d.ts.map +0 -1
  202. package/dist/widget/hooks/useFiatCurrencyList.d.ts +0 -3
  203. package/dist/widget/hooks/useFiatCurrencyList.d.ts.map +0 -1
  204. package/src/widget/hooks/useExchangeRate.ts +0 -257
  205. package/src/widget/hooks/useFiatCurrencyList.ts +0 -66
@@ -1,5 +1,6 @@
1
1
  import type React from "react"
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react"
3
+ import { useQuery } from "@tanstack/react-query"
3
4
  import { zeroAddress } from "viem"
4
5
  import { useCountryList } from "../../query/geo.hooks.js"
5
6
  import { getCountryFlag, getCountryName } from "../utils/countryUtils.js"
@@ -17,10 +18,8 @@ import { useAccount } from "wagmi"
17
18
  import { getFullErrorMessage } from "../../error.js"
18
19
  import type { MeldPaymentMethod } from "../../meld/components/MeldFundMethods.js"
19
20
  import type { Country, MeldQuote, MeldToken } from "../../meld/utils/meld.js"
20
- import {
21
- getMeldOnrampClient,
22
- useMeldPaymentMethods,
23
- } from "../../meld/utils/meld.js"
21
+ import { getMeldOnrampClient } from "../../meld/utils/meld.js"
22
+ import { meldQueries } from "../../query/meld.queries.js"
24
23
  import type { TransactionStatusInfo } from "../hooks/useMeldTransactionStatus.js"
25
24
  import { useOnRampCountryDefaults } from "../hooks/useOnRampCountryDefaults.js"
26
25
  import { useTrails } from "../providers/TrailsProvider.js"
@@ -54,11 +53,11 @@ export interface MeldFormProps {
54
53
 
55
54
  // Common countries for Meld
56
55
  const COMMON_COUNTRIES: Country[] = [
57
- { code: "US", name: "United States" },
58
- { code: "CA", name: "Canada" },
59
- { code: "GB", name: "United Kingdom" },
60
- { code: "EU", name: "European Union" },
61
- { code: "AU", name: "Australia" },
56
+ { countryCode: "US", name: "United States" },
57
+ { countryCode: "CA", name: "Canada" },
58
+ { countryCode: "GB", name: "United Kingdom" },
59
+ { countryCode: "EU", name: "European Union" },
60
+ { countryCode: "AU", name: "Australia" },
62
61
  ]
63
62
 
64
63
  // Simplified payment methods
@@ -113,6 +112,10 @@ const COMMON_TOKENS: MeldToken[] = [
113
112
  // Amount presets
114
113
  const AMOUNT_PRESETS = [10, 25, 50, 100]
115
114
 
115
+ // TODO: Temporary localStorage for redirect/session recovery. Should this be sessionStorage?
116
+ const MELD_SESSION_ID_STORAGE_KEY = "meld-current-session-id"
117
+ const MELD_EXTERNAL_SESSION_ID_STORAGE_KEY = "meld-current-external-session-id"
118
+
116
119
  // Helper function to map Meld chain names to chain IDs
117
120
  function getChainId(chainName?: string): number | undefined {
118
121
  const chainMap: Record<string, number> = {
@@ -161,19 +164,19 @@ export const MeldForm: React.FC<MeldFormProps> = ({
161
164
  if (detectedCountryCode) {
162
165
  // First try to find in common countries list
163
166
  const foundCountry = COMMON_COUNTRIES.find(
164
- (c) => c.code === detectedCountryCode,
167
+ (c) => c.countryCode === detectedCountryCode,
165
168
  )
166
169
  if (foundCountry) return foundCountry
167
170
 
168
171
  // If not found, create a country object for it using API data
169
172
  return {
170
- code: detectedCountryCode,
173
+ countryCode: detectedCountryCode,
171
174
  name: getCountryName(detectedCountryCode, countries),
172
175
  }
173
176
  }
174
177
 
175
178
  // Final fallback
176
- return COMMON_COUNTRIES[0] || { code: "US", name: "United States" }
179
+ return COMMON_COUNTRIES[0] || { countryCode: "US", name: "United States" }
177
180
  }, [propSelectedCountry, detectedCountryCode, countries])
178
181
  const selectedPaymentMethod = propSelectedPaymentMethod || "CREDIT_DEBIT_CARD"
179
182
 
@@ -190,7 +193,9 @@ export const MeldForm: React.FC<MeldFormProps> = ({
190
193
  }, [trailsApiKey, trailsConfig?.trailsApiUrl])
191
194
 
192
195
  // Fetch payment methods to get details including images
193
- const { paymentMethods } = useMeldPaymentMethods(trailsClient, currency)
196
+ const { data: paymentMethods = [] } = useQuery(
197
+ meldQueries.paymentMethodList(trailsClient, currency),
198
+ )
194
199
 
195
200
  // Find the selected payment method details
196
201
  const selectedPaymentMethodDetails = useMemo(() => {
@@ -242,34 +247,31 @@ export const MeldForm: React.FC<MeldFormProps> = ({
242
247
  }, [onTransactionStatus])
243
248
 
244
249
  // Handle status changes from new component
245
- const handleTransactionStatusChange = useCallback(
246
- (statusInfo: TransactionStatusInfo) => {
247
- // Convert to legacy format for backwards compatibility
248
- const legacyStatus = {
249
- type:
250
- statusInfo.state === "success"
251
- ? ("success" as const)
252
- : statusInfo.state === "failed" || statusInfo.state === "error"
253
- ? ("error" as const)
254
- : statusInfo.state === "polling"
255
- ? ("pending" as const)
256
- : null,
257
- message: statusInfo.message,
258
- }
250
+ const handleTransactionStatusChange = (statusInfo: TransactionStatusInfo) => {
251
+ // Convert to legacy format for backwards compatibility
252
+ const legacyStatus = {
253
+ type:
254
+ statusInfo.state === "success"
255
+ ? ("success" as const)
256
+ : statusInfo.state === "failed" || statusInfo.state === "error"
257
+ ? ("error" as const)
258
+ : statusInfo.state === "polling"
259
+ ? ("pending" as const)
260
+ : null,
261
+ message: statusInfo.message,
262
+ }
259
263
 
260
- // Call the latest callback using ref to prevent infinite loop
261
- onTransactionStatusRef.current?.(legacyStatus)
262
- },
263
- [], // No dependencies needed since we use ref
264
- )
264
+ // Call the latest callback using ref to prevent infinite loop
265
+ onTransactionStatusRef.current?.(legacyStatus)
266
+ }
265
267
 
266
268
  // Handle popup window closed
267
- const handleWindowClosed = useCallback(() => {
269
+ const handleWindowClosed = () => {
268
270
  logger.console.log("[MeldForm] Popup window was closed")
269
271
  setPopupWindowRef(null)
270
272
  setHasClickedContinue(false) // Reset button state when window closes
271
273
  setIsPopupOpen(false) // Reset popup state
272
- }, [])
274
+ }
273
275
 
274
276
  // Check for transaction status from URL parameters (in case of redirect)
275
277
  useEffect(() => {
@@ -286,7 +288,7 @@ export const MeldForm: React.FC<MeldFormProps> = ({
286
288
  )
287
289
  setCurrentTransactionId(urlSessionId)
288
290
  setCurrentSessionId(urlSessionId)
289
- localStorage.setItem("meld-current-session-id", urlSessionId)
291
+ localStorage.setItem(MELD_SESSION_ID_STORAGE_KEY, urlSessionId)
290
292
  }
291
293
  }, [currentTransactionId])
292
294
 
@@ -306,7 +308,7 @@ export const MeldForm: React.FC<MeldFormProps> = ({
306
308
 
307
309
  logger.console.log("[MeldForm] Loading providers with params:", {
308
310
  walletAddress: walletAddress || "fallback",
309
- countryCode: selectedCountry.code,
311
+ countryCode: selectedCountry.countryCode,
310
312
  sourceCurrencyCode: currency,
311
313
  sourceAmount: amount,
312
314
  destinationCurrencyCode: selectedToken.code,
@@ -322,7 +324,7 @@ export const MeldForm: React.FC<MeldFormProps> = ({
322
324
  const quoteResponse = await trailsClient.getMeldQuote({
323
325
  walletAddress:
324
326
  walletAddress || "0x1234567890123456789012345678901234567890",
325
- countryCode: selectedCountry.code,
327
+ countryCode: selectedCountry.countryCode,
326
328
  sourceCurrencyCode: currency,
327
329
  sourceAmount: amount,
328
330
  destinationCurrencyCode: selectedToken.code,
@@ -394,7 +396,7 @@ export const MeldForm: React.FC<MeldFormProps> = ({
394
396
  const sessionResponse = await trailsClient.createMeldWidgetSession({
395
397
  sessionData: {
396
398
  walletAddress: walletAddress,
397
- countryCode: selectedCountry.code,
399
+ countryCode: selectedCountry.countryCode,
398
400
  sourceCurrencyCode: currency,
399
401
  sourceAmount: amount,
400
402
  destinationCurrencyCode: selectedToken.code,
@@ -437,9 +439,9 @@ export const MeldForm: React.FC<MeldFormProps> = ({
437
439
  setCurrentSessionId(sessionId)
438
440
  setCurrentExternalSessionId(responseExternalSessionId)
439
441
  setCurrentTransactionId(sessionId)
440
- localStorage.setItem("meld-current-session-id", sessionId)
442
+ localStorage.setItem(MELD_SESSION_ID_STORAGE_KEY, sessionId)
441
443
  localStorage.setItem(
442
- "meld-current-external-session-id",
444
+ MELD_EXTERNAL_SESSION_ID_STORAGE_KEY,
443
445
  responseExternalSessionId,
444
446
  )
445
447
  } catch (error) {
@@ -549,9 +551,9 @@ export const MeldForm: React.FC<MeldFormProps> = ({
549
551
  className="flex items-center gap-2 bg-transparent border border-gray-300 dark:border-gray-600 rounded-lg px-2 py-1 text-sm trails-text-primary hover:bg-gray-50 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors cursor-pointer"
550
552
  >
551
553
  <span className="text-base">
552
- {getCountryFlag(selectedCountry.code, countries)}
554
+ {getCountryFlag(selectedCountry.countryCode, countries)}
553
555
  </span>
554
- <span className="font-medium">{selectedCountry.code}</span>
556
+ <span className="font-medium">{selectedCountry.countryCode}</span>
555
557
  <ChevronDown className="w-4 h-4 text-gray-500" />
556
558
  </button>
557
559
  </div>
@@ -2,9 +2,10 @@ import { Building2, Check, Copy, CreditCard, RefreshCw } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useState } from "react"
4
4
  import { useAccount } from "wagmi"
5
+ import { isValidNumber } from "../../utils/validation.js"
5
6
  import { getChainName } from "../../chains.js"
6
7
  import { logger } from "../../logger.js"
7
- import { useMeldProviders } from "../../meld/utils/meld.js"
8
+ import { useMeldServiceProviders } from "../../query/meld.hooks.js"
8
9
  import type { MeldServiceProvider } from "../../onramp-client/trails-onramp.gen.js"
9
10
  import { SECOND_MS, formatRelativeDate } from "../../utils/time.js"
10
11
  import {
@@ -79,7 +80,7 @@ function formatAmount(amount?: string): string {
79
80
  if (!amount) return "-"
80
81
 
81
82
  const numAmount = parseFloat(amount)
82
- if (Number.isNaN(numAmount)) return amount
83
+ if (!isValidNumber(numAmount)) return amount
83
84
 
84
85
  // Format with appropriate decimal places
85
86
  // Use 'en-US' to ensure consistent decimal formatting (period as decimal separator)
@@ -361,7 +362,7 @@ export const MeldHistory: React.FC<MeldHistoryProps> = ({
361
362
  })
362
363
 
363
364
  // Fetch Meld providers once and cache for 24 hours (already built into useMeldProviders)
364
- const { providers: meldProviders } = useMeldProviders()
365
+ const { data: meldProviders = [] } = useMeldServiceProviders()
365
366
 
366
367
  if (!address) {
367
368
  return (
@@ -2,6 +2,7 @@ import { ExternalLink } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useCallback, useEffect, useMemo, useState } from "react"
4
4
  import { useAccount } from "wagmi"
5
+ import { isPositiveNumber } from "../../utils/validation.js"
5
6
  import { getChainName } from "../../chains.js"
6
7
  import { MeldCountriesList } from "../../meld/components/MeldCountriesList.js"
7
8
  import type { MeldPaymentMethod } from "../../meld/components/MeldFundMethods.js"
@@ -14,8 +15,6 @@ import { normalizeAddress, truncateAddress } from "../../utils/address.js"
14
15
  import { MINUTE_MS, SECOND_MS } from "../../utils/time.js"
15
16
  import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
16
17
  import type { MeldTransaction } from "../hooks/useOnRampTransactionStatus.js"
17
- import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
18
- import { useTokenList } from "../hooks/useTokenList.js"
19
18
  import { useTrails } from "../providers/TrailsProvider.js"
20
19
  import { MeldForm } from "./MeldForm.js"
21
20
  import { ScreenHeader } from "./ScreenHeader.js"
@@ -37,12 +36,33 @@ type Screen =
37
36
  const MELD_AMOUNT_STORAGE_KEY = "meld-form-amount"
38
37
  const MELD_CURRENCY_STORAGE_KEY = "meld-debug-currency"
39
38
 
39
+ function parseStoredCountry(raw: string | null): Country | null {
40
+ if (!raw) return null
41
+
42
+ try {
43
+ // TODO: Temporary legacy `{ code }` compatibility until storage migration is centralized.
44
+ const parsed = JSON.parse(raw) as Partial<Country> & { code?: string }
45
+ const countryCode =
46
+ typeof parsed.countryCode === "string"
47
+ ? parsed.countryCode
48
+ : typeof parsed.code === "string"
49
+ ? parsed.code
50
+ : null
51
+
52
+ if (!countryCode) return null
53
+
54
+ return {
55
+ countryCode,
56
+ name: typeof parsed.name === "string" ? parsed.name : countryCode,
57
+ }
58
+ } catch {
59
+ return null
60
+ }
61
+ }
62
+
40
63
  interface AmountInputScreenProps {
41
64
  onBack: (amount: string, currency: string) => void
42
65
  onContinue: (amount: string, currency: string) => void
43
- _selectedCountry: Country | null
44
- _selectedToken: MeldToken | null
45
- _selectedPaymentMethod: MeldPaymentMethod | null
46
66
  initialAmount?: string
47
67
  initialCurrency?: string
48
68
  }
@@ -50,9 +70,6 @@ interface AmountInputScreenProps {
50
70
  const AmountInputScreen: React.FC<AmountInputScreenProps> = ({
51
71
  onBack,
52
72
  onContinue,
53
- _selectedCountry,
54
- _selectedToken,
55
- _selectedPaymentMethod,
56
73
  initialAmount = "",
57
74
  initialCurrency = "USD",
58
75
  }) => {
@@ -61,10 +78,7 @@ const AmountInputScreen: React.FC<AmountInputScreenProps> = ({
61
78
 
62
79
  const numAmount = parseFloat(amount)
63
80
  const isValid =
64
- amount.trim() !== "" &&
65
- !Number.isNaN(numAmount) &&
66
- numAmount > 0 &&
67
- numAmount <= 10000
81
+ amount.trim() !== "" && isPositiveNumber(numAmount) && numAmount <= 10000
68
82
 
69
83
  // Local storage persistence
70
84
  useEffect(() => {
@@ -165,11 +179,6 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
165
179
  }) => {
166
180
  const trailsConfig = useTrails()
167
181
  const { setCurrentScreen: setGlobalScreen } = useCurrentScreen()
168
- const { setSelectedToken: setOriginToken } = useOriginSelectedToken()
169
- const { filteredTokens } = useTokenList({
170
- onContinue: () => {},
171
- onError: () => {},
172
- })
173
182
  const { address: walletAddress } = useAccount()
174
183
 
175
184
  // Create OnrampClient using trails API key from config
@@ -192,12 +201,7 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
192
201
 
193
202
  // Load from local storage on mount
194
203
  const [selectedCountry, setSelectedCountry] = useState<Country | null>(() => {
195
- try {
196
- const saved = localStorage.getItem("meld-debug-country")
197
- return saved ? JSON.parse(saved) : null
198
- } catch {
199
- return null
200
- }
204
+ return parseStoredCountry(localStorage.getItem("meld-debug-country"))
201
205
  })
202
206
 
203
207
  const [selectedPaymentMethod, setSelectedPaymentMethod] =
@@ -243,6 +247,8 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
243
247
  }>({ status: "", message: "", type: null })
244
248
 
245
249
  // Check for transaction status from URL parameters (when user returns from Meld)
250
+ // TODO: temporary ignore this Biome warning for now; do not refactor code in this pass.
251
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Preserve current dependency shape per requested no-code-change rollback.
246
252
  useEffect(() => {
247
253
  const urlParams = new URLSearchParams(window.location.search)
248
254
 
@@ -943,11 +949,8 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
943
949
  "[MeldStepsFlow] Stored external session ID in localStorage",
944
950
  )
945
951
 
946
- // Build redirect URL (current page URL without query params)
947
- const _redirectUrl = window.location.origin + window.location.pathname
948
-
949
952
  logger.console.log("[MeldStepsFlow] Creating widget session with:", {
950
- countryCode: selectedCountry.code,
953
+ countryCode: selectedCountry.countryCode,
951
954
  destinationCurrencyCode: selectedToken.code,
952
955
  serviceProvider: selectedProvider.serviceProvider,
953
956
  paymentMethodType: selectedPaymentMethod,
@@ -962,7 +965,7 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
962
965
  const sessionResponse = await trailsClient.createMeldWidgetSession({
963
966
  sessionData: {
964
967
  walletAddress: walletAddress,
965
- countryCode: selectedCountry.code,
968
+ countryCode: selectedCountry.countryCode,
966
969
  sourceCurrencyCode: selectedCurrency,
967
970
  sourceAmount: selectedAmount,
968
971
  destinationCurrencyCode: selectedToken.code,
@@ -1062,43 +1065,6 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
1062
1065
  trailsClient,
1063
1066
  ])
1064
1067
 
1065
- // Declare unused functions with underscore prefix to avoid linting warnings
1066
- const _handleOpenWidget = useCallback(async () => {
1067
- await generateWidgetUrl()
1068
- if (
1069
- widgetUrl &&
1070
- !widgetUrl.startsWith("Error") &&
1071
- !widgetUrl.startsWith("Please") &&
1072
- !widgetUrl.startsWith("No")
1073
- ) {
1074
- window.open(widgetUrl, "_blank")
1075
- }
1076
- }, [widgetUrl, generateWidgetUrl])
1077
-
1078
- const _handleContinue = useCallback(() => {
1079
- if (!selectedToken) {
1080
- return
1081
- }
1082
-
1083
- // Find the matching token in the widget's token list
1084
- // Meld token has symbol (e.g., "USDC") and chainId
1085
- // We need to match by symbol and chainId
1086
- const matchingToken = filteredTokens.find((token) => {
1087
- // Match by symbol and chainId
1088
- return (
1089
- token.symbol.toUpperCase() === selectedToken.symbol.toUpperCase() &&
1090
- token.chainId === selectedToken.chainId
1091
- )
1092
- })
1093
-
1094
- if (matchingToken) {
1095
- // Set as origin token
1096
- setOriginToken(matchingToken)
1097
- // Navigate to home screen
1098
- setGlobalScreen("home")
1099
- }
1100
- }, [selectedToken, filteredTokens, setOriginToken, setGlobalScreen])
1101
-
1102
1068
  // Poll transaction status when we have a pending transaction
1103
1069
  useEffect(() => {
1104
1070
  logger.console.log("[MeldStepsFlow] Polling useEffect triggered:", {
@@ -1184,7 +1150,7 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
1184
1150
  // First, get a quote to find the best service provider
1185
1151
  const quoteResponse = await trailsClient.getMeldQuote({
1186
1152
  walletAddress: "0x1234567890123456789012345678901234567890",
1187
- countryCode: selectedCountry.code,
1153
+ countryCode: selectedCountry.countryCode,
1188
1154
  sourceCurrencyCode: selectedCurrency,
1189
1155
  sourceAmount: selectedAmount,
1190
1156
  destinationCurrencyCode: selectedToken.code,
@@ -1334,7 +1300,6 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
1334
1300
  onBack={handleBackFromSubScreen}
1335
1301
  onSelectCountry={handleSelectCountry}
1336
1302
  selectedCountry={selectedCountry}
1337
- trailsClient={trailsClient}
1338
1303
  />
1339
1304
  )
1340
1305
  }
@@ -1361,7 +1326,7 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
1361
1326
  onSelectToken={handleSelectToken}
1362
1327
  selectedToken={selectedToken}
1363
1328
  trailsClient={trailsClient}
1364
- countryCode={selectedCountry?.code}
1329
+ countryCode={selectedCountry?.countryCode}
1365
1330
  />
1366
1331
  )
1367
1332
  }
@@ -1382,9 +1347,6 @@ export const MeldStepsFlow: React.FC<MeldStepsFlowProps> = ({
1382
1347
  setCurrentScreen("menu")
1383
1348
  }}
1384
1349
  onContinue={handleAmountContinue}
1385
- _selectedCountry={selectedCountry}
1386
- _selectedToken={selectedToken}
1387
- _selectedPaymentMethod={selectedPaymentMethod}
1388
1350
  initialAmount={selectedAmount}
1389
1351
  initialCurrency={selectedCurrency}
1390
1352
  />
@@ -1,12 +1,12 @@
1
1
  import type React from "react"
2
2
  import { useEffect, useState } from "react"
3
-
4
- // OFT progress bar duration: 20 minutes
5
- const OFT_PROGRESS_DURATION_MS = 20 * 60 * 1000
3
+ import { formatDuration } from "../../utils/time.js"
6
4
 
7
5
  interface OFTProgressBarProps {
8
6
  /** Timestamp when the deposit was confirmed */
9
7
  depositConfirmedAt: number
8
+ /** Estimated total duration in seconds */
9
+ estimatedDurationSeconds: number
10
10
  /** Whether to show with animation */
11
11
  showContent?: boolean
12
12
  }
@@ -17,21 +17,32 @@ interface OFTProgressBarProps {
17
17
  */
18
18
  export const OFTProgressBar: React.FC<OFTProgressBarProps> = ({
19
19
  depositConfirmedAt,
20
+ estimatedDurationSeconds,
20
21
  showContent = true,
21
22
  }) => {
23
+ const hasValidDuration =
24
+ Number.isFinite(estimatedDurationSeconds) && estimatedDurationSeconds > 0
25
+ const clampedDurationSeconds = hasValidDuration ? estimatedDurationSeconds : 0
26
+ const progressDurationMs = clampedDurationSeconds * 1000
27
+
22
28
  const [progress, setProgress] = useState(0)
23
- const [remainingMinutes, setRemainingMinutes] = useState(20)
29
+ const [remainingMinutes, setRemainingMinutes] = useState(
30
+ Math.ceil(clampedDurationSeconds / 60),
31
+ )
24
32
 
25
33
  useEffect(() => {
34
+ if (!hasValidDuration) {
35
+ setProgress(0)
36
+ setRemainingMinutes(0)
37
+ return
38
+ }
39
+
26
40
  const updateProgress = () => {
27
41
  const elapsed = Date.now() - depositConfirmedAt
28
- const newProgress = Math.min(
29
- (elapsed / OFT_PROGRESS_DURATION_MS) * 100,
30
- 100,
31
- )
42
+ const newProgress = Math.min((elapsed / progressDurationMs) * 100, 100)
32
43
  const remaining = Math.max(
33
44
  0,
34
- Math.ceil((OFT_PROGRESS_DURATION_MS - elapsed) / 60000),
45
+ Math.ceil((progressDurationMs - elapsed) / 60000),
35
46
  )
36
47
  setProgress(newProgress)
37
48
  setRemainingMinutes(remaining)
@@ -41,7 +52,11 @@ export const OFTProgressBar: React.FC<OFTProgressBarProps> = ({
41
52
 
42
53
  const interval = setInterval(updateProgress, 1000)
43
54
  return () => clearInterval(interval)
44
- }, [depositConfirmedAt])
55
+ }, [depositConfirmedAt, hasValidDuration, progressDurationMs])
56
+
57
+ if (!hasValidDuration) {
58
+ return null
59
+ }
45
60
 
46
61
  return (
47
62
  <div
@@ -51,7 +66,11 @@ export const OFTProgressBar: React.FC<OFTProgressBarProps> = ({
51
66
  >
52
67
  <div className="flex justify-between text-sm text-gray-600 dark:text-gray-400 mb-2">
53
68
  <span>Bridging via LayerZero</span>
54
- <span>{remainingMinutes} min remaining</span>
69
+ <span>
70
+ {remainingMinutes > 0
71
+ ? `${remainingMinutes} min remaining`
72
+ : "completing soon"}
73
+ </span>
55
74
  </div>
56
75
  <div className="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
57
76
  <div
@@ -60,7 +79,8 @@ export const OFTProgressBar: React.FC<OFTProgressBarProps> = ({
60
79
  />
61
80
  </div>
62
81
  <p className="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">
63
- LayerZero OFT transfers typically complete within 5-20 minutes
82
+ LayerZero OFT transfers typically complete in about{" "}
83
+ {formatDuration(clampedDurationSeconds)}
64
84
  </p>
65
85
  </div>
66
86
  )
@@ -2,6 +2,7 @@ import { ChevronDown } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useEffect, useRef, useState } from "react"
4
4
  import { formatUnits } from "viem"
5
+ import { isValidNumber } from "../../utils/validation.js"
5
6
  import type { MeldQuote } from "../../meld/utils/meld.js"
6
7
  import type { PrepareSendQuote } from "../../prepareSend.js"
7
8
 
@@ -106,7 +107,7 @@ export const OnRampProviderSelector: React.FC<OnRampProviderSelectorProps> = ({
106
107
 
107
108
  if (isUsdStablecoin && trailsQuote?.destinationAmountFormatted) {
108
109
  const amount = parseFloat(trailsQuote.destinationAmountFormatted)
109
- if (!Number.isNaN(amount)) {
110
+ if (isValidNumber(amount)) {
110
111
  return `$${amount.toFixed(2)}`
111
112
  }
112
113
  }
@@ -2,6 +2,7 @@ import { Building2, Check, Copy, CreditCard } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useState } from "react"
4
4
  import { getChainName } from "../../chains.js"
5
+ import { isValidNumber } from "../../utils/validation.js"
5
6
  import { logger } from "../../logger.js"
6
7
  import type { MeldServiceProvider } from "../../onramp-client/trails-onramp.gen.js"
7
8
  import { SECOND_MS, formatRelativeDate } from "../../utils/time.js"
@@ -61,7 +62,7 @@ function formatAmount(amount?: string): string {
61
62
  if (!amount) return "-"
62
63
 
63
64
  const numAmount = parseFloat(amount)
64
- if (Number.isNaN(numAmount)) return amount
65
+ if (!isValidNumber(numAmount)) return amount
65
66
 
66
67
  return numAmount.toLocaleString("en-US", {
67
68
  minimumFractionDigits: 2,
@@ -2,6 +2,7 @@ import { ChevronRight, Search } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react"
4
4
  import { getChainInfo } from "../../chains.js"
5
+ import { isValidNumber } from "../../utils/validation.js"
5
6
  import { logger } from "../../logger.js"
6
7
  import { TradeType } from "../../prepareSend.js"
7
8
  import { formatUsdAmountLocaleDisplay } from "../../utils/format.js"
@@ -61,6 +62,8 @@ export const Pay: React.FC<PayProps> = ({
61
62
  setWalletConfirmRetryHandler,
62
63
  swapProvider,
63
64
  bridgeProvider,
65
+ swapProviderFallback,
66
+ bridgeProviderFallback,
64
67
  fundMethod,
65
68
  checkoutOnHandlers,
66
69
  recentTokens,
@@ -223,6 +226,8 @@ export const Pay: React.FC<PayProps> = ({
223
226
  tradeType: TradeType.EXACT_OUTPUT, // Key difference: using EXACT_OUTPUT
224
227
  swapProvider,
225
228
  bridgeProvider,
229
+ swapProviderFallback,
230
+ bridgeProviderFallback,
226
231
  fundMethod,
227
232
  mode,
228
233
  checkoutOnHandlers,
@@ -761,7 +766,7 @@ export const Pay: React.FC<PayProps> = ({
761
766
  const balance = parseFloat(
762
767
  originTokenBalance.balanceFormatted,
763
768
  )
764
- if (!Number.isNaN(balance)) {
769
+ if (isValidNumber(balance)) {
765
770
  setTokenAmountForBackend(balance.toFixed(6))
766
771
  }
767
772
  }
@@ -773,7 +778,7 @@ export const Pay: React.FC<PayProps> = ({
773
778
  const balance = parseFloat(
774
779
  originTokenBalance.balanceFormatted,
775
780
  )
776
- if (!Number.isNaN(balance)) {
781
+ if (isValidNumber(balance)) {
777
782
  setTokenAmountForBackend(balance.toFixed(6))
778
783
  }
779
784
  }
@@ -1059,6 +1064,7 @@ export const Pay: React.FC<PayProps> = ({
1059
1064
  isPassthrough={isPassthroughEligible(
1060
1065
  prepareSendQuote.passthroughEligible,
1061
1066
  selectedFeeOption,
1067
+ processedFeeOptions,
1062
1068
  )}
1063
1069
  />
1064
1070
  </div>
@@ -5,6 +5,7 @@ import {
5
5
  getDefaultGasCostEstimate,
6
6
  } from "../../estimate.js"
7
7
  import { useClickTracking } from "../hooks/useClickTracking.js"
8
+ import { isValidNumber } from "../../utils/validation.js"
8
9
 
9
10
  interface PercentageMaxButtonsProps {
10
11
  userBalance: string | undefined
@@ -47,7 +48,7 @@ export const PercentageMaxButtons: React.FC<PercentageMaxButtonsProps> = ({
47
48
  })
48
49
 
49
50
  const balance = parseFloat(userBalance)
50
- if (Number.isNaN(balance)) return
51
+ if (!isValidNumber(balance)) return
51
52
 
52
53
  const amount = (balance * percentage) / 100
53
54
  onAmountSelect(amount.toFixed(6))
@@ -69,8 +70,9 @@ export const PercentageMaxButtons: React.FC<PercentageMaxButtonsProps> = ({
69
70
  // For native tokens, subtract gas cost
70
71
  // Priority: 1) Use actual gas cost from quote, 2) Fetch real gas price, 3) Fallback to 1% of balance
71
72
  const effectiveGasCost =
72
- gasCostFormatted ||
73
- (await getDefaultGasCostEstimate(userBalance, chainId))
73
+ gasCostFormatted && gasCostFormatted !== "0"
74
+ ? gasCostFormatted
75
+ : await getDefaultGasCostEstimate(userBalance, chainId)
74
76
  const maxAmount = calculateMaxNativeAmount(userBalance, effectiveGasCost)
75
77
  onAmountSelect(maxAmount)
76
78
  } else {
@@ -56,6 +56,8 @@ interface PoolDepositProps {
56
56
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
57
57
  swapProvider?: string
58
58
  bridgeProvider?: string
59
+ swapProviderFallback?: boolean
60
+ bridgeProviderFallback?: boolean
59
61
  fundMethod?: FundMethod
60
62
  checkoutOnHandlers?: CheckoutOnHandlers
61
63
  recentTokens?: Token[]
@@ -77,6 +79,8 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
77
79
  setWalletConfirmRetryHandler,
78
80
  swapProvider,
79
81
  bridgeProvider,
82
+ swapProviderFallback,
83
+ bridgeProviderFallback,
80
84
  fundMethod,
81
85
  checkoutOnHandlers,
82
86
  recentTokens,
@@ -207,6 +211,8 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
207
211
  tradeType: TradeType.EXACT_INPUT,
208
212
  swapProvider,
209
213
  bridgeProvider,
214
+ swapProviderFallback,
215
+ bridgeProviderFallback,
210
216
  fundMethod,
211
217
  mode,
212
218
  checkoutOnHandlers,
@@ -750,7 +750,7 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
750
750
  {isBalanceVisible
751
751
  ? isLoadingBalance
752
752
  ? "Loading..."
753
- : poolBalance || "..."
753
+ : poolBalance || "0.00"
754
754
  : "••••••"}
755
755
  </span>
756
756
  </button>