0xtrails 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/dist/abis/trailsHydrate.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +41 -0
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/{ccip-62W6LwH2.js → ccip-Cg9-lJ6K.js} +16 -16
  5. package/dist/chainSwitch.d.ts.map +1 -1
  6. package/dist/chains.d.ts +9 -3
  7. package/dist/chains.d.ts.map +1 -1
  8. package/dist/error.d.ts +1 -0
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/{index-C0QTNYIA.js → index-DEojZg7b.js} +50431 -50424
  11. package/dist/index.d.ts +1 -3
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +377 -421
  14. package/dist/intentReceiptPoller.d.ts.map +1 -1
  15. package/dist/intents.d.ts +1 -3
  16. package/dist/intents.d.ts.map +1 -1
  17. package/dist/mutations.d.ts +1 -4
  18. package/dist/mutations.d.ts.map +1 -1
  19. package/dist/prepareSend.d.ts.map +1 -1
  20. package/dist/query/balance.hooks.d.ts.map +1 -1
  21. package/dist/query/chains.hooks.d.ts.map +1 -1
  22. package/dist/query/chains.queries.d.ts +4 -1
  23. package/dist/query/chains.queries.d.ts.map +1 -1
  24. package/dist/queryParams.d.ts.map +1 -1
  25. package/dist/recover.d.ts.map +1 -1
  26. package/dist/tokens.d.ts.map +1 -1
  27. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  28. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  29. package/dist/transactionIntent/deposits/standardDeposit.d.ts +1 -7
  30. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  31. package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
  32. package/dist/transactionIntent/helpers/transactionStateHelpers.d.ts +10 -1
  33. package/dist/transactionIntent/helpers/transactionStateHelpers.d.ts.map +1 -1
  34. package/dist/transactionIntent/types.d.ts +3 -6
  35. package/dist/transactionIntent/types.d.ts.map +1 -1
  36. package/dist/transactionIntent/utils/resilientDepositTracker.d.ts +3 -3
  37. package/dist/transactionIntent/utils/resilientDepositTracker.d.ts.map +1 -1
  38. package/dist/transactions.d.ts +2 -0
  39. package/dist/transactions.d.ts.map +1 -1
  40. package/dist/umd/trails.min.js +200 -200
  41. package/dist/walletUtils.d.ts +4 -0
  42. package/dist/walletUtils.d.ts.map +1 -1
  43. package/dist/wallets.d.ts +2 -1
  44. package/dist/wallets.d.ts.map +1 -1
  45. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  46. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -1
  47. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  48. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  49. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  50. package/dist/widget/components/ConnectedWallets.d.ts +5 -1
  51. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  52. package/dist/widget/components/DepositTracker.d.ts +1 -1
  53. package/dist/widget/components/DepositTracker.d.ts.map +1 -1
  54. package/dist/widget/components/DirectTransfer.d.ts +0 -8
  55. package/dist/widget/components/DirectTransfer.d.ts.map +1 -1
  56. package/dist/widget/components/Fund.d.ts +1 -1
  57. package/dist/widget/components/Fund.d.ts.map +1 -1
  58. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  59. package/dist/widget/components/FundWalletSelection.d.ts +0 -8
  60. package/dist/widget/components/FundWalletSelection.d.ts.map +1 -1
  61. package/dist/widget/components/FundingMethodSelectorButton.d.ts +1 -1
  62. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  63. package/dist/widget/components/MeldStepsFlow.d.ts.map +1 -1
  64. package/dist/widget/components/OnrampErrorScreen.d.ts.map +1 -1
  65. package/dist/widget/components/OnrampPaymentMethods.d.ts.map +1 -1
  66. package/dist/widget/components/OnrampProviderConfirmation.d.ts +0 -6
  67. package/dist/widget/components/OnrampProviderConfirmation.d.ts.map +1 -1
  68. package/dist/widget/components/Pay.d.ts.map +1 -1
  69. package/dist/widget/components/QrCode.d.ts.map +1 -1
  70. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  71. package/dist/widget/components/Receipt.d.ts.map +1 -1
  72. package/dist/widget/components/ReceiptRecoverableFunds.d.ts +25 -0
  73. package/dist/widget/components/ReceiptRecoverableFunds.d.ts.map +1 -0
  74. package/dist/widget/components/RecipientSelectorButton.d.ts +3 -1
  75. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  76. package/dist/widget/components/Recipients.d.ts.map +1 -1
  77. package/dist/widget/components/Swap.d.ts +2 -16
  78. package/dist/widget/components/Swap.d.ts.map +1 -1
  79. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  80. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  81. package/dist/widget/components/TransactionHistoryItem.d.ts.map +1 -1
  82. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  83. package/dist/widget/components/TruncatedTransactionHash.d.ts.map +1 -1
  84. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  85. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  86. package/dist/widget/components/WidgetProviders.d.ts.map +1 -1
  87. package/dist/widget/components/Withdraw.d.ts.map +1 -1
  88. package/dist/widget/css/compiled.css +1 -1
  89. package/dist/widget/hooks/useClickTracking.d.ts.map +1 -1
  90. package/dist/widget/hooks/useDebugScreens.d.ts +1 -10
  91. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  92. package/dist/widget/hooks/useDepositMonitor.d.ts +2 -4
  93. package/dist/widget/hooks/useDepositMonitor.d.ts.map +1 -1
  94. package/dist/widget/hooks/useExternalFundingReceiptSync.d.ts +11 -0
  95. package/dist/widget/hooks/useExternalFundingReceiptSync.d.ts.map +1 -0
  96. package/dist/widget/hooks/useIntentReceiptBalances.d.ts +16 -0
  97. package/dist/widget/hooks/useIntentReceiptBalances.d.ts.map +1 -0
  98. package/dist/widget/hooks/useQuote.d.ts +0 -4
  99. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  100. package/dist/widget/hooks/useScreenTracking.d.ts +2 -0
  101. package/dist/widget/hooks/useScreenTracking.d.ts.map +1 -0
  102. package/dist/widget/hooks/useSelectedFundMethod.d.ts +0 -2
  103. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -1
  104. package/dist/widget/hooks/useSendForm.d.ts +0 -4
  105. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  106. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  107. package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -1
  108. package/dist/widget/hooks/useViewManager.d.ts +89 -0
  109. package/dist/widget/hooks/useViewManager.d.ts.map +1 -0
  110. package/dist/widget/hooks/useWalletConnectUri.d.ts.map +1 -1
  111. package/dist/widget/hooks/useWalletConnectionContext.d.ts +1 -1
  112. package/dist/widget/hooks/useWalletConnectionContext.d.ts.map +1 -1
  113. package/dist/widget/index.d.ts +1 -1
  114. package/dist/widget/index.d.ts.map +1 -1
  115. package/dist/widget/index.js +7 -6
  116. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  117. package/dist/widget/types/commonProps.d.ts +1 -6
  118. package/dist/widget/types/commonProps.d.ts.map +1 -1
  119. package/dist/widget/utils/forexRateStore.d.ts.map +1 -1
  120. package/dist/widget/utils/fundMethodSwitchState.d.ts +10 -0
  121. package/dist/widget/utils/fundMethodSwitchState.d.ts.map +1 -0
  122. package/dist/widget/utils/localeStore.d.ts.map +1 -1
  123. package/dist/widget/utils/viewManagerGuards.d.ts +5 -0
  124. package/dist/widget/utils/viewManagerGuards.d.ts.map +1 -0
  125. package/dist/widget/widget.d.ts +23 -2
  126. package/dist/widget/widget.d.ts.map +1 -1
  127. package/package.json +2 -2
  128. package/src/abis/trailsHydrate.ts +2 -1
  129. package/src/analytics.ts +60 -0
  130. package/src/chainSwitch.ts +11 -8
  131. package/src/chains.ts +82 -37
  132. package/src/constants.ts +2 -2
  133. package/src/error.ts +8 -0
  134. package/src/index.ts +1 -12
  135. package/src/intentReceiptPoller.ts +27 -0
  136. package/src/intents.ts +36 -87
  137. package/src/mutations.ts +11 -102
  138. package/src/onramp-client/index.ts +3 -3
  139. package/src/prepareSend.ts +6 -2
  140. package/src/query/balance.hooks.ts +31 -10
  141. package/src/query/chains.hooks.ts +7 -1
  142. package/src/query/chains.queries.ts +8 -5
  143. package/src/queryParams.ts +8 -6
  144. package/src/recover.ts +9 -9
  145. package/src/tokens.ts +4 -2
  146. package/src/transactionIntent/deposits/depositOrchestrator.ts +0 -2
  147. package/src/transactionIntent/deposits/gaslessDeposit.ts +8 -0
  148. package/src/transactionIntent/deposits/standardDeposit.ts +25 -35
  149. package/src/transactionIntent/handlers/intentHandler.ts +234 -138
  150. package/src/transactionIntent/helpers/transactionStateHelpers.ts +108 -1
  151. package/src/transactionIntent/types.ts +14 -8
  152. package/src/transactionIntent/utils/resilientDepositTracker.ts +72 -183
  153. package/src/transactions.ts +16 -0
  154. package/src/walletUtils.ts +188 -1
  155. package/src/wallets.ts +50 -15
  156. package/src/widget/compiled.css +1 -1
  157. package/src/widget/components/AccountActionsDropdown.tsx +4 -6
  158. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +4 -6
  159. package/src/widget/components/AccountSettings.tsx +36 -22
  160. package/src/widget/components/ClassicSwap.tsx +67 -9
  161. package/src/widget/components/ConnectWallet.tsx +5 -7
  162. package/src/widget/components/ConnectedWallets.tsx +143 -82
  163. package/src/widget/components/DepositTracker.tsx +4 -5
  164. package/src/widget/components/DirectTransfer.tsx +85 -84
  165. package/src/widget/components/Earn.tsx +3 -3
  166. package/src/widget/components/Fund.tsx +90 -17
  167. package/src/widget/components/FundMethods.tsx +77 -43
  168. package/src/widget/components/FundWalletSelection.tsx +13 -397
  169. package/src/widget/components/FundingMethodSelectorButton.tsx +11 -10
  170. package/src/widget/components/MeldStepsFlow.tsx +64 -30
  171. package/src/widget/components/OnrampErrorScreen.tsx +2 -18
  172. package/src/widget/components/OnrampPaymentMethods.tsx +4 -6
  173. package/src/widget/components/OnrampProviderConfirmation.tsx +91 -110
  174. package/src/widget/components/OriginTransferInformation.tsx +2 -2
  175. package/src/widget/components/Pay.tsx +27 -7
  176. package/src/widget/components/PaymentMethods.tsx +10 -10
  177. package/src/widget/components/PoolDeposit.tsx +2 -2
  178. package/src/widget/components/QrCode.tsx +13 -11
  179. package/src/widget/components/QuoteDetails.tsx +16 -6
  180. package/src/widget/components/Receipt.tsx +66 -6
  181. package/src/widget/components/ReceiptRecoverableFunds.tsx +135 -0
  182. package/src/widget/components/RecipientSelectorButton.tsx +6 -17
  183. package/src/widget/components/Recipients.tsx +38 -29
  184. package/src/widget/components/Swap.tsx +2 -25
  185. package/src/widget/components/TokenList.tsx +2 -2
  186. package/src/widget/components/TokenSelector.tsx +11 -11
  187. package/src/widget/components/TokenSelectorButton.tsx +3 -3
  188. package/src/widget/components/TrailsHookModal.tsx +1 -1
  189. package/src/widget/components/TransactionDetails.tsx +43 -37
  190. package/src/widget/components/TransactionHistoryItem.tsx +1 -42
  191. package/src/widget/components/TransferPendingVertical.tsx +11 -4
  192. package/src/widget/components/TruncatedTransactionHash.tsx +5 -0
  193. package/src/widget/components/WalletConfirmation.tsx +5 -8
  194. package/src/widget/components/WalletConnect.tsx +6 -2
  195. package/src/widget/components/WidgetProviders.tsx +34 -43
  196. package/src/widget/components/Withdraw.tsx +25 -11
  197. package/src/widget/hooks/useClickTracking.ts +5 -0
  198. package/src/widget/hooks/useDebugScreens.ts +40 -86
  199. package/src/widget/hooks/useDepositMonitor.ts +14 -149
  200. package/src/widget/hooks/useExternalFundingReceiptSync.ts +79 -0
  201. package/src/widget/hooks/useIntentReceiptBalances.ts +141 -0
  202. package/src/widget/hooks/useQuote.ts +7 -16
  203. package/src/widget/hooks/useScreenTracking.ts +14 -0
  204. package/src/widget/hooks/useSelectedFundMethod.tsx +0 -5
  205. package/src/widget/hooks/useSendForm.ts +5 -14
  206. package/src/widget/hooks/useTokenList.ts +3 -16
  207. package/src/widget/hooks/useTrailsSendTransaction.ts +1 -5
  208. package/src/widget/hooks/useViewManager.tsx +505 -0
  209. package/src/widget/hooks/useWalletConnectUri.tsx +77 -18
  210. package/src/widget/hooks/useWalletConnectionContext.tsx +1 -1
  211. package/src/widget/index.tsx +1 -0
  212. package/src/widget/providers/TrailsProvider.tsx +0 -41
  213. package/src/widget/styles.ts +1 -1
  214. package/src/widget/types/commonProps.ts +0 -8
  215. package/src/widget/utils/forexRateStore.ts +0 -2
  216. package/src/widget/utils/fundMethodSwitchState.ts +25 -0
  217. package/src/widget/utils/localeStore.ts +0 -1
  218. package/src/widget/utils/viewManagerGuards.ts +49 -0
  219. package/src/widget/widget.tsx +405 -316
  220. package/dist/intentStorage.d.ts +0 -24
  221. package/dist/intentStorage.d.ts.map +0 -1
  222. package/dist/mode.d.ts +0 -2
  223. package/dist/mode.d.ts.map +0 -1
  224. package/dist/widget/hooks/useBack.d.ts +0 -22
  225. package/dist/widget/hooks/useBack.d.ts.map +0 -1
  226. package/dist/widget/hooks/useCurrentScreen.d.ts +0 -13
  227. package/dist/widget/hooks/useCurrentScreen.d.ts.map +0 -1
  228. package/dist/widget/hooks/useInitialRedirect.d.ts +0 -7
  229. package/dist/widget/hooks/useInitialRedirect.d.ts.map +0 -1
  230. package/dist/widget/hooks/useMode.d.ts +0 -20
  231. package/dist/widget/hooks/useMode.d.ts.map +0 -1
  232. package/dist/widget/hooks/usePreviousScreen.d.ts +0 -12
  233. package/dist/widget/hooks/usePreviousScreen.d.ts.map +0 -1
  234. package/dist/widget/workers/intentExecutionWorker.d.ts +0 -73
  235. package/dist/widget/workers/intentExecutionWorker.d.ts.map +0 -1
  236. package/src/intentStorage.ts +0 -106
  237. package/src/mode.ts +0 -1
  238. package/src/widget/hooks/useBack.tsx +0 -210
  239. package/src/widget/hooks/useCurrentScreen.tsx +0 -73
  240. package/src/widget/hooks/useInitialRedirect.tsx +0 -70
  241. package/src/widget/hooks/useMode.tsx +0 -51
  242. package/src/widget/hooks/usePreviousScreen.ts +0 -36
  243. package/src/widget/workers/intentExecutionWorker.ts +0 -502
@@ -0,0 +1,505 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useReducer,
8
+ useRef,
9
+ } from "react"
10
+ import type { ReactNode } from "react"
11
+ import { logger } from "../../logger.js"
12
+
13
+ export type Mode = "pay" | "fund" | "earn" | "swap" | "withdraw"
14
+
15
+ export type Screen =
16
+ | "connect"
17
+ | "tokens"
18
+ | "select-origin-token"
19
+ | "select-origin-amount"
20
+ | "send-form"
21
+ | "fund-form"
22
+ | "fund-methods"
23
+ | "payment-methods"
24
+ | "select-funding-wallet"
25
+ | "onramp-payment-methods"
26
+ | "recipients"
27
+ | "earn-pools"
28
+ | "earn"
29
+ | "swap"
30
+ | "withdraw"
31
+ | "wallet-confirmation"
32
+ | "qr-code-wallet-select"
33
+ | "direct-transfer-screen"
34
+ | "onramp-meld"
35
+ | "pending"
36
+ | "receipt"
37
+ | "onramp-mesh"
38
+ | "wallet-connect"
39
+ | "wallet-list"
40
+ | "wallet-connection-pending"
41
+ | "account-history"
42
+ | "account-settings"
43
+ | "user-preferences"
44
+ | "chain-list"
45
+ | "disconnect"
46
+ | "qrcode-options"
47
+ | "meld-history"
48
+ | "onramp-confirmation"
49
+
50
+ export interface NavigationEvent {
51
+ from: Screen
52
+ to: Screen
53
+ }
54
+
55
+ export type NavigationListener = (event: NavigationEvent) => void
56
+
57
+ export interface StackEntry {
58
+ screen: Screen
59
+ backTarget?: Screen
60
+ key: string
61
+ timestamp: number
62
+ }
63
+
64
+ export interface ViewState {
65
+ screen: Screen
66
+ mode: Mode
67
+ configuredMode: Mode
68
+ stack: StackEntry[]
69
+ direction: "forward" | "backward" | "none"
70
+ screenData: Record<string, unknown> | null
71
+ }
72
+
73
+ export type ViewAction =
74
+ | {
75
+ type: "NAVIGATE"
76
+ screen: Screen
77
+ backTarget?: Screen
78
+ data?: Record<string, unknown>
79
+ }
80
+ | { type: "GO_BACK" }
81
+ | { type: "SET_MODE"; mode: Mode }
82
+ | { type: "OVERRIDE_MODE"; mode: Mode }
83
+ | { type: "RESET_MODE" }
84
+ | { type: "GO_HOME" }
85
+ | { type: "RESET"; initialScreen?: Screen }
86
+ | { type: "GO_BACK_TO"; screen: Screen }
87
+ | { type: "REPLACE"; screen: Screen; data?: Record<string, unknown> }
88
+
89
+ const MAX_STACK_SIZE = 20
90
+
91
+ export function getInitialScreen(mode: Mode): Screen {
92
+ switch (mode) {
93
+ case "swap":
94
+ return "swap"
95
+ case "earn":
96
+ return "earn"
97
+ case "fund":
98
+ return "fund-methods"
99
+ case "pay":
100
+ return "send-form"
101
+ case "withdraw":
102
+ return "withdraw"
103
+ }
104
+ }
105
+
106
+ export function createEntryKey(): string {
107
+ if (
108
+ typeof crypto !== "undefined" &&
109
+ typeof crypto.randomUUID === "function"
110
+ ) {
111
+ return crypto.randomUUID().slice(0, 8)
112
+ }
113
+ return Math.random().toString(36).slice(2, 10)
114
+ }
115
+
116
+ function createEntry(screen: Screen, backTarget?: Screen): StackEntry {
117
+ return {
118
+ screen,
119
+ backTarget,
120
+ key: createEntryKey(),
121
+ timestamp: Date.now(),
122
+ }
123
+ }
124
+
125
+ export function viewReducer(state: ViewState, action: ViewAction): ViewState {
126
+ switch (action.type) {
127
+ case "NAVIGATE": {
128
+ const currentEntry = state.stack[state.stack.length - 1]
129
+ const nextData = action.data ?? null
130
+ if (
131
+ state.screen === action.screen &&
132
+ currentEntry?.backTarget === action.backTarget &&
133
+ state.screenData === nextData
134
+ ) {
135
+ return state
136
+ }
137
+
138
+ const entry = createEntry(action.screen, action.backTarget)
139
+
140
+ return {
141
+ ...state,
142
+ screen: action.screen,
143
+ stack: [...state.stack, entry].slice(-MAX_STACK_SIZE),
144
+ direction: "forward",
145
+ screenData: action.data ?? null,
146
+ }
147
+ }
148
+
149
+ case "GO_BACK": {
150
+ if (state.stack.length <= 1) return state
151
+
152
+ const currentEntry = state.stack[state.stack.length - 1]
153
+ if (currentEntry?.backTarget) {
154
+ const targetIndex = state.stack.findLastIndex(
155
+ (e, index) =>
156
+ e.screen === currentEntry.backTarget &&
157
+ index < state.stack.length - 1,
158
+ )
159
+ const newStack =
160
+ targetIndex >= 0
161
+ ? state.stack.slice(0, targetIndex + 1)
162
+ : state.stack.slice(0, -1)
163
+ const newScreen =
164
+ newStack[newStack.length - 1]?.screen ?? getInitialScreen(state.mode)
165
+
166
+ return {
167
+ ...state,
168
+ screen: newScreen,
169
+ stack: newStack,
170
+ direction: "backward",
171
+ screenData: null,
172
+ }
173
+ }
174
+
175
+ const newStack = state.stack.slice(0, -1)
176
+ const newScreen =
177
+ newStack[newStack.length - 1]?.screen ?? getInitialScreen(state.mode)
178
+
179
+ return {
180
+ ...state,
181
+ screen: newScreen,
182
+ stack: newStack,
183
+ direction: "backward",
184
+ screenData: null,
185
+ }
186
+ }
187
+
188
+ case "GO_BACK_TO": {
189
+ const targetIndex = state.stack.findLastIndex(
190
+ (e) => e.screen === action.screen,
191
+ )
192
+ if (targetIndex < 0) return state
193
+
194
+ const newStack = state.stack.slice(0, targetIndex + 1)
195
+
196
+ return {
197
+ ...state,
198
+ screen: action.screen,
199
+ stack: newStack,
200
+ direction: "backward",
201
+ screenData: null,
202
+ }
203
+ }
204
+
205
+ case "REPLACE": {
206
+ const nextData = action.data ?? null
207
+ if (state.screen === action.screen && state.screenData === nextData) {
208
+ return state
209
+ }
210
+
211
+ const newStack = [...state.stack]
212
+ if (newStack.length > 0) {
213
+ newStack[newStack.length - 1] = createEntry(action.screen)
214
+ } else {
215
+ newStack.push(createEntry(action.screen))
216
+ }
217
+
218
+ return {
219
+ ...state,
220
+ screen: action.screen,
221
+ stack: newStack,
222
+ direction: "none",
223
+ screenData: action.data ?? null,
224
+ }
225
+ }
226
+
227
+ case "SET_MODE": {
228
+ const initialScreen = getInitialScreen(action.mode)
229
+ return {
230
+ ...state,
231
+ mode: action.mode,
232
+ configuredMode: action.mode,
233
+ screen: initialScreen,
234
+ stack: [createEntry(initialScreen)],
235
+ direction: "none",
236
+ screenData: null,
237
+ }
238
+ }
239
+
240
+ case "OVERRIDE_MODE": {
241
+ const initialScreen = getInitialScreen(action.mode)
242
+ return {
243
+ ...state,
244
+ mode: action.mode,
245
+ screen: initialScreen,
246
+ stack: [createEntry(initialScreen)],
247
+ direction: "none",
248
+ screenData: null,
249
+ }
250
+ }
251
+
252
+ case "RESET_MODE": {
253
+ const initialScreen = getInitialScreen(state.configuredMode)
254
+ return {
255
+ ...state,
256
+ mode: state.configuredMode,
257
+ screen: initialScreen,
258
+ stack: [createEntry(initialScreen)],
259
+ direction: "none",
260
+ screenData: null,
261
+ }
262
+ }
263
+
264
+ case "GO_HOME": {
265
+ const homeScreen = getInitialScreen(state.mode)
266
+ return {
267
+ ...state,
268
+ screen: homeScreen,
269
+ stack: [createEntry(homeScreen)],
270
+ direction: "backward",
271
+ screenData: null,
272
+ }
273
+ }
274
+
275
+ case "RESET": {
276
+ const initialScreen =
277
+ action.initialScreen ?? getInitialScreen(state.configuredMode)
278
+ return {
279
+ ...state,
280
+ mode: state.configuredMode,
281
+ screen: initialScreen,
282
+ stack: [createEntry(initialScreen)],
283
+ direction: "none",
284
+ screenData: null,
285
+ }
286
+ }
287
+
288
+ default:
289
+ return state
290
+ }
291
+ }
292
+
293
+ export function createInitialState(configuredMode: Mode): ViewState {
294
+ const initialScreen = getInitialScreen(configuredMode)
295
+ return {
296
+ screen: initialScreen,
297
+ mode: configuredMode,
298
+ configuredMode,
299
+ stack: [createEntry(initialScreen)],
300
+ direction: "none",
301
+ screenData: null,
302
+ }
303
+ }
304
+
305
+ export interface ViewManagerContextValue {
306
+ state: ViewState
307
+ dispatch: React.Dispatch<ViewAction>
308
+ navigate: (
309
+ screen: Screen,
310
+ options?: { backTarget?: Screen; data?: Record<string, unknown> },
311
+ ) => void
312
+ goBack: () => void
313
+ goBackTo: (screen: Screen) => void
314
+ replace: (screen: Screen, data?: Record<string, unknown>) => void
315
+ goHome: () => void
316
+ reset: (initialScreen?: Screen) => void
317
+ overrideMode: (mode: Mode) => void
318
+ resetMode: () => void
319
+ subscribe: SubscribeFn
320
+ canGoBack: boolean
321
+ backScreen: Screen | null
322
+ direction: "forward" | "backward" | "none"
323
+ mode: Mode
324
+ configuredMode: Mode
325
+ screen: Screen
326
+ }
327
+
328
+ type SubscribeFn = (listener: NavigationListener) => () => void
329
+
330
+ const ViewManagerContext = createContext<ViewManagerContextValue | null>(null)
331
+
332
+ interface ViewManagerProviderProps {
333
+ children: ReactNode
334
+ configuredMode: Mode
335
+ }
336
+
337
+ export function ViewManagerProvider({
338
+ children,
339
+ configuredMode,
340
+ }: ViewManagerProviderProps) {
341
+ const [state, dispatch] = useReducer(
342
+ viewReducer,
343
+ configuredMode,
344
+ createInitialState,
345
+ )
346
+
347
+ const prevConfiguredModeRef = useRef(configuredMode)
348
+ useEffect(() => {
349
+ if (configuredMode === prevConfiguredModeRef.current) return
350
+ prevConfiguredModeRef.current = configuredMode
351
+ dispatch({ type: "SET_MODE", mode: configuredMode })
352
+ }, [configuredMode])
353
+
354
+ const listenersRef = useRef(new Set<NavigationListener>())
355
+ const prevScreenRef = useRef(state.screen)
356
+
357
+ useEffect(() => {
358
+ const prev = prevScreenRef.current
359
+ prevScreenRef.current = state.screen
360
+ if (prev !== state.screen) {
361
+ const event: NavigationEvent = { from: prev, to: state.screen }
362
+ for (const listener of listenersRef.current) {
363
+ try {
364
+ listener(event)
365
+ } catch (error) {
366
+ logger.console.error("[trails-sdk] Navigation listener error", error)
367
+ }
368
+ }
369
+ }
370
+ }, [state.screen])
371
+
372
+ const subscribe: SubscribeFn = useCallback((listener) => {
373
+ listenersRef.current.add(listener)
374
+ return () => {
375
+ listenersRef.current.delete(listener)
376
+ }
377
+ }, [])
378
+
379
+ const navigate = useCallback(
380
+ (
381
+ screen: Screen,
382
+ options?: { backTarget?: Screen; data?: Record<string, unknown> },
383
+ ) => {
384
+ dispatch({
385
+ type: "NAVIGATE",
386
+ screen,
387
+ backTarget: options?.backTarget,
388
+ data: options?.data,
389
+ })
390
+ },
391
+ [],
392
+ )
393
+
394
+ const goBack = useCallback(() => {
395
+ dispatch({ type: "GO_BACK" })
396
+ }, [])
397
+
398
+ const goBackTo = useCallback((screen: Screen) => {
399
+ dispatch({ type: "GO_BACK_TO", screen })
400
+ }, [])
401
+
402
+ const replace = useCallback(
403
+ (screen: Screen, data?: Record<string, unknown>) => {
404
+ dispatch({ type: "REPLACE", screen, data })
405
+ },
406
+ [],
407
+ )
408
+
409
+ const goHome = useCallback(() => {
410
+ dispatch({ type: "GO_HOME" })
411
+ }, [])
412
+
413
+ const reset = useCallback((initialScreen?: Screen) => {
414
+ dispatch({ type: "RESET", initialScreen })
415
+ }, [])
416
+
417
+ const overrideMode = useCallback(
418
+ (m: Mode) => dispatch({ type: "OVERRIDE_MODE", mode: m }),
419
+ [],
420
+ )
421
+
422
+ const resetMode = useCallback(() => dispatch({ type: "RESET_MODE" }), [])
423
+
424
+ const canGoBack = state.stack.length > 1
425
+
426
+ const backScreen = useMemo((): Screen | null => {
427
+ if (state.stack.length <= 1) return null
428
+
429
+ const currentEntry = state.stack[state.stack.length - 1]
430
+ if (currentEntry?.backTarget) {
431
+ return currentEntry.backTarget
432
+ }
433
+
434
+ const previousEntry = state.stack[state.stack.length - 2]
435
+ return previousEntry?.screen ?? null
436
+ }, [state.stack])
437
+
438
+ // biome-ignore lint/correctness/useExhaustiveDependencies: stable dispatch wrappers omitted intentionally
439
+ const value = useMemo<ViewManagerContextValue>(
440
+ () => ({
441
+ state,
442
+ dispatch,
443
+ navigate,
444
+ goBack,
445
+ goBackTo,
446
+ replace,
447
+ goHome,
448
+ reset,
449
+ overrideMode,
450
+ resetMode,
451
+ canGoBack,
452
+ backScreen,
453
+ direction: state.direction,
454
+ mode: state.mode,
455
+ configuredMode: state.configuredMode,
456
+ screen: state.screen,
457
+ subscribe,
458
+ }),
459
+ [state, canGoBack, backScreen],
460
+ )
461
+
462
+ return (
463
+ <ViewManagerContext.Provider value={value}>
464
+ {children}
465
+ </ViewManagerContext.Provider>
466
+ )
467
+ }
468
+
469
+ export function useViewManager(): ViewManagerContextValue {
470
+ const context = useContext(ViewManagerContext)
471
+ if (!context) {
472
+ throw new Error("useViewManager must be used within a ViewManagerProvider")
473
+ }
474
+ return context
475
+ }
476
+
477
+ export function useNavigationEffect(callback: NavigationListener) {
478
+ const { subscribe } = useViewManager()
479
+ const callbackRef = useRef(callback)
480
+ callbackRef.current = callback
481
+
482
+ useEffect(() => {
483
+ return subscribe((event) => callbackRef.current(event))
484
+ }, [subscribe])
485
+ }
486
+
487
+ export function useScreenGuard(
488
+ condition: boolean,
489
+ target: Screen,
490
+ options?: { backTarget?: Screen },
491
+ ): boolean {
492
+ const { replace, navigate } = useViewManager()
493
+
494
+ useEffect(() => {
495
+ if (!condition) return
496
+
497
+ if (options?.backTarget) {
498
+ navigate(target, { backTarget: options.backTarget })
499
+ } else {
500
+ replace(target)
501
+ }
502
+ }, [condition, target, options?.backTarget, replace, navigate])
503
+
504
+ return condition
505
+ }
@@ -1,12 +1,10 @@
1
1
  import { useEffect, useCallback, useRef, useState } from "react"
2
2
  import { useConnect, useConnectors, type Connector } from "wagmi"
3
- import { walletConnect } from "wagmi/connectors"
4
3
  import { useTrails } from "../providers/TrailsProvider.js"
5
4
  import { logger } from "../../logger.js"
6
5
 
7
- /** Connectors from useConnectors() have emitter; raw CreateConnectorFn from walletConnect() does not. */
8
6
  function getConnectorEmitter(
9
- connector: ReturnType<typeof walletConnect> | Connector,
7
+ connector: Connector,
10
8
  ): Connector["emitter"] | null {
11
9
  if (typeof connector === "object" && "emitter" in connector) {
12
10
  return connector.emitter
@@ -14,20 +12,22 @@ function getConnectorEmitter(
14
12
  return null
15
13
  }
16
14
 
17
- // Local singleton to avoid multiple Core initializations
18
- let wcConnectorSingleton: ReturnType<typeof walletConnect> | null = null
19
- function getWalletConnectConnector(projectId: string) {
20
- if (!wcConnectorSingleton) {
21
- logger.console.log(
22
- "[trails-sdk] [useWalletConnectUri] Creating new connector with projectId:",
23
- projectId,
24
- )
25
- wcConnectorSingleton = walletConnect({
26
- projectId: projectId,
27
- showQrModal: false,
28
- })
15
+ function getUriFromProvider(provider: unknown): string | null {
16
+ if (provider == null || typeof provider !== "object") {
17
+ return null
29
18
  }
30
- return wcConnectorSingleton
19
+
20
+ const providerRecord = provider as {
21
+ connector?: { uri?: unknown }
22
+ session?: { pairingTopic?: unknown }
23
+ }
24
+
25
+ const connectorUri = providerRecord.connector?.uri
26
+ if (typeof connectorUri === "string" && connectorUri.length > 0) {
27
+ return connectorUri
28
+ }
29
+
30
+ return null
31
31
  }
32
32
 
33
33
  /**
@@ -81,8 +81,13 @@ export function useWalletConnectUri(projectId?: string): {
81
81
 
82
82
  if ((status === "pending" || status === "success") && !force) return
83
83
 
84
- const makeConnector = () => getWalletConnectConnector(finalProjectId)
85
- const connector = wcConnectorFromConfig || makeConnector()
84
+ const connector = wcConnectorFromConfig
85
+ if (!connector) {
86
+ logger.console.log(
87
+ "[trails-sdk] [useWalletConnectUri] WalletConnect connector not ready yet",
88
+ )
89
+ return
90
+ }
86
91
 
87
92
  logger.console.log(
88
93
  "[trails-sdk] [useWalletConnectUri] wcConnectorFromConfig",
@@ -158,6 +163,18 @@ export function useWalletConnectUri(projectId?: string): {
158
163
  }
159
164
 
160
165
  await connect({ connector })
166
+
167
+ if (typeof connector.getProvider === "function") {
168
+ const provider = await connector.getProvider()
169
+ const providerUri = getUriFromProvider(provider)
170
+ if (providerUri) {
171
+ logger.console.log(
172
+ "[trails-sdk] [useWalletConnectUri] Setting URI from provider.connector.uri",
173
+ )
174
+ debouncedSetWcUri(providerUri)
175
+ setError(null)
176
+ }
177
+ }
161
178
  } catch (err) {
162
179
  logger.console.error(
163
180
  "[trails-sdk] [useWalletConnectUri] connect error",
@@ -204,6 +221,48 @@ export function useWalletConnectUri(projectId?: string): {
204
221
  }
205
222
  }, [wcUri, status, handleConnect])
206
223
 
224
+ useEffect(() => {
225
+ if (!wcConnectorFromConfig || wcUri || status !== "pending") {
226
+ return
227
+ }
228
+
229
+ let isCancelled = false
230
+ let attempts = 0
231
+
232
+ const tryReadPendingUri = async () => {
233
+ if (
234
+ isCancelled ||
235
+ typeof wcConnectorFromConfig.getProvider !== "function"
236
+ ) {
237
+ return
238
+ }
239
+
240
+ const provider = await wcConnectorFromConfig.getProvider()
241
+ const providerUri = getUriFromProvider(provider)
242
+ if (providerUri) {
243
+ logger.console.log(
244
+ "[trails-sdk] [useWalletConnectUri] Recovered URI during pending status",
245
+ )
246
+ debouncedSetWcUri(providerUri)
247
+ setError(null)
248
+ return
249
+ }
250
+
251
+ attempts += 1
252
+ if (attempts < 10) {
253
+ setTimeout(() => {
254
+ void tryReadPendingUri()
255
+ }, 300)
256
+ }
257
+ }
258
+
259
+ void tryReadPendingUri()
260
+
261
+ return () => {
262
+ isCancelled = true
263
+ }
264
+ }, [wcConnectorFromConfig, wcUri, status, debouncedSetWcUri])
265
+
207
266
  // Force QR code regeneration when wcUri is cleared
208
267
  useEffect(() => {
209
268
  if (!wcUri && status !== "pending" && status !== "success") {
@@ -5,7 +5,7 @@ import {
5
5
  useCallback,
6
6
  type ReactNode,
7
7
  } from "react"
8
- import type { Screen } from "./useCurrentScreen.js"
8
+ import type { Screen } from "./useViewManager.js"
9
9
  import { logger } from "../../logger.js"
10
10
 
11
11
  export type WalletConnectionPurpose =
@@ -3,6 +3,7 @@
3
3
  export type { TrailsWidgetProps, TrailsWidgetRef } from "./widget.js"
4
4
  export {
5
5
  createModalController,
6
+ WidgetFundMethod,
6
7
  TrailsWidget,
7
8
  } from "./widget.js"
8
9
 
@@ -20,11 +20,8 @@ import {
20
20
  } from "../../constants.js"
21
21
  import { declareCommitHash, updateGlobalConfig } from "../../config.js"
22
22
  import { updateProjectAccessKey } from "../../analytics.js"
23
- import { logger } from "../../logger.js"
24
23
  import { useSyncLoggerDebug } from "../../logger/useSyncLoggerDebug.js"
25
24
  import { getQueryParam } from "../../queryParams.js"
26
- import { IntentExecutionWorker } from "../workers/intentExecutionWorker.js"
27
- import { getTrailsClient } from "../../trailsClient.js"
28
25
 
29
26
  export type SequenceEnv = "prod" | "dev" | "local" | "cors-anywhere"
30
27
 
@@ -119,44 +116,6 @@ export const TrailsProvider = ({ children, config }: TrailsProviderProps) => {
119
116
  }
120
117
  }, [trailsConfig.trailsApiKey])
121
118
 
122
- // Initialize and start the intent execution worker (singleton)
123
- useEffect(() => {
124
- if (!trailsConfig.trailsApiKey) {
125
- logger.console.log(
126
- "[trails-sdk] No API key, skipping intent worker initialization",
127
- )
128
- return
129
- }
130
-
131
- logger.console.log(
132
- "[trails-sdk] Initializing intent execution worker singleton",
133
- )
134
-
135
- const trailsClient = getTrailsClient({
136
- apiKey: trailsConfig.trailsApiKey,
137
- hostname: trailsConfig.trailsApiUrl,
138
- })
139
-
140
- // Get or create the singleton instance
141
- const worker = IntentExecutionWorker.getInstance({
142
- trailsClient,
143
- trailsApiKey: trailsConfig.trailsApiKey,
144
- sequenceIndexerUrl: trailsConfig.sequenceIndexerUrl,
145
- enabled: true,
146
- })
147
-
148
- // Start the worker if not already running
149
- worker.start()
150
-
151
- // Note: We don't stop the worker on unmount since it's a singleton
152
- // It will keep running across component remounts
153
- // Only destroy it when the entire app unmounts (optional)
154
- }, [
155
- trailsConfig.trailsApiKey,
156
- trailsConfig.trailsApiUrl,
157
- trailsConfig.sequenceIndexerUrl,
158
- ])
159
-
160
119
  const sequenceHooksConfig = useMemo(() => {
161
120
  return {
162
121
  projectAccessKey: trailsConfig.trailsApiKey || "",