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,7 +3,7 @@ import { useEffect, useState } from "react"
3
3
  import {
4
4
  globalConfig,
5
5
  getSequenceEnv,
6
- getSequenceApiUrl,
6
+ getTrailsApiUrl,
7
7
  getSequenceIndexerUrl,
8
8
  getSequenceProjectAccessKey,
9
9
  getWalletConnectProjectId,
@@ -15,6 +15,8 @@ import type { RelayerEnv } from "../../relayer.js"
15
15
  import { getChainInfo } from "../../chains.js"
16
16
  import { mainnet } from "viem/chains"
17
17
  import { logger } from "../../logger.js"
18
+ import { useWidgetProps } from "../hooks/useWidgetProps.js"
19
+ import { useTargetAmount } from "../hooks/useTargetAmount.js"
18
20
 
19
21
  interface ConfigDisplayProps {
20
22
  className?: string
@@ -23,8 +25,16 @@ interface ConfigDisplayProps {
23
25
  export const ConfigDisplay: React.FC<ConfigDisplayProps> = ({
24
26
  className = "",
25
27
  }) => {
28
+ const {
29
+ toast = false,
30
+ toAddress,
31
+ toAmount,
32
+ toToken,
33
+ toCalldata,
34
+ } = useWidgetProps()
35
+ const { targetAmountUsdFormatted } = useTargetAmount()
26
36
  const [sequenceEnv, setSequenceEnv] = useState(getSequenceEnv())
27
- const [sequenceApiUrl, setSequenceApiUrl] = useState(getSequenceApiUrl())
37
+ const [trailsApiUrl, setTrailsApiUrl] = useState(getTrailsApiUrl())
28
38
  const [sequenceIndexerUrl, setSequenceIndexerUrl] = useState(
29
39
  getSequenceIndexerUrl(),
30
40
  )
@@ -51,7 +61,7 @@ export const ConfigDisplay: React.FC<ConfigDisplayProps> = ({
51
61
  const updateConfig = () => {
52
62
  logger.console.log("[trails-sdk] globalConfig", globalConfig)
53
63
  setSequenceEnv(getSequenceEnv())
54
- setSequenceApiUrl(getSequenceApiUrl())
64
+ setTrailsApiUrl(getTrailsApiUrl())
55
65
  setSequenceIndexerUrl(getSequenceIndexerUrl())
56
66
  setProjectAccessKey(getSequenceProjectAccessKey())
57
67
  setWalletConnectProjectId(getWalletConnectProjectId())
@@ -81,8 +91,8 @@ export const ConfigDisplay: React.FC<ConfigDisplayProps> = ({
81
91
  { label: "Sequence ENV", value: sequenceEnv, display: sequenceEnv },
82
92
  {
83
93
  label: "API URL",
84
- value: sequenceApiUrl,
85
- display: truncateString(sequenceApiUrl),
94
+ value: trailsApiUrl,
95
+ display: truncateString(trailsApiUrl),
86
96
  },
87
97
  {
88
98
  label: "Indexer URL",
@@ -111,6 +121,32 @@ export const ConfigDisplay: React.FC<ConfigDisplayProps> = ({
111
121
  },
112
122
  { label: "Slippage Tolerance", value: `${slippageTolerance}%` },
113
123
  { label: "Debug Mode", value: debugMode ? "Enabled" : "Disabled" },
124
+ { label: "Toast Notifications", value: toast ? "Enabled" : "Disabled" },
125
+ {
126
+ label: "Target Amount USD",
127
+ value: targetAmountUsdFormatted || "N/A",
128
+ display: targetAmountUsdFormatted || "N/A",
129
+ },
130
+ {
131
+ label: "To Address",
132
+ value: toAddress || "N/A",
133
+ display: toAddress ? truncateString(toAddress, 20) : "N/A",
134
+ },
135
+ {
136
+ label: "To Amount",
137
+ value: toAmount || "N/A",
138
+ display: toAmount || "N/A",
139
+ },
140
+ {
141
+ label: "To Token",
142
+ value: toToken || "N/A",
143
+ display: toToken ? truncateString(toToken, 20) : "N/A",
144
+ },
145
+ {
146
+ label: "To Calldata",
147
+ value: toCalldata || "N/A",
148
+ display: toCalldata ? truncateString(toCalldata, 20) : "N/A",
149
+ },
114
150
  ]
115
151
 
116
152
  return (
@@ -1,6 +1,11 @@
1
1
  import React, { useEffect, useState } from "react"
2
2
  import { ChevronRight, LogOut } from "lucide-react"
3
- import { useAccount, useConnections, useSwitchAccount } from "wagmi"
3
+ import {
4
+ useAccount,
5
+ useConnections,
6
+ useSwitchAccount,
7
+ useConnectors,
8
+ } from "wagmi"
4
9
  import { AlignJustify, Wallet } from "lucide-react"
5
10
  import { ScreenHeader } from "./ScreenHeader.js"
6
11
  import { useWallets, wagmiConnectorToWalletId } from "../../wallets.js"
@@ -35,6 +40,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
35
40
  const { switchAccount } = useSwitchAccount()
36
41
  const { setCurrentScreen } = useCurrentScreen()
37
42
  const { wallets: allWallets } = useWallets()
43
+ const connectors = useConnectors()
38
44
  const [error, setError] = useState<string | null>(null)
39
45
 
40
46
  useEffect(() => {
@@ -96,6 +102,87 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
96
102
 
97
103
  const connectedWallets = getConnectedWallets()
98
104
 
105
+ // Get available browser wallets using EIP-6963 and injected providers
106
+ const getAvailableBrowserWallets = (): Array<{
107
+ connector: any
108
+ walletId: string
109
+ walletConfig: any
110
+ name: string
111
+ icon?: string
112
+ }> => {
113
+ const filteredConnectors = connectors
114
+ .filter((connector) => {
115
+ // EIP-6963 compliant wallets will have type "injected" and be ready when installed
116
+ // Filter for connectors that are:
117
+ // 1. Injected type (browser extensions, including EIP-6963)
118
+ // 2. Not WalletConnect (which is a protocol, not a browser wallet)
119
+ // 3. Not Privy wallet (should not appear in detected wallets)
120
+ // 4. Not already connected
121
+ const isInjected = connector.type === "injected"
122
+ const isNotWalletConnect = connector.id !== "walletConnect"
123
+ const isNotPrivy =
124
+ connector.id !== "privy" &&
125
+ !connector.name?.toLowerCase().includes("privy") &&
126
+ !connector.id?.toLowerCase().includes("privy")
127
+ const isNotAlreadyConnected = !connections.some(
128
+ (conn) => conn.connector.id === connector.id,
129
+ )
130
+
131
+ // Log for debugging EIP-6963 detection
132
+ if (isInjected && isNotWalletConnect) {
133
+ logger.console.log(
134
+ "[trails-sdk] Detected browser wallet via EIP-6963:",
135
+ {
136
+ id: connector.id,
137
+ name: connector.name,
138
+ type: connector.type,
139
+ uid: connector.uid,
140
+ hasIcon: !!connector.icon,
141
+ },
142
+ )
143
+ }
144
+
145
+ // Log all connectors for debugging
146
+ logger.console.log("[trails-sdk] All connector:", {
147
+ id: connector.id,
148
+ name: connector.name,
149
+ type: connector.type,
150
+ })
151
+
152
+ return (
153
+ isInjected &&
154
+ isNotWalletConnect &&
155
+ isNotPrivy &&
156
+ isNotAlreadyConnected
157
+ )
158
+ })
159
+ .map((connector) => {
160
+ const walletId = wagmiConnectorToWalletId(connector)
161
+ const walletConfig = allWallets.find((wallet) => wallet.id === walletId)
162
+
163
+ return {
164
+ connector,
165
+ walletId,
166
+ walletConfig,
167
+ name: connector.name,
168
+ // EIP-6963 providers may have additional metadata including icon
169
+ icon: connector.icon || walletConfig?.icon,
170
+ }
171
+ })
172
+
173
+ // Deduplicate by walletId, keeping the first occurrence
174
+ const seenWalletIds = new Set<string>()
175
+ return filteredConnectors.filter((wallet) => {
176
+ if (seenWalletIds.has(wallet.walletId)) {
177
+ return false
178
+ }
179
+ seenWalletIds.add(wallet.walletId)
180
+ return true
181
+ })
182
+ }
183
+
184
+ const allAvailableBrowserWallets = getAvailableBrowserWallets()
185
+
99
186
  // Handle switching to a different connected wallet
100
187
  const handleWalletSwitch = async (
101
188
  walletAddress: string,
@@ -160,6 +247,14 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
160
247
  return filteredWallets
161
248
  }, [lastClickedWallet, allWallets])
162
249
 
250
+ // Filter out browser wallets that are already in the main wallet options to avoid showing same wallet twice
251
+ const availableBrowserWallets = React.useMemo(() => {
252
+ const mainWalletIds = new Set(orderedWalletOptions.map((w) => w.id))
253
+ return allAvailableBrowserWallets.filter(
254
+ (wallet) => !mainWalletIds.has(wallet.walletId),
255
+ )
256
+ }, [allAvailableBrowserWallets, orderedWalletOptions])
257
+
163
258
  return (
164
259
  <div className="space-y-6">
165
260
  <ScreenHeader
@@ -171,14 +266,6 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
171
266
  {isConnected ? (
172
267
  <div className="space-y-4">
173
268
  <div className="flex flex-col gap-3">
174
- {error && (
175
- <div className="border rounded-lg p-4 bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800">
176
- <p className="text-sm break-words text-red-600 dark:text-red-200">
177
- {error}
178
- </p>
179
- </div>
180
- )}
181
-
182
269
  {/* Connected Wallets List */}
183
270
  {connectedWallets.map((wallet) => (
184
271
  <div key={wallet.address} className="space-y-2">
@@ -242,6 +329,39 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
242
329
  </div>
243
330
  ))}
244
331
 
332
+ {/* Available Browser Wallets Section */}
333
+ {availableBrowserWallets.length > 0 && (
334
+ <div className="pt-2 space-y-2">
335
+ {availableBrowserWallets.map((wallet) => (
336
+ <button
337
+ key={wallet.connector.uid || wallet.connector.id}
338
+ type="button"
339
+ onClick={() => handleWalletConnect(wallet.walletId)}
340
+ className="w-full flex items-center justify-between cursor-pointer font-semibold py-4 px-6 trails-border-radius-large-button transition-all duration-200 trails-bg-secondary trails-hover-bg trails-text-primary"
341
+ >
342
+ <div className="flex items-center space-x-3">
343
+ {typeof wallet.icon === "string" ? (
344
+ <img
345
+ src={wallet.icon}
346
+ alt={wallet.name}
347
+ className="h-6 w-6"
348
+ />
349
+ ) : (
350
+ <Wallet className="h-6 w-6 text-gray-600 dark:text-gray-400" />
351
+ )}
352
+ <div className="flex items-center space-x-2">
353
+ <span>{wallet.walletConfig?.name || wallet.name}</span>
354
+ <span className="text-xs bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 trails-border-radius-container font-normal">
355
+ Detected
356
+ </span>
357
+ </div>
358
+ </div>
359
+ <ChevronRight className="h-5 w-5 text-gray-400" />
360
+ </button>
361
+ ))}
362
+ </div>
363
+ )}
364
+
245
365
  {/* Connect Another Wallet Button */}
246
366
  <button
247
367
  type="button"
@@ -273,6 +393,36 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
273
393
  </div>
274
394
  ) : (
275
395
  <div className="flex flex-col gap-3">
396
+ {/* Available Browser Wallets Section - Not Connected */}
397
+ {availableBrowserWallets.length > 0 &&
398
+ availableBrowserWallets.map((wallet) => (
399
+ <button
400
+ key={wallet.connector.uid || wallet.connector.id}
401
+ type="button"
402
+ onClick={() => handleWalletConnect(wallet.walletId)}
403
+ className="w-full flex items-center justify-between cursor-pointer font-semibold py-4 px-6 trails-border-radius-large-button transition-all duration-200 trails-bg-secondary trails-hover-bg trails-text-primary"
404
+ >
405
+ <div className="flex items-center space-x-3">
406
+ {typeof wallet.icon === "string" ? (
407
+ <img
408
+ src={wallet.icon}
409
+ alt={wallet.name}
410
+ className="h-6 w-6"
411
+ />
412
+ ) : (
413
+ <Wallet className="h-6 w-6 text-gray-600 dark:text-gray-400" />
414
+ )}
415
+ <div className="flex items-center space-x-2">
416
+ <span>{wallet.walletConfig?.name || wallet.name}</span>
417
+ <span className="text-xs bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 trails-border-radius-container font-normal">
418
+ Detected
419
+ </span>
420
+ </div>
421
+ </div>
422
+ <ChevronRight className="h-5 w-5 text-gray-400" />
423
+ </button>
424
+ ))}
425
+
276
426
  {orderedWalletOptions.length > 0 ? (
277
427
  <>
278
428
  {orderedWalletOptions.map((wallet) => (
@@ -301,11 +451,18 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
301
451
  )}
302
452
  <div className="flex items-center space-x-2">
303
453
  <span>{walletConfig?.name || wallet.name}</span>
304
- {lastClickedWallet === wallet.id && (
454
+ {lastClickedWallet === wallet.id ? (
305
455
  <span className="text-xs bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 trails-border-radius-container font-normal">
306
456
  Recent
307
457
  </span>
308
- )}
458
+ ) : allAvailableBrowserWallets.some(
459
+ (detectedWallet) =>
460
+ detectedWallet.walletId === wallet.id,
461
+ ) ? (
462
+ <span className="text-xs bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 px-2 py-1 trails-border-radius-container font-normal">
463
+ Detected
464
+ </span>
465
+ ) : null}
309
466
  </div>
310
467
  </>
311
468
  )
@@ -0,0 +1,342 @@
1
+ import { useState } from "react"
2
+ import type React from "react"
3
+ import { Wallet, Copy, X } from "lucide-react"
4
+ import {
5
+ useAccount,
6
+ useConnections,
7
+ useSwitchAccount,
8
+ useDisconnect,
9
+ } from "wagmi"
10
+ import { useWallets, wagmiConnectorToWalletId } from "../../wallets.js"
11
+ import { logger } from "../../logger.js"
12
+ import { truncateAddress } from "../../utils.js"
13
+ import { useAccountTotalBalanceUsd } from "../../tokenBalances.js"
14
+ import { Identicon } from "./Identicon.js"
15
+
16
+ export interface ConnectedWalletsProps {
17
+ onWalletSwitch?: (address: string) => void
18
+ showActiveWallet?: boolean
19
+ className?: string
20
+ }
21
+
22
+ interface WalletItemProps {
23
+ wallet: {
24
+ address: string
25
+ connector: any
26
+ walletConfig: any
27
+ walletId: string
28
+ isActive: boolean
29
+ }
30
+ onWalletSwitch: (address: string, connector: any) => void
31
+ onDisconnect: (connector: any, e: React.MouseEvent) => void
32
+ onCopyAddress: (address: string, e: React.MouseEvent) => void
33
+ copiedAddress: string | null
34
+ }
35
+
36
+ const WalletItem: React.FC<WalletItemProps> = ({
37
+ wallet,
38
+ onWalletSwitch,
39
+ onDisconnect,
40
+ onCopyAddress,
41
+ copiedAddress,
42
+ }) => {
43
+ const { totalBalanceUsdFormatted, isLoadingTotalBalanceUsd } =
44
+ useAccountTotalBalanceUsd(wallet.address)
45
+
46
+ return (
47
+ <div className="space-y-1">
48
+ {/* Wallet Button */}
49
+ <div
50
+ onClick={() => {
51
+ if (!wallet.isActive) {
52
+ onWalletSwitch(wallet.address, wallet.connector)
53
+ }
54
+ }}
55
+ onKeyDown={(e) => {
56
+ if ((e.key === "Enter" || e.key === " ") && !wallet.isActive) {
57
+ e.preventDefault()
58
+ onWalletSwitch(wallet.address, wallet.connector)
59
+ }
60
+ }}
61
+ tabIndex={wallet.isActive ? -1 : 0}
62
+ className={`group w-full flex items-center justify-between font-medium py-2 px-3 rounded-lg transition-all duration-200 ${
63
+ wallet.isActive
64
+ ? "bg-blue-50 dark:bg-blue-900/20 text-gray-700 dark:text-gray-300 cursor-default"
65
+ : "bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-300 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-600"
66
+ }`}
67
+ >
68
+ <div className="flex items-center space-x-3">
69
+ {/* Identicon */}
70
+ <Identicon value={wallet.address} size={32} />
71
+ <div className="flex flex-col items-start space-y-1">
72
+ <div className="flex items-center space-x-2">
73
+ {/* Wallet Icon */}
74
+ {typeof wallet.walletConfig?.icon === "string" ? (
75
+ <img
76
+ src={wallet.walletConfig.icon}
77
+ alt={wallet.walletConfig.name}
78
+ className="h-4 w-4"
79
+ />
80
+ ) : (
81
+ <Wallet className="h-4 w-4 text-gray-600 dark:text-gray-400" />
82
+ )}
83
+ <span className="text-sm">
84
+ {wallet.walletConfig?.name ||
85
+ wallet.connector?.name ||
86
+ "Wallet"}
87
+ </span>
88
+ {!isLoadingTotalBalanceUsd && totalBalanceUsdFormatted && (
89
+ <span className="text-xs text-gray-400 dark:text-gray-500">
90
+ {totalBalanceUsdFormatted}
91
+ </span>
92
+ )}
93
+ </div>
94
+ <div className="flex items-center space-x-1">
95
+ <span className="text-xs text-gray-500 dark:text-gray-400 font-mono">
96
+ {truncateAddress(wallet.address)}
97
+ </span>
98
+ <button
99
+ type="button"
100
+ onClick={(e) => {
101
+ e.preventDefault()
102
+ e.stopPropagation()
103
+ onCopyAddress(wallet.address, e)
104
+ }}
105
+ onMouseDown={(e) => e.stopPropagation()}
106
+ onMouseUp={(e) => e.stopPropagation()}
107
+ className={`p-0.5 rounded transition-all duration-200 cursor-pointer z-10 relative ${
108
+ copiedAddress === wallet.address
109
+ ? "bg-green-100 dark:bg-green-900/30"
110
+ : "hover:bg-gray-200 dark:hover:bg-gray-600"
111
+ }`}
112
+ title={
113
+ copiedAddress === wallet.address
114
+ ? "Copied!"
115
+ : "Copy full address"
116
+ }
117
+ >
118
+ {copiedAddress === wallet.address ? (
119
+ <svg
120
+ className="w-3 h-3 text-green-600 dark:text-green-400"
121
+ fill="none"
122
+ viewBox="0 0 24 24"
123
+ stroke="currentColor"
124
+ aria-label="Copied"
125
+ >
126
+ <title>Copied</title>
127
+ <path
128
+ strokeLinecap="round"
129
+ strokeLinejoin="round"
130
+ strokeWidth={2}
131
+ d="M5 13l4 4L19 7"
132
+ />
133
+ </svg>
134
+ ) : (
135
+ <Copy className="w-3 h-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" />
136
+ )}
137
+ </button>
138
+ </div>
139
+ </div>
140
+ </div>
141
+
142
+ <div className="flex items-center gap-2">
143
+ {wallet.isActive ? (
144
+ <span className="text-xs px-2 py-1 rounded font-medium bg-blue-500 text-white">
145
+ Active
146
+ </span>
147
+ ) : (
148
+ <span className="text-xs px-2 py-1 rounded font-medium bg-gray-200 text-gray-700 dark:bg-gray-600 dark:text-gray-300 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
149
+ Set as active
150
+ </span>
151
+ )}
152
+ <button
153
+ type="button"
154
+ onClick={(e) => onDisconnect(wallet.connector, e)}
155
+ onMouseDown={(e) => e.stopPropagation()}
156
+ onMouseUp={(e) => e.stopPropagation()}
157
+ className="p-1 rounded-full hover:bg-gray-200 dark:hover:bg-gray-600 opacity-0 group-hover:opacity-100 transition-all duration-200 cursor-pointer"
158
+ title="Disconnect wallet"
159
+ aria-label="Disconnect wallet"
160
+ >
161
+ <X className="w-4 h-4 text-gray-500 dark:text-gray-400" />
162
+ </button>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ )
167
+ }
168
+
169
+ export const ConnectedWallets: React.FC<ConnectedWalletsProps> = ({
170
+ onWalletSwitch,
171
+ showActiveWallet = true,
172
+ className = "",
173
+ }) => {
174
+ const { address, connector } = useAccount()
175
+ const connections = useConnections()
176
+ const { switchAccount } = useSwitchAccount()
177
+ const { disconnect } = useDisconnect()
178
+ const { wallets: allWallets } = useWallets()
179
+ const [error, setError] = useState<string | null>(null)
180
+ const [copiedAddress, setCopiedAddress] = useState<string | null>(null)
181
+
182
+ // Get all connected wallets with their details
183
+ const getConnectedWallets = () => {
184
+ const walletMap = new Map()
185
+
186
+ connections.forEach((connection) => {
187
+ // Process each account in the connection
188
+ connection.accounts.forEach((account) => {
189
+ if (account && !walletMap.has(account)) {
190
+ const walletId = wagmiConnectorToWalletId(connection.connector)
191
+ const walletConfig = allWallets.find(
192
+ (wallet) => wallet.id === walletId,
193
+ )
194
+
195
+ walletMap.set(account, {
196
+ address: account,
197
+ connector: connection.connector,
198
+ walletConfig,
199
+ walletId,
200
+ isActive:
201
+ connection.connector.id === connector?.id && account === address,
202
+ })
203
+ }
204
+ })
205
+ })
206
+
207
+ return Array.from(walletMap.values())
208
+ }
209
+
210
+ const connectedWallets = getConnectedWallets()
211
+
212
+ // Handle switching to a different connected wallet
213
+ const handleWalletSwitch = async (
214
+ walletAddress: string,
215
+ walletConnector: any,
216
+ ) => {
217
+ try {
218
+ setError(null)
219
+ // Find the connection for this wallet
220
+ const connection = connections.find(
221
+ (conn) =>
222
+ conn.accounts.includes(walletAddress as `0x${string}`) &&
223
+ conn.connector.id === walletConnector.id,
224
+ )
225
+
226
+ if (connection) {
227
+ await switchAccount({
228
+ connector: connection.connector,
229
+ })
230
+ logger.console.log(`[trails-sdk] Switched to wallet: ${walletAddress}`)
231
+
232
+ // Call the optional callback
233
+ if (onWalletSwitch) {
234
+ onWalletSwitch(walletAddress)
235
+ }
236
+ }
237
+ } catch (error) {
238
+ logger.console.error("[trails-sdk] Failed to switch wallet:", error)
239
+ setError(
240
+ error instanceof Error ? error.message : "Failed to switch wallet",
241
+ )
242
+ }
243
+ }
244
+
245
+ // Handle disconnecting a specific wallet
246
+ const handleDisconnect = async (
247
+ walletConnector: any,
248
+ e: React.MouseEvent,
249
+ ) => {
250
+ e.preventDefault()
251
+ e.stopPropagation()
252
+ try {
253
+ setError(null)
254
+ await disconnect({ connector: walletConnector })
255
+ logger.console.log("[trails-sdk] Disconnected wallet")
256
+ } catch (error) {
257
+ logger.console.error("[trails-sdk] Failed to disconnect wallet:", error)
258
+ setError(
259
+ error instanceof Error ? error.message : "Failed to disconnect wallet",
260
+ )
261
+ }
262
+ }
263
+
264
+ // Handle copy to clipboard with success indication
265
+ const handleCopyAddress = async (address: string, e: React.MouseEvent) => {
266
+ e.preventDefault()
267
+ e.stopPropagation()
268
+ try {
269
+ await navigator.clipboard.writeText(address)
270
+ setCopiedAddress(address)
271
+ setTimeout(() => setCopiedAddress(null), 2000) // Clear after 2 seconds
272
+ logger.console.log("[trails-sdk] Successfully copied address:", address)
273
+ } catch (error) {
274
+ logger.console.error("[trails-sdk] Failed to copy address:", error)
275
+ // Fallback for older browsers or when clipboard API is not available
276
+ try {
277
+ const textArea = document.createElement("textarea")
278
+ textArea.value = address
279
+ document.body.appendChild(textArea)
280
+ textArea.select()
281
+ document.execCommand("copy")
282
+ document.body.removeChild(textArea)
283
+ setCopiedAddress(address)
284
+ setTimeout(() => setCopiedAddress(null), 2000)
285
+ logger.console.log(
286
+ "[trails-sdk] Successfully copied address using fallback:",
287
+ address,
288
+ )
289
+ } catch (fallbackError) {
290
+ logger.console.error(
291
+ "[trails-sdk] Fallback copy also failed:",
292
+ fallbackError,
293
+ )
294
+ }
295
+ }
296
+ }
297
+
298
+ if (connectedWallets.length === 0) {
299
+ return null
300
+ }
301
+
302
+ return (
303
+ <div className={`space-y-2 ${className}`}>
304
+ {/* Header */}
305
+ <div className="flex items-center gap-2 mb-3">
306
+ <Wallet className="h-4 w-4 text-gray-400 dark:text-gray-500" />
307
+ <span className="text-sm font-medium text-gray-500 dark:text-gray-400">
308
+ Connected Wallets
309
+ </span>
310
+ </div>
311
+
312
+ {error && (
313
+ <div className="border rounded-lg p-4 bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800">
314
+ <p className="text-sm break-words text-red-600 dark:text-red-200">
315
+ {error}
316
+ </p>
317
+ </div>
318
+ )}
319
+
320
+ {/* Connected Wallets List */}
321
+ {connectedWallets.map((wallet) => {
322
+ // Skip active wallet if showActiveWallet is false
323
+ if (!showActiveWallet && wallet.isActive) {
324
+ return null
325
+ }
326
+
327
+ return (
328
+ <WalletItem
329
+ key={wallet.address}
330
+ wallet={wallet}
331
+ onWalletSwitch={handleWalletSwitch}
332
+ onDisconnect={handleDisconnect}
333
+ onCopyAddress={handleCopyAddress}
334
+ copiedAddress={copiedAddress}
335
+ />
336
+ )
337
+ })}
338
+ </div>
339
+ )
340
+ }
341
+
342
+ export default ConnectedWallets
@@ -4,6 +4,7 @@ import { useRef, useState } from "react"
4
4
  import { getDebug } from "../../config.js"
5
5
  import { ConfigDisplay } from "./ConfigDisplay.js"
6
6
  import { DebugScreensList } from "./DebugScreensList.js"
7
+ import { DebugToast } from "./DebugToast.js"
7
8
 
8
9
  interface DebugMenuProps {
9
10
  onScreenSelect: (screen: string) => void
@@ -39,6 +40,7 @@ export const DebugMenu: React.FC<DebugMenuProps> = ({ onScreenSelect }) => {
39
40
  {isOpen && (
40
41
  <div className="absolute bottom-full right-0 mb-1 w-80 border border-solid rounded-lg shadow-lg overflow-hidden max-h-[400px] overflow-y-auto custom-scrollbar bg-white border-gray-200 text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 z-50">
41
42
  <DebugScreensList onScreenSelect={handleScreenSelect} />
43
+ <DebugToast />
42
44
  <ConfigDisplay />
43
45
  </div>
44
46
  )}
@@ -16,6 +16,7 @@ const SCREENS = [
16
16
  "Chain List",
17
17
  "Send Form",
18
18
  "Fund Form",
19
+ "Recipients",
19
20
  "Earn",
20
21
  "Earn Pools",
21
22
  "Swap",
@@ -36,6 +37,8 @@ const SCREENS = [
36
37
  "Receipt Failed",
37
38
  "Receipt Refunded",
38
39
  "Account History",
40
+ "Account Settings",
41
+ "User Preferences",
39
42
  ] as const
40
43
 
41
44
  export const DebugScreensList: React.FC<DebugScreensListProps> = ({