0xtrails 0.2.2 → 0.2.5

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 (194) hide show
  1. package/dist/aave.d.ts +8 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/{ccip-ConT1gDe.js → ccip-CXlshvBY.js} +1 -1
  4. package/dist/chains.d.ts +5 -1
  5. package/dist/chains.d.ts.map +1 -1
  6. package/dist/config.d.ts +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +5 -4
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/error.d.ts +1 -0
  11. package/dist/error.d.ts.map +1 -1
  12. package/dist/estimate.d.ts +52 -0
  13. package/dist/estimate.d.ts.map +1 -1
  14. package/dist/{index-CMh8uEbV.js → index-_QuyGrjU.js} +86304 -83380
  15. package/dist/index.d.ts +4 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -2
  18. package/dist/intentEntrypoint.d.ts +0 -8
  19. package/dist/intentEntrypoint.d.ts.map +1 -1
  20. package/dist/intents.d.ts +40 -0
  21. package/dist/intents.d.ts.map +1 -1
  22. package/dist/metaTxnMonitor.d.ts +5 -4
  23. package/dist/metaTxnMonitor.d.ts.map +1 -1
  24. package/dist/metaTxns.d.ts +3 -3
  25. package/dist/metaTxns.d.ts.map +1 -1
  26. package/dist/morpho.d.ts +8 -0
  27. package/dist/morpho.d.ts.map +1 -1
  28. package/dist/prepareSend.d.ts +16 -6
  29. package/dist/prepareSend.d.ts.map +1 -1
  30. package/dist/queryParams.d.ts.map +1 -1
  31. package/dist/relayer.d.ts +10 -7
  32. package/dist/relayer.d.ts.map +1 -1
  33. package/dist/sequenceWallet.d.ts +3 -2
  34. package/dist/sequenceWallet.d.ts.map +1 -1
  35. package/dist/tokenBalances.d.ts +7 -0
  36. package/dist/tokenBalances.d.ts.map +1 -1
  37. package/dist/tokens.d.ts +2 -1
  38. package/dist/tokens.d.ts.map +1 -1
  39. package/dist/trails.d.ts +2 -2
  40. package/dist/trails.d.ts.map +1 -1
  41. package/dist/wallets.d.ts.map +1 -1
  42. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  43. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  44. package/dist/widget/components/ClassicSwap.d.ts +2 -0
  45. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  46. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  47. package/dist/widget/components/ConnectedWallets.d.ts +4 -0
  48. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  49. package/dist/widget/components/Earn.d.ts.map +1 -1
  50. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  51. package/dist/widget/components/Fund.d.ts +1 -0
  52. package/dist/widget/components/Fund.d.ts.map +1 -1
  53. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  54. package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +11 -5
  55. package/dist/widget/components/FundSwap.d.ts.map +1 -0
  56. package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
  57. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
  58. package/dist/widget/components/Modal.d.ts.map +1 -1
  59. package/dist/widget/components/Pay.d.ts +1 -0
  60. package/dist/widget/components/Pay.d.ts.map +1 -1
  61. package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
  62. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
  63. package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +11 -34
  64. package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
  65. package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +16 -8
  66. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
  67. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  68. package/dist/widget/components/Receive.d.ts.map +1 -1
  69. package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
  70. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
  71. package/dist/widget/components/Recipients.d.ts.map +1 -1
  72. package/dist/widget/components/RefundWarning.d.ts +1 -0
  73. package/dist/widget/components/RefundWarning.d.ts.map +1 -1
  74. package/dist/widget/components/RequiredPropsError.d.ts +8 -0
  75. package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
  76. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  77. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  78. package/dist/widget/components/Swap.d.ts +1 -0
  79. package/dist/widget/components/Swap.d.ts.map +1 -1
  80. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  81. package/dist/widget/components/TokenImage.d.ts +1 -0
  82. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  83. package/dist/widget/components/TokenList.d.ts.map +1 -1
  84. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  85. package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
  86. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
  87. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  88. package/dist/widget/components/WaasFeeOptions.d.ts +8 -0
  89. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
  90. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  91. package/dist/widget/components/WalletList.d.ts.map +1 -1
  92. package/dist/widget/css/compiled.css +2 -0
  93. package/dist/widget/css/index.css +554 -0
  94. package/dist/widget/hooks/useBack.d.ts +6 -0
  95. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  96. package/dist/widget/hooks/useCheckout.d.ts +1 -1
  97. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  98. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  99. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  100. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
  101. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  102. package/dist/widget/hooks/useInitialRedirect.d.ts +7 -0
  103. package/dist/widget/hooks/useInitialRedirect.d.ts.map +1 -0
  104. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  105. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  106. package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
  107. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
  108. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  109. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  110. package/dist/widget/index.js +1 -1
  111. package/dist/widget/widget.d.ts +4 -4
  112. package/dist/widget/widget.d.ts.map +1 -1
  113. package/package.json +30 -23
  114. package/src/aave.ts +32 -0
  115. package/src/chains.ts +23 -3
  116. package/src/config.ts +12 -4
  117. package/src/constants.ts +11 -16
  118. package/src/error.ts +20 -2
  119. package/src/estimate.ts +416 -5
  120. package/src/index.ts +8 -3
  121. package/src/intentEntrypoint.ts +0 -15
  122. package/src/intents.ts +161 -11
  123. package/src/metaTxnMonitor.ts +28 -22
  124. package/src/metaTxns.ts +3 -3
  125. package/src/morpho.ts +32 -0
  126. package/src/prepareSend.ts +710 -458
  127. package/src/queryParams.ts +2 -1
  128. package/src/relayer.ts +15 -16
  129. package/src/sequenceWallet.ts +7 -3
  130. package/src/tokenBalances.ts +47 -0
  131. package/src/tokens.ts +17 -1
  132. package/src/trails.ts +2 -2
  133. package/src/wallets.ts +8 -0
  134. package/src/widget/compiled.css +2 -2
  135. package/src/widget/components/AccountActionsDropdown.tsx +9 -15
  136. package/src/widget/components/AccountIntentTransactionHistory.tsx +1 -1
  137. package/src/widget/components/AccountSettings.tsx +10 -27
  138. package/src/widget/components/ChainFilterDropdown.tsx +1 -1
  139. package/src/widget/components/ChainList.tsx +1 -1
  140. package/src/widget/components/ClassicSwap.tsx +111 -155
  141. package/src/widget/components/ConnectWallet.tsx +10 -39
  142. package/src/widget/components/ConnectedWallets.tsx +113 -58
  143. package/src/widget/components/Earn.tsx +73 -589
  144. package/src/widget/components/EarnPools.tsx +2 -1
  145. package/src/widget/components/Fund.tsx +81 -109
  146. package/src/widget/components/FundMethods.tsx +82 -159
  147. package/src/widget/components/FundSwap.tsx +52 -0
  148. package/src/widget/components/FundingMethodSelectorButton.tsx +60 -0
  149. package/src/widget/components/Modal.tsx +6 -2
  150. package/src/widget/components/Pay.tsx +198 -200
  151. package/src/widget/components/PercentageMaxButtons.tsx +77 -0
  152. package/src/widget/components/PoolDeposit.tsx +593 -0
  153. package/src/widget/components/PoolWithdraw.tsx +903 -0
  154. package/src/widget/components/QuoteDetails.tsx +22 -8
  155. package/src/widget/components/Receive.tsx +1 -3
  156. package/src/widget/components/RecipientSelectorButton.tsx +42 -0
  157. package/src/widget/components/Recipients.tsx +64 -156
  158. package/src/widget/components/RefundWarning.tsx +5 -1
  159. package/src/widget/components/RequiredPropsError.tsx +33 -0
  160. package/src/widget/components/ScreenHeader.tsx +5 -1
  161. package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
  162. package/src/widget/components/Swap.tsx +2 -43
  163. package/src/widget/components/SwapSettings.tsx +3 -15
  164. package/src/widget/components/TokenImage.tsx +21 -4
  165. package/src/widget/components/TokenList.tsx +0 -1
  166. package/src/widget/components/TokenSelector.tsx +2 -1
  167. package/src/widget/components/TokenSelectorButton.tsx +75 -0
  168. package/src/widget/components/UserPreferences.tsx +6 -24
  169. package/src/widget/components/WaasFeeOptions.tsx +331 -0
  170. package/src/widget/components/WalletConfirmation.tsx +55 -3
  171. package/src/widget/components/WalletList.tsx +7 -5
  172. package/src/widget/hooks/useBack.tsx +113 -9
  173. package/src/widget/hooks/useCheckout.ts +36 -20
  174. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  175. package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
  176. package/src/widget/hooks/useInitialRedirect.tsx +70 -0
  177. package/src/widget/hooks/usePayMessage.tsx +86 -11
  178. package/src/widget/hooks/useSelectedFeeToken.tsx +10 -16
  179. package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
  180. package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
  181. package/src/widget/hooks/useSendForm.ts +34 -12
  182. package/src/widget/hooks/useTokenList.ts +1 -1
  183. package/src/widget/index.css +27 -0
  184. package/src/widget/widget.tsx +245 -208
  185. package/dist/widget/components/FundSendForm.d.ts.map +0 -1
  186. package/dist/widget/components/PaySendForm.d.ts.map +0 -1
  187. package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
  188. package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
  189. package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
  190. package/src/widget/components/FundSendForm.tsx +0 -903
  191. package/src/widget/components/PaySendForm.tsx +0 -869
  192. package/src/widget/components/SimpleSwap.tsx +0 -983
  193. package/src/widget/hooks/useSwapSettings.tsx +0 -100
  194. /package/dist/{style.css → 0xtrails.css} +0 -0
@@ -1,9 +1,11 @@
1
1
  import { TokenImage } from "./TokenImage.js"
2
2
  import { ChevronLeft } from "lucide-react"
3
3
  import type React from "react"
4
- import { useEffect, useState } from "react"
4
+ import { useEffect, useMemo, useState } from "react"
5
5
  import type { PrepareSendQuote } from "../../prepareSend.js"
6
6
  import { QuoteDetails } from "./QuoteDetails.js"
7
+ import { WaasFeeOptions } from "./WaasFeeOptions.js"
8
+ import { useAccount } from "wagmi"
7
9
 
8
10
  interface WalletConfirmationProps {
9
11
  onBack?: () => void
@@ -21,6 +23,9 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
21
23
  }) => {
22
24
  const [showContent, setShowContent] = useState(false)
23
25
  const [showTimeoutWarning, setShowTimeoutWarning] = useState(false)
26
+ const [isFeeOptionConfirmed, setIsFeeOptionConfirmed] = useState(false)
27
+ const [areFeeOptionsLoaded, setAreFeeOptionsLoaded] = useState(false)
28
+ const { connector } = useAccount()
24
29
 
25
30
  useEffect(() => {
26
31
  setShowContent(true)
@@ -39,6 +44,25 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
39
44
  return () => clearTimeout(timer)
40
45
  }, [retryEnabled])
41
46
 
47
+ const isSequenceWaas = useMemo(() => {
48
+ if (!connector) return false
49
+ const connectorName = connector.name?.toLowerCase() || ""
50
+ const connectorId = connector.id?.toLowerCase() || ""
51
+ const isSequenceWaas =
52
+ connectorName.includes("waas") ||
53
+ connectorId.includes("waas") ||
54
+ connectorId === "sequence-waas"
55
+
56
+ console.log("[trails-sdk] Sequence WaaS detection:", {
57
+ connectorName,
58
+ connectorId,
59
+ isSequenceWaas,
60
+ connector: connector.name,
61
+ })
62
+
63
+ return isSequenceWaas
64
+ }, [connector])
65
+
42
66
  return (
43
67
  <div className="space-y-6">
44
68
  <div className="flex items-center relative">
@@ -67,6 +91,7 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
67
91
  imageUrl={quote?.originToken.imageUrl}
68
92
  symbol={quote?.originToken.symbol}
69
93
  chainId={quote?.originChain.id}
94
+ contractAddress={quote?.originToken.contractAddress}
70
95
  size={64}
71
96
  />
72
97
  </div>
@@ -76,12 +101,39 @@ export const WalletConfirmation: React.FC<WalletConfirmationProps> = ({
76
101
  className={`mb-2 transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
77
102
  >
78
103
  <h2 className="text-xl font-bold text-gray-900 dark:text-white">
79
- {retryEnabled ? "Try again" : "Waiting for wallet…"}
104
+ {retryEnabled
105
+ ? "Try again"
106
+ : !isSequenceWaas
107
+ ? "Waiting for wallet…"
108
+ : isFeeOptionConfirmed
109
+ ? "Waiting for wallet…"
110
+ : areFeeOptionsLoaded
111
+ ? "Waiting for fee selection…"
112
+ : "Waiting for fee options…"}
80
113
  </h2>
81
114
  <p className="mt-2 text-sm text-gray-600 dark:text-gray-300">
82
- Please approve the request in your wallet
115
+ {!isSequenceWaas
116
+ ? "Please approve the request in your wallet"
117
+ : isFeeOptionConfirmed
118
+ ? "Please approve the request in your wallet"
119
+ : ""}
83
120
  </p>
84
121
  </div>
122
+
123
+ {isSequenceWaas && (
124
+ <div
125
+ className={`mb-2 mt-4 transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
126
+ >
127
+ <WaasFeeOptions
128
+ chainId={quote?.originChain.id}
129
+ setIsFeeOptionConfirmed={setIsFeeOptionConfirmed}
130
+ onFeeOptionsLoaded={() => {
131
+ console.log("[trails-sdk] Fee options loaded callback called")
132
+ setAreFeeOptionsLoaded(true)
133
+ }}
134
+ />
135
+ </div>
136
+ )}
85
137
  </div>
86
138
 
87
139
  {/* Timeout Warning */}
@@ -21,7 +21,9 @@ export const WalletList: React.FC<WalletListProps> = ({
21
21
 
22
22
  const prefilteredWalletOptions = useMemo(() => {
23
23
  return walletOptions
24
- .filter((wallet) => wallet.id !== "injected")
24
+ .filter(
25
+ (wallet) => wallet.id !== "injected" && wallet.id !== "sequence-waas",
26
+ )
25
27
  .sort((a, b) => {
26
28
  const aIndex = topShownWallets.indexOf(a.id)
27
29
  const bIndex = topShownWallets.indexOf(b.id)
@@ -45,10 +47,11 @@ export const WalletList: React.FC<WalletListProps> = ({
45
47
  const filteredWalletOptions = useMemo(() => {
46
48
  if (!searchTerm.trim()) return prefilteredWalletOptions
47
49
 
50
+ const query = searchTerm.trim().toLowerCase()
48
51
  return prefilteredWalletOptions.filter((wallet) => {
49
52
  return (
50
- wallet.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
51
- wallet.id.toLowerCase().includes(searchTerm.toLowerCase())
53
+ wallet.name.toLowerCase().includes(query) ||
54
+ wallet.id.toLowerCase().includes(query)
52
55
  )
53
56
  })
54
57
  }, [prefilteredWalletOptions, searchTerm])
@@ -60,10 +63,9 @@ export const WalletList: React.FC<WalletListProps> = ({
60
63
  return (
61
64
  <div className="space-y-6">
62
65
  <ScreenHeader
63
- headerContent="More Wallets"
66
+ headerContent="Connect a Wallet"
64
67
  headerContentAlign="left"
65
68
  onBack={onBack}
66
- showAccountActions={true}
67
69
  />
68
70
 
69
71
  {/* Search Input */}
@@ -1,4 +1,10 @@
1
- import { createContext, useContext, useState, useEffect } from "react"
1
+ import {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useEffect,
6
+ useCallback,
7
+ } from "react"
2
8
  import type { ReactNode } from "react"
3
9
  import { useCurrentScreen, type Screen } from "./useCurrentScreen.js"
4
10
  import { logger } from "../../logger.js"
@@ -9,6 +15,12 @@ interface BackContextType {
9
15
  clearHistory: () => void
10
16
  isNavigatingBack: boolean
11
17
  getHistory: () => Screen[]
18
+ getBreadcrumbHistory: () => Screen[]
19
+ addBreadcrumb: (screen: Screen) => void
20
+ setAccountSettingsSource: (screen: Screen) => void
21
+ getAccountSettingsSource: () => Screen | null
22
+ setCurrentScreenWithBack: (screen: Screen, backTo?: Screen) => void
23
+ getPreviousScreen: () => Screen | null
12
24
  }
13
25
 
14
26
  const BackContext = createContext<BackContextType | null>(null)
@@ -20,6 +32,9 @@ interface BackProviderProps {
20
32
  export function BackProvider({ children }: BackProviderProps) {
21
33
  const { currentScreen, setCurrentScreen } = useCurrentScreen()
22
34
  const [history, setHistory] = useState<Screen[]>([])
35
+ const [navigationStack, setNavigationStack] = useState<
36
+ Array<[Screen, Screen?]>
37
+ >([])
23
38
  const [isNavigatingBack, setIsNavigatingBack] = useState(false)
24
39
 
25
40
  // Track screen changes and update history
@@ -45,20 +60,83 @@ export function BackProvider({ children }: BackProviderProps) {
45
60
  })
46
61
  }, [currentScreen])
47
62
 
48
- const canGoBack = history.length > 1
63
+ // Get the previous screen from navigation stack
64
+ const getPreviousScreen = (): Screen | null => {
65
+ if (navigationStack.length === 0) {
66
+ return null
67
+ }
68
+
69
+ // Find the current screen in the navigation stack
70
+ const currentIndex = navigationStack.findIndex(
71
+ ([screen]) => screen === currentScreen,
72
+ )
73
+ if (currentIndex === -1) {
74
+ return null
75
+ }
76
+
77
+ const currentEntry = navigationStack[currentIndex]
78
+ if (!currentEntry) {
79
+ return null
80
+ }
81
+ const [, backToScreen] = currentEntry
82
+
83
+ // If this screen has a specific back target, use it
84
+ if (backToScreen) {
85
+ return backToScreen
86
+ }
87
+
88
+ // Otherwise, go to the previous screen in the stack
89
+ if (currentIndex > 0) {
90
+ const previousEntry = navigationStack[currentIndex - 1]
91
+ if (previousEntry) {
92
+ const [previousScreen] = previousEntry
93
+ return previousScreen
94
+ }
95
+ }
96
+
97
+ return null
98
+ }
99
+
100
+ // Function to set current screen with optional back target
101
+ const setCurrentScreenWithBack = useCallback(
102
+ (screen: Screen, backTo?: Screen) => {
103
+ setNavigationStack((prevStack) => {
104
+ // Remove any existing entry for this screen
105
+ const filteredStack = prevStack.filter(
106
+ ([existingScreen]) => existingScreen !== screen,
107
+ )
108
+
109
+ // Add new entry
110
+ const newEntry: [Screen, Screen?] = backTo ? [screen, backTo] : [screen]
111
+ const newStack = [...filteredStack, newEntry]
112
+
113
+ // Keep stack limited to last 10 entries
114
+ if (newStack.length > 10) {
115
+ return newStack.slice(-10)
116
+ }
117
+
118
+ return newStack
119
+ })
120
+
121
+ setCurrentScreen(screen)
122
+ },
123
+ [setCurrentScreen],
124
+ )
125
+
126
+ const canGoBack = navigationStack.length > 0 || history.length > 1
49
127
 
50
128
  const goBack = canGoBack
51
129
  ? () => {
52
- // Remove current screen and go to previous one
53
- const newHistory = history.slice(0, -1)
54
- const previousScreen = newHistory[newHistory.length - 1]
130
+ const previousScreen = getPreviousScreen()
55
131
 
56
132
  logger.console.log(
57
- "[trails-sdk] goBack",
133
+ "[trails-sdk] goBack (navigation stack)",
58
134
  "previousScreen:",
59
135
  previousScreen,
60
136
  "currentScreen:",
61
137
  currentScreen,
138
+ "navigationStack:",
139
+ navigationStack,
62
140
  )
63
141
 
64
142
  // Set flag to prevent auto-selection when navigating back
@@ -67,24 +145,44 @@ export function BackProvider({ children }: BackProviderProps) {
67
145
  }
68
146
 
69
147
  if (previousScreen) {
70
- setHistory(newHistory)
148
+ // Remove current screen from navigation stack
149
+ setNavigationStack((prevStack) =>
150
+ prevStack.filter(([screen]) => screen !== currentScreen),
151
+ )
71
152
  setCurrentScreen(previousScreen)
153
+ } else {
154
+ // Fallback: if no navigation stack, use regular history
155
+ const regularHistory = history
156
+ if (regularHistory.length > 1) {
157
+ const fallbackScreen = regularHistory[regularHistory.length - 2]
158
+ if (fallbackScreen) {
159
+ logger.console.log(
160
+ "[trails-sdk] goBack (fallback to regular history)",
161
+ "previousScreen:",
162
+ fallbackScreen,
163
+ )
164
+ setCurrentScreen(fallbackScreen)
165
+ }
166
+ }
72
167
  }
73
168
 
74
- // Reset the flag after a short delay to allow the screen change to complete
169
+ // Reset the flag after a longer delay to allow the screen change to complete
170
+ // and prevent post-connection navigation from interfering with manual back navigation
75
171
  if (setIsNavigatingBack) {
76
172
  setTimeout(() => {
77
173
  setIsNavigatingBack(false)
78
- }, 100)
174
+ }, 500)
79
175
  }
80
176
  }
81
177
  : undefined
82
178
 
83
179
  const clearHistory = () => {
84
180
  setHistory([currentScreen])
181
+ setNavigationStack([[currentScreen]])
85
182
  }
86
183
 
87
184
  const getHistory = () => history
185
+ const getBreadcrumbHistory = () => navigationStack.map(([screen]) => screen)
88
186
 
89
187
  const value: BackContextType = {
90
188
  goBack,
@@ -92,6 +190,12 @@ export function BackProvider({ children }: BackProviderProps) {
92
190
  clearHistory,
93
191
  isNavigatingBack,
94
192
  getHistory,
193
+ getBreadcrumbHistory,
194
+ addBreadcrumb: () => {}, // Deprecated, kept for compatibility
195
+ setAccountSettingsSource: () => {}, // Deprecated, kept for compatibility
196
+ getAccountSettingsSource: () => null, // Deprecated, kept for compatibility
197
+ setCurrentScreenWithBack,
198
+ getPreviousScreen,
95
199
  }
96
200
 
97
201
  return <BackContext.Provider value={value}>{children}</BackContext.Provider>
@@ -2,6 +2,7 @@ import { useCallback, useRef } from "react"
2
2
  import { getSessionId } from "../../analytics.js"
3
3
  import { logger } from "../../logger.js"
4
4
  import { updatePersistentToast } from "../../toast.js"
5
+ import { getPrettifiedErrorMessage } from "../../error.js"
5
6
 
6
7
  export type CheckoutCallbacks = {
7
8
  onCheckoutStart?: (data: { sessionId: string }) => void
@@ -17,7 +18,7 @@ export type CheckoutCallbacks = {
17
18
  export type CheckoutOnHandlers = {
18
19
  triggerCheckoutStart: () => void
19
20
  triggerCheckoutQuote: (quote: any) => void
20
- triggerCheckoutComplete: () => void
21
+ triggerCheckoutComplete: (txStatus: "success" | "fail") => void
21
22
  triggerCheckoutError: (error: string) => void
22
23
  triggerCheckoutStatusUpdate: (transactionStates: any[]) => void
23
24
  }
@@ -81,26 +82,38 @@ export function useCheckout({
81
82
  [onCheckoutQuote],
82
83
  )
83
84
 
84
- const triggerCheckoutComplete = useCallback(() => {
85
- if (onCheckoutComplete && sessionIdRef.current) {
86
- try {
87
- onCheckoutComplete({
88
- sessionId: sessionIdRef.current,
89
- })
90
- } catch (error) {
91
- logger.console.error(
92
- "[trails-sdk] Error calling onCheckoutComplete:",
93
- error,
94
- )
85
+ const triggerCheckoutComplete = useCallback(
86
+ (txStatus: "success" | "fail") => {
87
+ if (onCheckoutComplete && sessionIdRef.current) {
88
+ try {
89
+ onCheckoutComplete({
90
+ sessionId: sessionIdRef.current,
91
+ })
92
+ } catch (error) {
93
+ logger.console.error(
94
+ "[trails-sdk] Error calling onCheckoutComplete:",
95
+ error,
96
+ )
97
+ }
95
98
  }
96
- }
97
99
 
98
- updatePersistentToast(
99
- "Transaction Complete",
100
- "Your transaction has been successfully processed",
101
- "success",
102
- )
103
- }, [onCheckoutComplete])
100
+ if (txStatus === "success") {
101
+ updatePersistentToast(
102
+ "Transaction Complete",
103
+ "Your transaction has been successfully processed",
104
+ "success",
105
+ )
106
+ } else {
107
+ // Default to error for any non-success status (including 'fail' and any unexpected values)
108
+ updatePersistentToast(
109
+ "Transaction Failed",
110
+ "Your transaction failed or was refunded",
111
+ "error",
112
+ )
113
+ }
114
+ },
115
+ [onCheckoutComplete],
116
+ )
104
117
 
105
118
  const triggerCheckoutError = useCallback(
106
119
  (error: string) => {
@@ -120,7 +133,10 @@ export function useCheckout({
120
133
 
121
134
  updatePersistentToast(
122
135
  "Transaction Failed",
123
- error || "An error occurred while processing your transaction",
136
+ getPrettifiedErrorMessage(
137
+ error,
138
+ "An error occurred while processing your transaction",
139
+ ),
124
140
  "error",
125
141
  )
126
142
  },
@@ -25,6 +25,7 @@ export type Screen =
25
25
  | "user-preferences"
26
26
  | "chain-list"
27
27
  | "disconnect"
28
+ | "home"
28
29
 
29
30
  interface CurrentScreenContextType {
30
31
  currentScreen: Screen
@@ -14,6 +14,7 @@ import { getChainInfo } from "../../chains.js"
14
14
  import { logger } from "../../logger.js"
15
15
 
16
16
  const MINIMUM_24H_VOLUME_USD = 1_000_000 // $1M
17
+ const MINIMUM_BALANCE_USD_FOR_PRIORITIZATION = 1 // $1
17
18
 
18
19
  /**
19
20
  * Hook for intelligent default token selection in 0xtrails widget.
@@ -33,9 +34,9 @@ const MINIMUM_24H_VOLUME_USD = 1_000_000 // $1M
33
34
  * - Fallback to any token with balance > 0
34
35
  *
35
36
  * Destination Token Logic (only when toToken/toChainId not provided):
36
- * - Matches origin token symbol on different chain when possible
37
- * - Default destination chain: Base (unless origin is Base, then Arbitrum)
38
- * - Fallback priority: USDC > ETH > first available token
37
+ * - Default destination chain: Base if origin is not Base, Arbitrum if origin is Base
38
+ * - Default destination token: USDC on destination chain
39
+ * - Fallback: Same symbol on destination chain > ETH on destination chain > first available token on destination chain
39
40
  *
40
41
  * @returns defaultOriginToken - Best origin token that can cover targetAmountUsd (if specified)
41
42
  * @returns defaultDestinationToken - Matching token on different chain (null if toToken/toChainId set)
@@ -231,8 +232,11 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
231
232
  }
232
233
 
233
234
  // Helper to sort tokens by balance USD and volume
234
- const sortTokensByBalanceAndVolume = (tokens: TokenBalanceExtended[]) => {
235
- return [...tokens].sort((a, b) => {
235
+ const sortTokensByBalanceAndVolume = (
236
+ tokens: TokenBalanceExtended[],
237
+ targetAmount?: number,
238
+ ) => {
239
+ const sortedTokens = [...tokens].sort((a, b) => {
236
240
  const aVolume = getToken24hVolume(a)
237
241
  const bVolume = getToken24hVolume(b)
238
242
  const aHighVolume = aVolume >= MINIMUM_24H_VOLUME_USD
@@ -247,6 +251,56 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
247
251
  const bBalanceUsd = b.balanceUsd ?? 0
248
252
  return bBalanceUsd - aBalanceUsd
249
253
  })
254
+
255
+ // After initial sorting, prioritize USDC and native tokens at the top
256
+ const effectiveTargetAmount =
257
+ targetAmount ?? MINIMUM_BALANCE_USD_FOR_PRIORITIZATION
258
+
259
+ const prioritizedTokens: TokenBalanceExtended[] = []
260
+ const remainingTokens: TokenBalanceExtended[] = []
261
+
262
+ // Separate tokens into prioritized and remaining
263
+ for (const token of sortedTokens) {
264
+ const balanceUsd = token.balanceUsd ?? 0
265
+ const isNative =
266
+ !("contractAddress" in token) || token.contractAddress === zeroAddress
267
+ const isUSDC =
268
+ !isNative && token.contractInfo?.symbol?.toUpperCase() === "USDC"
269
+
270
+ if ((isUSDC || isNative) && balanceUsd >= effectiveTargetAmount) {
271
+ prioritizedTokens.push(token)
272
+ } else {
273
+ remainingTokens.push(token)
274
+ }
275
+ }
276
+
277
+ // Sort prioritized tokens: USDC first, then native tokens
278
+ prioritizedTokens.sort((a, b) => {
279
+ const aIsNative =
280
+ !("contractAddress" in a) || a.contractAddress === zeroAddress
281
+ const bIsNative =
282
+ !("contractAddress" in b) || b.contractAddress === zeroAddress
283
+ const aIsUSDC =
284
+ !aIsNative && a.contractInfo?.symbol?.toUpperCase() === "USDC"
285
+ const bIsUSDC =
286
+ !bIsNative && b.contractInfo?.symbol?.toUpperCase() === "USDC"
287
+
288
+ // USDC comes first
289
+ if (aIsUSDC && !bIsUSDC) return -1
290
+ if (!aIsUSDC && bIsUSDC) return 1
291
+
292
+ // Then native tokens
293
+ if (aIsNative && !bIsNative) return -1
294
+ if (!aIsNative && bIsNative) return 1
295
+
296
+ // Within same type, sort by balance USD (highest first)
297
+ const aBalanceUsd = a.balanceUsd ?? 0
298
+ const bBalanceUsd = b.balanceUsd ?? 0
299
+ return bBalanceUsd - aBalanceUsd
300
+ })
301
+
302
+ // Return prioritized tokens first, then remaining tokens
303
+ return [...prioritizedTokens, ...remainingTokens]
250
304
  }
251
305
 
252
306
  // Find the best origin token using intelligent selection
@@ -281,7 +335,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
281
335
  const sameChainTokens = tokensWithSufficientBalance.filter(
282
336
  (token) => token.chainId === targetChainId,
283
337
  )
284
- const sortedSameChain = sortTokensByBalanceAndVolume(sameChainTokens)
338
+ const sortedSameChain = sortTokensByBalanceAndVolume(
339
+ sameChainTokens,
340
+ effectiveTargetAmount,
341
+ )
285
342
  bestOriginToken = sortedSameChain[0] ?? null
286
343
  if (bestOriginToken) {
287
344
  logger.console.log(
@@ -296,7 +353,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
296
353
  (token) =>
297
354
  tokenMatches(token, toToken) && token.chainId !== targetChainId,
298
355
  )
299
- const sortedSameToken = sortTokensByBalanceAndVolume(sameTokenDiffChain)
356
+ const sortedSameToken = sortTokensByBalanceAndVolume(
357
+ sameTokenDiffChain,
358
+ effectiveTargetAmount,
359
+ )
300
360
  bestOriginToken = sortedSameToken[0] ?? null
301
361
  if (bestOriginToken) {
302
362
  logger.console.log(
@@ -310,8 +370,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
310
370
  const diffChainDiffToken = tokensWithSufficientBalance.filter(
311
371
  (token) => token.chainId !== targetChainId,
312
372
  )
313
- const sortedDiffChainDiffToken =
314
- sortTokensByBalanceAndVolume(diffChainDiffToken)
373
+ const sortedDiffChainDiffToken = sortTokensByBalanceAndVolume(
374
+ diffChainDiffToken,
375
+ effectiveTargetAmount,
376
+ )
315
377
  bestOriginToken = sortedDiffChainDiffToken[0] ?? null
316
378
  if (bestOriginToken) {
317
379
  logger.console.log(
@@ -322,7 +384,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
322
384
 
323
385
  // Fallback: If no token can cover target amount, use any token (sorted by balance/volume)
324
386
  if (!bestOriginToken) {
325
- const allTokensSorted = sortTokensByBalanceAndVolume(tokensWithBalance)
387
+ const allTokensSorted = sortTokensByBalanceAndVolume(
388
+ tokensWithBalance,
389
+ effectiveTargetAmount,
390
+ )
326
391
  bestOriginToken = allTokensSorted[0] ?? null
327
392
  logger.console.log(
328
393
  "[trails-sdk] Fallback: No token can cover target amount, using highest balance token",
@@ -330,8 +395,10 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
330
395
  }
331
396
  } else {
332
397
  // When no specific destination is set, select highest value token with good liquidity
333
- const sortedByBalanceAndVolume =
334
- sortTokensByBalanceAndVolume(tokensWithBalance)
398
+ const sortedByBalanceAndVolume = sortTokensByBalanceAndVolume(
399
+ tokensWithBalance,
400
+ MINIMUM_BALANCE_USD_FOR_PRIORITIZATION,
401
+ ) // Use minimum balance for prioritization
335
402
  bestOriginToken = sortedByBalanceAndVolume[0] ?? null
336
403
  logger.console.log(
337
404
  "[trails-sdk] No destination specified, selected highest value token with best liquidity",
@@ -358,30 +425,39 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
358
425
  let destinationToken: DefaultToken | null = null
359
426
 
360
427
  if (shouldComputeDestination) {
361
- // Determine default destination chain: Base if origin is not Base, Arbitrum if origin is Base
428
+ // Determine destination chain: Base if origin is not Base, Arbitrum if origin is Base
362
429
  const defaultDestChainId =
363
430
  originToken.chainId === base.id ? arbitrum.id : base.id
364
431
 
365
- // Find matching token on destination chain (prefer same symbol)
366
- const destChainTokens = supportedTokens.filter(
367
- (token: any) => token.chainId === defaultDestChainId,
432
+ // Find USDC on destination chain first
433
+ const usdcOnDestChain = supportedTokens.find(
434
+ (token: any) =>
435
+ token.chainId === defaultDestChainId && token.symbol === "USDC",
368
436
  )
369
437
 
370
438
  let destToken: any = null
371
439
 
372
- // Try to find same symbol on dest chain
373
- const sameSymbolToken = destChainTokens.find((token: any) => {
374
- return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
375
- })
376
-
377
- if (sameSymbolToken) {
378
- destToken = sameSymbolToken
440
+ if (usdcOnDestChain) {
441
+ destToken = usdcOnDestChain
379
442
  } else {
380
- // Fallback: USDC > ETH > first available token
381
- destToken =
382
- destChainTokens.find((token: any) => token.symbol === "USDC") ||
383
- destChainTokens.find((token: any) => token.symbol === "ETH") ||
384
- destChainTokens[0]
443
+ // Fallback: Find matching token on destination chain
444
+ const destChainTokens = supportedTokens.filter(
445
+ (token: any) => token.chainId === defaultDestChainId,
446
+ )
447
+
448
+ // Try to find same symbol on destination chain first
449
+ const sameSymbolOnDestChain = destChainTokens.find((token: any) => {
450
+ return token.symbol.toUpperCase() === originToken.symbol.toUpperCase()
451
+ })
452
+
453
+ if (sameSymbolOnDestChain) {
454
+ destToken = sameSymbolOnDestChain
455
+ } else {
456
+ // Fallback: ETH on destination chain > first available token on destination chain
457
+ destToken =
458
+ destChainTokens.find((token: any) => token.symbol === "ETH") ||
459
+ destChainTokens[0]
460
+ }
385
461
  }
386
462
 
387
463
  const decimals = destToken?.decimals