0xtrails 0.1.13 → 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 (256) hide show
  1. package/dist/aave.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +12 -2
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/apiClient.d.ts +1 -1
  5. package/dist/apiClient.d.ts.map +1 -1
  6. package/dist/{ccip-D3gTQONK.js → ccip-BbfANth7.js} +12 -12
  7. package/dist/cctp.d.ts.map +1 -1
  8. package/dist/cctpqueue.d.ts +3 -3
  9. package/dist/cctpqueue.d.ts.map +1 -1
  10. package/dist/chains.d.ts.map +1 -1
  11. package/dist/config.d.ts +18 -5
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/constants.d.ts +6 -5
  14. package/dist/constants.d.ts.map +1 -1
  15. package/dist/contractUtils.d.ts +2 -0
  16. package/dist/contractUtils.d.ts.map +1 -1
  17. package/dist/customChains.d.ts +24 -0
  18. package/dist/customChains.d.ts.map +1 -0
  19. package/dist/gasless.d.ts +19 -7
  20. package/dist/gasless.d.ts.map +1 -1
  21. package/dist/{index-CnUM7lKf.js → index-WpIVoh3X.js} +36741 -31761
  22. package/dist/index.d.ts +5 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +405 -394
  25. package/dist/indexerClient.d.ts +10 -0
  26. package/dist/indexerClient.d.ts.map +1 -1
  27. package/dist/intentEntrypoint.d.ts +122 -0
  28. package/dist/intentEntrypoint.d.ts.map +1 -0
  29. package/dist/intents.d.ts +5 -3
  30. package/dist/intents.d.ts.map +1 -1
  31. package/dist/metaTxnMonitor.d.ts.map +1 -1
  32. package/dist/morpho.d.ts.map +1 -1
  33. package/dist/pools.d.ts +3 -1
  34. package/dist/pools.d.ts.map +1 -1
  35. package/dist/prepareSend.d.ts +18 -9
  36. package/dist/prepareSend.d.ts.map +1 -1
  37. package/dist/prices.d.ts +1 -1
  38. package/dist/prices.d.ts.map +1 -1
  39. package/dist/relaySdk.d.ts.map +1 -1
  40. package/dist/relayer.d.ts.map +1 -1
  41. package/dist/toast.d.ts +9 -0
  42. package/dist/toast.d.ts.map +1 -0
  43. package/dist/tokenBalances.d.ts +6 -2
  44. package/dist/tokenBalances.d.ts.map +1 -1
  45. package/dist/tokens.d.ts.map +1 -1
  46. package/dist/trails.d.ts +6 -5
  47. package/dist/trails.d.ts.map +1 -1
  48. package/dist/trailsClient.d.ts +12 -0
  49. package/dist/trailsClient.d.ts.map +1 -0
  50. package/dist/trailsRouter.d.ts +22 -0
  51. package/dist/trailsRouter.d.ts.map +1 -0
  52. package/dist/transactions.d.ts +8 -1
  53. package/dist/transactions.d.ts.map +1 -1
  54. package/dist/wallets.d.ts.map +1 -1
  55. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  56. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  57. package/dist/widget/components/AccountSettings.d.ts +7 -0
  58. package/dist/widget/components/AccountSettings.d.ts.map +1 -0
  59. package/dist/widget/components/ChainList.d.ts +0 -1
  60. package/dist/widget/components/ChainList.d.ts.map +1 -1
  61. package/dist/widget/components/ClassicSwap.d.ts +46 -0
  62. package/dist/widget/components/ClassicSwap.d.ts.map +1 -0
  63. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  64. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  65. package/dist/widget/components/ConnectedWallets.d.ts +9 -0
  66. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -0
  67. package/dist/widget/components/DebugMenu.d.ts.map +1 -1
  68. package/dist/widget/components/DebugScreensList.d.ts.map +1 -1
  69. package/dist/widget/components/DebugToast.d.ts +3 -0
  70. package/dist/widget/components/DebugToast.d.ts.map +1 -0
  71. package/dist/widget/components/Earn.d.ts.map +1 -1
  72. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  73. package/dist/widget/components/FeeOption.d.ts +22 -0
  74. package/dist/widget/components/FeeOption.d.ts.map +1 -0
  75. package/dist/widget/components/FeeOptions.d.ts +13 -17
  76. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  77. package/dist/widget/components/Fund.d.ts +44 -0
  78. package/dist/widget/components/Fund.d.ts.map +1 -0
  79. package/dist/widget/components/FundMethods.d.ts +1 -1
  80. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  81. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  82. package/dist/widget/components/Identicon.d.ts +9 -0
  83. package/dist/widget/components/Identicon.d.ts.map +1 -0
  84. package/dist/widget/components/MeshConnectExchanges.d.ts +5 -2
  85. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  86. package/dist/widget/components/MeshConnectFlow.d.ts +2 -0
  87. package/dist/widget/components/MeshConnectFlow.d.ts.map +1 -1
  88. package/dist/widget/components/NativeGasOption.d.ts +12 -0
  89. package/dist/widget/components/NativeGasOption.d.ts.map +1 -0
  90. package/dist/widget/components/Pay.d.ts +46 -0
  91. package/dist/widget/components/Pay.d.ts.map +1 -0
  92. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  93. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  94. package/dist/widget/components/Receive.d.ts.map +1 -1
  95. package/dist/widget/components/RecentTokens.d.ts.map +1 -1
  96. package/dist/widget/components/Recipients.d.ts +9 -0
  97. package/dist/widget/components/Recipients.d.ts.map +1 -0
  98. package/dist/widget/components/RefundWarning.d.ts +9 -0
  99. package/dist/widget/components/RefundWarning.d.ts.map +1 -0
  100. package/dist/widget/components/SimpleSwap.d.ts.map +1 -1
  101. package/dist/widget/components/Swap.d.ts.map +1 -1
  102. package/dist/widget/components/SwapSettings.d.ts +1 -5
  103. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  104. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  105. package/dist/widget/components/ThemeSyncer.d.ts +6 -0
  106. package/dist/widget/components/ThemeSyncer.d.ts.map +1 -0
  107. package/dist/widget/components/Toast.d.ts +24 -0
  108. package/dist/widget/components/Toast.d.ts.map +1 -0
  109. package/dist/widget/components/TokenList.d.ts.map +1 -1
  110. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  111. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  112. package/dist/widget/components/TruncatedAddress.d.ts +2 -0
  113. package/dist/widget/components/TruncatedAddress.d.ts.map +1 -1
  114. package/dist/widget/components/UserPreferences.d.ts +7 -0
  115. package/dist/widget/components/UserPreferences.d.ts.map +1 -0
  116. package/dist/widget/hooks/useBack.d.ts +2 -0
  117. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  118. package/dist/widget/hooks/useBalanceVisible.d.ts +1 -0
  119. package/dist/widget/hooks/useBalanceVisible.d.ts.map +1 -1
  120. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  121. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  122. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  123. package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
  124. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  125. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +54 -0
  126. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -0
  127. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  128. package/dist/widget/hooks/usePayMessage.d.ts +34 -0
  129. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -0
  130. package/dist/widget/hooks/useRecipients.d.ts +17 -0
  131. package/dist/widget/hooks/useRecipients.d.ts.map +1 -0
  132. package/dist/widget/hooks/useSelectedFeeToken.d.ts +32 -0
  133. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -0
  134. package/dist/widget/hooks/useSelectedMeshExchange.d.ts +14 -0
  135. package/dist/widget/hooks/useSelectedMeshExchange.d.ts.map +1 -0
  136. package/dist/widget/hooks/useSelectedRecipient.d.ts +12 -0
  137. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -0
  138. package/dist/widget/hooks/useSendForm.d.ts +10 -13
  139. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  140. package/dist/widget/hooks/useSwapAmount.d.ts +13 -0
  141. package/dist/widget/hooks/useSwapAmount.d.ts.map +1 -0
  142. package/dist/widget/hooks/useSwapSettings.d.ts +16 -0
  143. package/dist/widget/hooks/useSwapSettings.d.ts.map +1 -0
  144. package/dist/widget/hooks/useTargetAmount.d.ts +5 -0
  145. package/dist/widget/hooks/useTargetAmount.d.ts.map +1 -0
  146. package/dist/widget/hooks/useTheme.d.ts +14 -0
  147. package/dist/widget/hooks/useTheme.d.ts.map +1 -0
  148. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  149. package/dist/widget/index.js +2 -2
  150. package/dist/widget/widget.d.ts +9 -0
  151. package/dist/widget/widget.d.ts.map +1 -1
  152. package/package.json +38 -36
  153. package/src/aave.ts +6 -1
  154. package/src/analytics.ts +109 -53
  155. package/src/apiClient.ts +1 -1
  156. package/src/cctp.ts +6 -2
  157. package/src/cctpqueue.ts +7 -7
  158. package/src/chains.ts +18 -0
  159. package/src/config.ts +63 -17
  160. package/src/constants.ts +20 -16
  161. package/src/contractUtils.ts +33 -2
  162. package/src/customChains.ts +24 -0
  163. package/src/gasless.ts +162 -109
  164. package/src/index.ts +11 -1
  165. package/src/indexerClient.ts +73 -1
  166. package/src/intentEntrypoint.ts +218 -0
  167. package/src/intents.ts +85 -54
  168. package/src/metaTxnMonitor.ts +1 -0
  169. package/src/morpho.ts +13 -2
  170. package/src/pools.ts +68 -86
  171. package/src/prepareSend.ts +1719 -967
  172. package/src/prices.ts +51 -7
  173. package/src/relaySdk.ts +6 -4
  174. package/src/relayer.ts +6 -3
  175. package/src/toast.ts +110 -0
  176. package/src/tokenBalances.ts +112 -20
  177. package/src/tokens.ts +70 -7
  178. package/src/trails.ts +81 -80
  179. package/src/trailsClient.ts +48 -0
  180. package/src/{proxyCaller.ts → trailsRouter.ts} +25 -20
  181. package/src/transactions.ts +30 -88
  182. package/src/umd.tsx +1 -1
  183. package/src/wallets.ts +2 -1
  184. package/src/widget/assets/sequence-logo.svg +15 -0
  185. package/src/widget/compiled.css +2 -2
  186. package/src/widget/components/AccountActionsDropdown.tsx +18 -159
  187. package/src/widget/components/AccountIntentTransactionHistory.tsx +346 -63
  188. package/src/widget/components/AccountSettings.tsx +102 -0
  189. package/src/widget/components/ChainFilterDropdown.tsx +1 -1
  190. package/src/widget/components/ChainList.tsx +10 -20
  191. package/src/widget/components/ClassicSwap.tsx +921 -0
  192. package/src/widget/components/ConfigDisplay.tsx +41 -5
  193. package/src/widget/components/ConnectWallet.tsx +168 -11
  194. package/src/widget/components/ConnectedWallets.tsx +342 -0
  195. package/src/widget/components/DebugMenu.tsx +2 -0
  196. package/src/widget/components/DebugScreensList.tsx +3 -0
  197. package/src/widget/components/DebugToast.tsx +63 -0
  198. package/src/widget/components/Earn.tsx +112 -143
  199. package/src/widget/components/EarnPools.tsx +2 -4
  200. package/src/widget/components/EarnPoolsFilters.tsx +6 -6
  201. package/src/widget/components/FeeOption.tsx +78 -0
  202. package/src/widget/components/FeeOptions.tsx +192 -127
  203. package/src/widget/components/Fund.tsx +1236 -0
  204. package/src/widget/components/FundMethods.tsx +4 -4
  205. package/src/widget/components/FundSendForm.tsx +1 -34
  206. package/src/widget/components/Identicon.tsx +158 -0
  207. package/src/widget/components/MeshConnectExchanges.tsx +32 -3
  208. package/src/widget/components/MeshConnectFlow.tsx +23 -4
  209. package/src/widget/components/NativeGasOption.tsx +99 -0
  210. package/src/widget/components/Pay.tsx +1092 -0
  211. package/src/widget/components/PaySendForm.tsx +1 -38
  212. package/src/widget/components/QuoteDetails.tsx +1 -30
  213. package/src/widget/components/Receipt.tsx +1 -1
  214. package/src/widget/components/Receive.tsx +4 -2
  215. package/src/widget/components/RecentTokens.tsx +2 -1
  216. package/src/widget/components/Recipients.tsx +448 -0
  217. package/src/widget/components/RefundWarning.tsx +61 -0
  218. package/src/widget/components/ScreenHeader.tsx +1 -1
  219. package/src/widget/components/SimpleSwap.tsx +74 -58
  220. package/src/widget/components/Swap.tsx +35 -853
  221. package/src/widget/components/SwapSettings.tsx +5 -11
  222. package/src/widget/components/ThemeProvider.tsx +32 -0
  223. package/src/widget/components/ThemeSyncer.tsx +47 -0
  224. package/src/widget/components/Toast.tsx +315 -0
  225. package/src/widget/components/TokenList.tsx +2 -34
  226. package/src/widget/components/TokenSelector.tsx +14 -3
  227. package/src/widget/components/TransactionDetails.tsx +153 -13
  228. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  229. package/src/widget/components/TruncatedAddress.tsx +5 -1
  230. package/src/widget/components/UserPreferences.tsx +155 -0
  231. package/src/widget/components/WalletList.tsx +1 -1
  232. package/src/widget/hooks/useBack.tsx +4 -0
  233. package/src/widget/hooks/useBalanceVisible.tsx +40 -2
  234. package/src/widget/hooks/useCheckout.ts +13 -0
  235. package/src/widget/hooks/useCurrentScreen.tsx +4 -0
  236. package/src/widget/hooks/useDebugScreens.ts +12 -2
  237. package/src/widget/hooks/useDefaultTokenSelection.tsx +471 -0
  238. package/src/widget/hooks/useIntentTransactionHistory.ts +212 -0
  239. package/src/widget/hooks/usePayMessage.tsx +370 -0
  240. package/src/widget/hooks/useRecipients.ts +168 -0
  241. package/src/widget/hooks/useSelectedFeeToken.tsx +299 -0
  242. package/src/widget/hooks/useSelectedMeshExchange.tsx +46 -0
  243. package/src/widget/hooks/useSelectedRecipient.tsx +48 -0
  244. package/src/widget/hooks/useSendForm.ts +257 -49
  245. package/src/widget/hooks/useSwapAmount.tsx +50 -0
  246. package/src/widget/hooks/useSwapSettings.tsx +100 -0
  247. package/src/widget/hooks/useTargetAmount.ts +23 -0
  248. package/src/widget/hooks/useTheme.tsx +80 -0
  249. package/src/widget/hooks/useTokenList.ts +20 -11
  250. package/src/widget/index.css +45 -21
  251. package/src/widget/widget.tsx +294 -136
  252. package/dist/address.d.ts +0 -2
  253. package/dist/address.d.ts.map +0 -1
  254. package/dist/proxyCaller.d.ts +0 -21
  255. package/dist/proxyCaller.d.ts.map +0 -1
  256. package/src/address.ts +0 -6
@@ -3,16 +3,10 @@ import type React from "react"
3
3
  import { useEffect, useRef, useState } from "react"
4
4
  import { SwapDisplayMode } from "./SwapDisplayMode.js"
5
5
  import { SlippageToleranceSettings } from "./SlippageToleranceSettings.js"
6
+ import { useSwapSettings } from "../hooks/useSwapSettings.js"
6
7
 
7
- interface SwapSettingsProps {
8
- isSimpleMode: boolean
9
- onModeChange: (isSimple: boolean) => void
10
- }
11
-
12
- export const SwapSettings: React.FC<SwapSettingsProps> = ({
13
- isSimpleMode,
14
- onModeChange,
15
- }) => {
8
+ export const SwapSettings: React.FC = () => {
9
+ const { isSimpleSwapMode, setIsSimpleSwapModeWithStorage } = useSwapSettings()
16
10
  const [isSettingsDropdownOpen, setIsSettingsDropdownOpen] = useState(false)
17
11
  const settingsDropdownRef = useRef<HTMLDivElement>(null)
18
12
 
@@ -34,7 +28,7 @@ export const SwapSettings: React.FC<SwapSettingsProps> = ({
34
28
  }, [isSettingsDropdownOpen])
35
29
 
36
30
  const handleModeSelect = (isSimple: boolean) => {
37
- onModeChange(isSimple)
31
+ setIsSimpleSwapModeWithStorage(isSimple)
38
32
  setIsSettingsDropdownOpen(false)
39
33
  }
40
34
 
@@ -54,7 +48,7 @@ export const SwapSettings: React.FC<SwapSettingsProps> = ({
54
48
  <div className="absolute right-0 top-full mt-2 w-80 trails-bg-card rounded-lg shadow-lg border trails-border-primary z-20">
55
49
  <div className="p-4 space-y-4">
56
50
  <SwapDisplayMode
57
- isSimpleMode={isSimpleMode}
51
+ isSimpleMode={isSimpleSwapMode}
58
52
  onModeChange={handleModeSelect}
59
53
  />
60
54
 
@@ -2,6 +2,7 @@ import type React from "react"
2
2
  import { createContext, useContext, useEffect, useState } from "react"
3
3
  import type { Theme } from "../../theme.js"
4
4
  import { ThemeProvider as DesignSystemThemeProvider } from "@0xsequence/design-system"
5
+ import { logger } from "../../logger.js"
5
6
 
6
7
  interface ThemeContextType {
7
8
  theme: Theme
@@ -60,6 +61,37 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
60
61
  return () => mediaQuery.removeEventListener("change", handleChange)
61
62
  }, [theme])
62
63
 
64
+ // Listen for theme preference changes from ThemeSyncer
65
+ useEffect(() => {
66
+ const handleThemePreferenceChange = (event: CustomEvent) => {
67
+ const { theme: preferredTheme } = event.detail
68
+ logger.console.log(
69
+ "[ThemeProvider] Received theme preference event:",
70
+ preferredTheme,
71
+ )
72
+ if (
73
+ preferredTheme &&
74
+ ["auto", "light", "dark"].includes(preferredTheme)
75
+ ) {
76
+ logger.console.log("[ThemeProvider] Applying theme:", preferredTheme)
77
+ // Override the current theme with user preference
78
+ setTheme(preferredTheme as Theme)
79
+ setIsDark(getIsDark(preferredTheme as Theme))
80
+ }
81
+ }
82
+
83
+ window.addEventListener(
84
+ "applyThemePreference",
85
+ handleThemePreferenceChange as EventListener,
86
+ )
87
+ return () => {
88
+ window.removeEventListener(
89
+ "applyThemePreference",
90
+ handleThemePreferenceChange as EventListener,
91
+ )
92
+ }
93
+ }, [])
94
+
63
95
  const getActiveTheme = (): "light" | "dark" => {
64
96
  return isDark ? "dark" : "light"
65
97
  }
@@ -0,0 +1,47 @@
1
+ import { useEffect } from "react"
2
+ import { useThemePreference } from "../hooks/useTheme.js"
3
+ import { useWidgetProps } from "../hooks/useWidgetProps.js"
4
+ import { logger } from "../../logger.js"
5
+
6
+ /**
7
+ * Component that syncs user theme preference with the actual theme system
8
+ * This ensures the user's saved theme preference is applied on widget load
9
+ */
10
+ export const ThemeSyncer: React.FC = () => {
11
+ const { selectedTheme } = useThemePreference()
12
+ const { customCss, theme: widgetTheme } = useWidgetProps()
13
+
14
+ // Sync user preference with actual theme on mount and when preference changes
15
+ // Only sync if customCss is not provided (developer override)
16
+ useEffect(() => {
17
+ if (!customCss) {
18
+ // Use setTimeout to ensure the ThemeProvider has mounted and set up its listener
19
+ const timeoutId = setTimeout(() => {
20
+ // Determine which theme to apply:
21
+ // 1. If user has a preference, use it
22
+ // 2. If no user preference but widget has theme prop, use widget theme
23
+ // 3. If neither, default to "auto"
24
+ const themeToApply = selectedTheme || widgetTheme || "auto"
25
+
26
+ logger.console.log(
27
+ "[ThemeSyncer] Applying theme - User preference:",
28
+ selectedTheme,
29
+ "Widget theme:",
30
+ widgetTheme,
31
+ "Final theme:",
32
+ themeToApply,
33
+ )
34
+
35
+ const event = new CustomEvent("applyThemePreference", {
36
+ detail: { theme: themeToApply },
37
+ })
38
+ window.dispatchEvent(event)
39
+ }, 0)
40
+
41
+ return () => clearTimeout(timeoutId)
42
+ }
43
+ }, [selectedTheme, customCss, widgetTheme])
44
+
45
+ // This component doesn't render anything
46
+ return null
47
+ }
@@ -0,0 +1,315 @@
1
+ import { AnimatePresence, motion } from "motion/react"
2
+ import { X, CheckCircle, AlertCircle, Info } from "lucide-react"
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useState,
7
+ useCallback,
8
+ useEffect,
9
+ useRef,
10
+ type ReactNode,
11
+ } from "react"
12
+ import { createPortal } from "react-dom"
13
+ import {
14
+ setGlobalShowToast,
15
+ setGlobalUpdatePersistentToast,
16
+ setGlobalRemovePersistentToast,
17
+ setToastEnabled,
18
+ } from "../../toast.js"
19
+ import { useWidgetProps } from "../hooks/useWidgetProps.js"
20
+ import { useTheme } from "./ThemeProvider.js"
21
+ import css from "../compiled.css?inline"
22
+
23
+ const TRAILS_ICON_URL = "https://trails.build/web-app-manifest-192x192.png"
24
+
25
+ // Default durations by toast type (in milliseconds)
26
+ const DEFAULT_DURATIONS: Record<ToastType, number> = {
27
+ success: 3000, // 3 seconds
28
+ error: 5000, // 5 seconds
29
+ info: 5000, // 5 seconds
30
+ }
31
+
32
+ export type ToastType = "success" | "error" | "info"
33
+
34
+ export interface Toast {
35
+ id: string
36
+ title: string
37
+ message: string
38
+ type: ToastType
39
+ duration?: number
40
+ iconUrl?: string
41
+ isPersistent?: boolean
42
+ }
43
+
44
+ interface ToastContextValue {
45
+ toasts: Toast[]
46
+ showToast: (
47
+ title: string,
48
+ message: string,
49
+ type?: ToastType,
50
+ duration?: number,
51
+ iconUrl?: string,
52
+ ) => void
53
+ updatePersistentToast: (
54
+ title: string,
55
+ message: string,
56
+ type?: ToastType,
57
+ iconUrl?: string,
58
+ ) => void
59
+ removePersistentToast: () => void
60
+ removeToast: (id: string) => void
61
+ }
62
+
63
+ const ToastContext = createContext<ToastContextValue | null>(null)
64
+
65
+ export function useToast() {
66
+ const context = useContext(ToastContext)
67
+ if (!context) {
68
+ throw new Error("useToast must be used within ToastProvider")
69
+ }
70
+ return context
71
+ }
72
+
73
+ const PERSISTENT_TOAST_ID = "persistent-toast"
74
+
75
+ export function ToastProvider({ children }: { children: ReactNode }) {
76
+ const [toasts, setToasts] = useState<Toast[]>([])
77
+ const { toast: toastEnabled = false } = useWidgetProps()
78
+
79
+ // Set global toast enabled state
80
+ useEffect(() => {
81
+ setToastEnabled(toastEnabled)
82
+ }, [toastEnabled])
83
+
84
+ const showToast = useCallback(
85
+ (
86
+ title: string,
87
+ message: string,
88
+ type: ToastType = "info",
89
+ duration?: number,
90
+ iconUrl?: string,
91
+ ) => {
92
+ // Check if toasts are enabled before showing
93
+ if (!toastEnabled) {
94
+ return
95
+ }
96
+
97
+ // Use default duration based on type if not provided
98
+ const effectiveDuration = duration ?? DEFAULT_DURATIONS[type]
99
+
100
+ // Use Trails icon by default if no custom icon provided
101
+ const effectiveIconUrl = iconUrl ?? TRAILS_ICON_URL
102
+
103
+ const id = `toast-${Date.now()}-${Math.random()}`
104
+ const toast: Toast = {
105
+ id,
106
+ title,
107
+ message,
108
+ type,
109
+ duration: effectiveDuration,
110
+ iconUrl: effectiveIconUrl,
111
+ }
112
+
113
+ setToasts((prev) => [...prev, toast])
114
+
115
+ // Auto-remove after duration
116
+ if (effectiveDuration > 0) {
117
+ setTimeout(() => {
118
+ setToasts((prev) => prev.filter((t) => t.id !== id))
119
+ }, effectiveDuration)
120
+ }
121
+ },
122
+ [toastEnabled],
123
+ )
124
+
125
+ const updatePersistentToast = useCallback(
126
+ (
127
+ title: string,
128
+ message: string,
129
+ type: ToastType = "info",
130
+ iconUrl?: string,
131
+ ) => {
132
+ // Check if toasts are enabled before showing
133
+ if (!toastEnabled) {
134
+ return
135
+ }
136
+
137
+ // Use Trails icon by default if no custom icon provided
138
+ const effectiveIconUrl = iconUrl ?? TRAILS_ICON_URL
139
+
140
+ const toast: Toast = {
141
+ id: PERSISTENT_TOAST_ID,
142
+ title,
143
+ message,
144
+ type,
145
+ duration: 0, // Never auto-dismiss
146
+ iconUrl: effectiveIconUrl,
147
+ isPersistent: true,
148
+ }
149
+
150
+ setToasts((prev) => {
151
+ // Remove existing persistent toast if any
152
+ const filtered = prev.filter((t) => t.id !== PERSISTENT_TOAST_ID)
153
+ // Add new/updated persistent toast
154
+ return [...filtered, toast]
155
+ })
156
+ },
157
+ [toastEnabled],
158
+ )
159
+
160
+ const removePersistentToast = useCallback(() => {
161
+ setToasts((prev) => prev.filter((t) => t.id !== PERSISTENT_TOAST_ID))
162
+ }, [])
163
+
164
+ const removeToast = useCallback((id: string) => {
165
+ setToasts((prev) => prev.filter((t) => t.id !== id))
166
+ }, [])
167
+
168
+ // Set global toast functions for use outside React components
169
+ useEffect(() => {
170
+ setGlobalShowToast(showToast)
171
+ }, [showToast])
172
+
173
+ useEffect(() => {
174
+ setGlobalUpdatePersistentToast(updatePersistentToast)
175
+ }, [updatePersistentToast])
176
+
177
+ useEffect(() => {
178
+ setGlobalRemovePersistentToast(removePersistentToast)
179
+ }, [removePersistentToast])
180
+
181
+ return (
182
+ <ToastContext.Provider
183
+ value={{
184
+ toasts,
185
+ showToast,
186
+ updatePersistentToast,
187
+ removePersistentToast,
188
+ removeToast,
189
+ }}
190
+ >
191
+ {children}
192
+ <ToastContainer toasts={toasts} onRemove={removeToast} />
193
+ </ToastContext.Provider>
194
+ )
195
+ }
196
+
197
+ interface ToastContainerProps {
198
+ toasts: Toast[]
199
+ onRemove: (id: string) => void
200
+ }
201
+
202
+ function ToastContainer({ toasts, onRemove }: ToastContainerProps) {
203
+ const hostRef = useRef<HTMLDivElement>(null)
204
+ const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null)
205
+ const { isDark } = useTheme()
206
+
207
+ useEffect(() => {
208
+ if (hostRef.current && !hostRef.current.shadowRoot) {
209
+ const shadow = hostRef.current.attachShadow({ mode: "open" })
210
+ setShadowRoot(shadow)
211
+
212
+ // Inject styles into shadow root
213
+ const styleTag = document.createElement("style")
214
+ styleTag.textContent = css
215
+ shadow.appendChild(styleTag)
216
+ }
217
+ }, [])
218
+
219
+ const toastContent = (
220
+ <div
221
+ className={`fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none ${isDark ? "dark" : ""}`}
222
+ >
223
+ <AnimatePresence>
224
+ {toasts.map((toast) => (
225
+ <ToastItem key={toast.id} toast={toast} onRemove={onRemove} />
226
+ ))}
227
+ </AnimatePresence>
228
+ </div>
229
+ )
230
+
231
+ return (
232
+ <div
233
+ ref={hostRef}
234
+ style={{ position: "fixed", top: 0, right: 0, zIndex: 99999 }}
235
+ >
236
+ {shadowRoot ? createPortal(toastContent, shadowRoot) : null}
237
+ </div>
238
+ )
239
+ }
240
+
241
+ interface ToastItemProps {
242
+ toast: Toast
243
+ onRemove: (id: string) => void
244
+ }
245
+
246
+ function ToastItem({ toast, onRemove }: ToastItemProps) {
247
+ const getIcon = () => {
248
+ switch (toast.type) {
249
+ case "success":
250
+ return (
251
+ <CheckCircle className="w-5 h-5 text-green-600 dark:text-green-400" />
252
+ )
253
+ case "error":
254
+ return (
255
+ <AlertCircle className="w-5 h-5 text-red-600 dark:text-red-400" />
256
+ )
257
+ case "info":
258
+ return <Info className="w-5 h-5 text-blue-600 dark:text-blue-400" />
259
+ default:
260
+ return <Info className="w-5 h-5 text-blue-600 dark:text-blue-400" />
261
+ }
262
+ }
263
+
264
+ const isPersistent = toast.isPersistent
265
+
266
+ return (
267
+ <motion.div
268
+ key={toast.id}
269
+ initial={{ opacity: 0, y: -20, x: 20 }}
270
+ animate={{ opacity: 1, y: 0, x: 0 }}
271
+ exit={{ opacity: 0, x: 20 }}
272
+ transition={{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }}
273
+ className="pointer-events-auto"
274
+ >
275
+ <div
276
+ className={`bg-white/95 dark:bg-gray-900/95 backdrop-blur-xl [box-shadow:0_10px_25px_-5px_rgba(0,0,0,0.08),0_8px_10px_-6px_rgba(0,0,0,0.08)] p-4 min-w-[340px] max-w-md border border-gray-200/50 dark:border-gray-700/50 [border-radius:var(--trails-widget-border-radius,16px)] ${isPersistent ? "ring-2 ring-blue-500/20 dark:ring-blue-400/20" : ""}`}
277
+ >
278
+ <div className="flex items-start gap-3">
279
+ {toast.iconUrl && (
280
+ <div className="flex-shrink-0">
281
+ <img
282
+ src={toast.iconUrl}
283
+ alt="App Icon"
284
+ className="w-10 h-10 rounded-xl shadow-sm"
285
+ onError={(e) => {
286
+ e.currentTarget.style.display = "none"
287
+ }}
288
+ />
289
+ </div>
290
+ )}
291
+ <div className="flex-1 min-w-0">
292
+ <div className="flex items-center gap-2 mb-0.5">
293
+ <div className="flex-shrink-0">{getIcon()}</div>
294
+ <h4 className="text-sm font-semibold text-gray-900 dark:text-white leading-snug">
295
+ {toast.title}
296
+ </h4>
297
+ </div>
298
+ <p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
299
+ {toast.message}
300
+ </p>
301
+ </div>
302
+ <button
303
+ type="button"
304
+ onClick={() => onRemove(toast.id)}
305
+ className="flex-shrink-0 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 transition-colors rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
306
+ >
307
+ <X className="w-4 h-4" />
308
+ </button>
309
+ </div>
310
+ </div>
311
+ </motion.div>
312
+ )
313
+ }
314
+
315
+ export default ToastProvider
@@ -1,4 +1,4 @@
1
- import { useState, useMemo } from "react"
1
+ import { useState } from "react"
2
2
  import type React from "react"
3
3
  import type { Token } from "../hooks/useTokenList.js"
4
4
  import type { SupportedToken } from "../../tokens.js"
@@ -8,7 +8,6 @@ import { ChainList } from "./ChainList.js"
8
8
  import { useTokenList } from "../hooks/useTokenList.js"
9
9
  import { useMode } from "../hooks/useMode.js"
10
10
  import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
11
- import { getChainInfo } from "../../chains.js"
12
11
 
13
12
  interface TokenListProps {
14
13
  onContinue: (selectedToken: Token) => void
@@ -55,32 +54,6 @@ export const TokenList: React.FC<TokenListProps> = ({
55
54
  fundMethod,
56
55
  })
57
56
 
58
- // Get filtered tokens for chain list
59
- const { filteredTokensFormatted } = useTokenList({
60
- onContinue: () => {}, // dummy function for getting tokens
61
- targetAmountUsd,
62
- onError: () => {}, // dummy function
63
- fundMethod,
64
- allSupportedTokens: false,
65
- })
66
-
67
- // Get unique chains from all tokens
68
- const uniqueChains = useMemo(() => {
69
- const chainIds = new Set(
70
- filteredTokensFormatted.map((token) => token.chainId),
71
- )
72
- return Array.from(chainIds)
73
- .map((chainId) => {
74
- const chainInfo = getChainInfo(chainId)
75
- return {
76
- chainId,
77
- name: chainInfo?.name || `Chain ${chainId}`,
78
- imageUrl: "", // We'll use ChainImage component for chain icons
79
- }
80
- })
81
- .sort((a, b) => a.name.localeCompare(b.name))
82
- }, [filteredTokensFormatted])
83
-
84
57
  const handleShowChainList = () => {
85
58
  if (onShowChainList) {
86
59
  onShowChainList()
@@ -92,12 +65,7 @@ export const TokenList: React.FC<TokenListProps> = ({
92
65
 
93
66
  // If showing chain list internally, render ChainList
94
67
  if (showChainListInternal) {
95
- return (
96
- <ChainList
97
- chains={uniqueChains}
98
- onBack={() => setShowChainListInternal(false)}
99
- />
100
- )
68
+ return <ChainList onBack={() => setShowChainListInternal(false)} />
101
69
  }
102
70
 
103
71
  return (
@@ -12,7 +12,7 @@ import { RecentTokens } from "./RecentTokens.js"
12
12
  import { getChainInfo } from "../../chains.js"
13
13
  import { ChainFilterDropdown } from "./ChainFilterDropdown.js"
14
14
  import { SearchInputField } from "./SearchInputField.js"
15
- import { truncateAddress } from "../../address.js"
15
+ import { truncateAddress } from "../../utils.js"
16
16
  import { logger } from "../../logger.js"
17
17
 
18
18
  interface TokenSelectorProps {
@@ -276,8 +276,8 @@ export const TokenSelector: React.FC<TokenSelectorProps> = ({
276
276
  ? "Insufficient balance for this token"
277
277
  : `Select ${tokenName} token`
278
278
  }
279
- className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-bg-secondary trails-hover-bg trails-border-radius-list-button ${
280
- isTokenSelected(token) ? "trails-bg-card" : ""
279
+ className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-list-item trails-border-radius-list-button ${
280
+ isTokenSelected(token) ? "trails-list-item-selected" : ""
281
281
  } ${!isSufficientBalance && fundMethod !== "qr-code" && fundMethod !== "exchange" ? "opacity-75 cursor-not-allowed" : "cursor-pointer"}`}
282
282
  >
283
283
  <div className="relative flex-shrink-0 mr-2">
@@ -477,6 +477,17 @@ export const TokenSelector: React.FC<TokenSelectorProps> = ({
477
477
  </div>
478
478
  )}
479
479
 
480
+ {/* Fund method label */}
481
+ {(fundMethod === "qr-code" || fundMethod === "exchange") && (
482
+ <div className="text-center pt-2 pb-1">
483
+ <p className="text-sm font-medium text-gray-600 dark:text-gray-400">
484
+ {fundMethod === "qr-code"
485
+ ? "Paying with QR Code Deposit"
486
+ : "Paying with Exchange Deposit"}
487
+ </p>
488
+ </div>
489
+ )}
490
+
480
491
  {/* Pay with another method button */}
481
492
  {onNavigateToFundMethods && !isLoadingTokens && (
482
493
  <div className="text-center pt-1">