@spacesops/wdk-react-native-core 1.0.0-beta.40

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 (275) hide show
  1. package/README.md +551 -0
  2. package/dist/__mocks__/react.d.ts +40 -0
  3. package/dist/__mocks__/react.js +33 -0
  4. package/dist/__mocks__/react.js.map +1 -0
  5. package/dist/__mocks__/secureStorage.d.ts +26 -0
  6. package/dist/__mocks__/secureStorage.js +80 -0
  7. package/dist/__mocks__/secureStorage.js.map +1 -0
  8. package/dist/__tests__/hooks/useBalance.test.d.ts +6 -0
  9. package/dist/__tests__/hooks/useBalance.test.js +217 -0
  10. package/dist/__tests__/hooks/useBalance.test.js.map +1 -0
  11. package/dist/__tests__/hooks/useWallet.test.d.ts +6 -0
  12. package/dist/__tests__/hooks/useWallet.test.js +105 -0
  13. package/dist/__tests__/hooks/useWallet.test.js.map +1 -0
  14. package/dist/__tests__/hooks/useWalletManager.test.d.ts +6 -0
  15. package/dist/__tests__/hooks/useWalletManager.test.js +193 -0
  16. package/dist/__tests__/hooks/useWalletManager.test.js.map +1 -0
  17. package/dist/__tests__/hooks/useWdkApp.test.d.ts +6 -0
  18. package/dist/__tests__/hooks/useWdkApp.test.js +55 -0
  19. package/dist/__tests__/hooks/useWdkApp.test.js.map +1 -0
  20. package/dist/__tests__/hooks/useWorklet.test.d.ts +6 -0
  21. package/dist/__tests__/hooks/useWorklet.test.js +168 -0
  22. package/dist/__tests__/hooks/useWorklet.test.js.map +1 -0
  23. package/dist/__tests__/provider/WdkAppProvider.test.d.ts +6 -0
  24. package/dist/__tests__/provider/WdkAppProvider.test.js +57 -0
  25. package/dist/__tests__/provider/WdkAppProvider.test.js.map +1 -0
  26. package/dist/__tests__/services/accountService.test.d.ts +6 -0
  27. package/dist/__tests__/services/accountService.test.js +216 -0
  28. package/dist/__tests__/services/accountService.test.js.map +1 -0
  29. package/dist/__tests__/services/addressService.test.d.ts +6 -0
  30. package/dist/__tests__/services/addressService.test.js +246 -0
  31. package/dist/__tests__/services/addressService.test.js.map +1 -0
  32. package/dist/__tests__/services/balanceService.test.d.ts +6 -0
  33. package/dist/__tests__/services/balanceService.test.js +333 -0
  34. package/dist/__tests__/services/balanceService.test.js.map +1 -0
  35. package/dist/__tests__/services/walletSetupService.test.d.ts +6 -0
  36. package/dist/__tests__/services/walletSetupService.test.js +304 -0
  37. package/dist/__tests__/services/walletSetupService.test.js.map +1 -0
  38. package/dist/__tests__/services/walletSwitchingService.test.d.ts +6 -0
  39. package/dist/__tests__/services/walletSwitchingService.test.js +209 -0
  40. package/dist/__tests__/services/walletSwitchingService.test.js.map +1 -0
  41. package/dist/__tests__/services/workletLifecycleService.test.d.ts +6 -0
  42. package/dist/__tests__/services/workletLifecycleService.test.js +451 -0
  43. package/dist/__tests__/services/workletLifecycleService.test.js.map +1 -0
  44. package/dist/__tests__/setup.d.ts +7 -0
  45. package/dist/__tests__/setup.js +49 -0
  46. package/dist/__tests__/setup.js.map +1 -0
  47. package/dist/__tests__/storage/mmkvStorage.test.d.ts +6 -0
  48. package/dist/__tests__/storage/mmkvStorage.test.js +213 -0
  49. package/dist/__tests__/storage/mmkvStorage.test.js.map +1 -0
  50. package/dist/__tests__/store/walletStore.test.d.ts +4 -0
  51. package/dist/__tests__/store/walletStore.test.js +86 -0
  52. package/dist/__tests__/store/walletStore.test.js.map +1 -0
  53. package/dist/__tests__/store/workletStore.test.d.ts +4 -0
  54. package/dist/__tests__/store/workletStore.test.js +255 -0
  55. package/dist/__tests__/store/workletStore.test.js.map +1 -0
  56. package/dist/__tests__/types/hrpc.test.d.ts +4 -0
  57. package/dist/__tests__/types/hrpc.test.js +99 -0
  58. package/dist/__tests__/types/hrpc.test.js.map +1 -0
  59. package/dist/__tests__/utils/balanceUtils.test.d.ts +4 -0
  60. package/dist/__tests__/utils/balanceUtils.test.js +133 -0
  61. package/dist/__tests__/utils/balanceUtils.test.js.map +1 -0
  62. package/dist/__tests__/utils/errorHandling.test.d.ts +6 -0
  63. package/dist/__tests__/utils/errorHandling.test.js +119 -0
  64. package/dist/__tests__/utils/errorHandling.test.js.map +1 -0
  65. package/dist/__tests__/utils/errorUtils.test.d.ts +4 -0
  66. package/dist/__tests__/utils/errorUtils.test.js +133 -0
  67. package/dist/__tests__/utils/errorUtils.test.js.map +1 -0
  68. package/dist/__tests__/utils/initializationState.test.d.ts +6 -0
  69. package/dist/__tests__/utils/initializationState.test.js +243 -0
  70. package/dist/__tests__/utils/initializationState.test.js.map +1 -0
  71. package/dist/__tests__/utils/jsonUtils.test.d.ts +4 -0
  72. package/dist/__tests__/utils/jsonUtils.test.js +118 -0
  73. package/dist/__tests__/utils/jsonUtils.test.js.map +1 -0
  74. package/dist/__tests__/utils/mmkvKeyManager.test.d.ts +6 -0
  75. package/dist/__tests__/utils/mmkvKeyManager.test.js +158 -0
  76. package/dist/__tests__/utils/mmkvKeyManager.test.js.map +1 -0
  77. package/dist/__tests__/utils/mnemonicUtils.test.d.ts +4 -0
  78. package/dist/__tests__/utils/mnemonicUtils.test.js +40 -0
  79. package/dist/__tests__/utils/mnemonicUtils.test.js.map +1 -0
  80. package/dist/__tests__/utils/raceConditions.test.d.ts +6 -0
  81. package/dist/__tests__/utils/raceConditions.test.js +239 -0
  82. package/dist/__tests__/utils/raceConditions.test.js.map +1 -0
  83. package/dist/__tests__/utils/result.test.d.ts +4 -0
  84. package/dist/__tests__/utils/result.test.js +66 -0
  85. package/dist/__tests__/utils/result.test.js.map +1 -0
  86. package/dist/__tests__/utils/schemas.test.d.ts +4 -0
  87. package/dist/__tests__/utils/schemas.test.js +66 -0
  88. package/dist/__tests__/utils/schemas.test.js.map +1 -0
  89. package/dist/__tests__/utils/storeHelpers.test.d.ts +4 -0
  90. package/dist/__tests__/utils/storeHelpers.test.js +227 -0
  91. package/dist/__tests__/utils/storeHelpers.test.js.map +1 -0
  92. package/dist/__tests__/utils/typeGuards.test.d.ts +4 -0
  93. package/dist/__tests__/utils/typeGuards.test.js +175 -0
  94. package/dist/__tests__/utils/typeGuards.test.js.map +1 -0
  95. package/dist/__tests__/utils/validation.test.d.ts +4 -0
  96. package/dist/__tests__/utils/validation.test.js +126 -0
  97. package/dist/__tests__/utils/validation.test.js.map +1 -0
  98. package/dist/__tests__/utils/walletUtils.test.d.ts +4 -0
  99. package/dist/__tests__/utils/walletUtils.test.js +156 -0
  100. package/dist/__tests__/utils/walletUtils.test.js.map +1 -0
  101. package/dist/hooks/useBalance.d.ts +170 -0
  102. package/dist/hooks/useBalance.js +442 -0
  103. package/dist/hooks/useBalance.js.map +1 -0
  104. package/dist/hooks/useWallet.d.ts +43 -0
  105. package/dist/hooks/useWallet.js +193 -0
  106. package/dist/hooks/useWallet.js.map +1 -0
  107. package/dist/hooks/useWalletManager.d.ts +100 -0
  108. package/dist/hooks/useWalletManager.js +526 -0
  109. package/dist/hooks/useWalletManager.js.map +1 -0
  110. package/dist/hooks/useWdkApp.d.ts +66 -0
  111. package/dist/hooks/useWdkApp.js +74 -0
  112. package/dist/hooks/useWdkApp.js.map +1 -0
  113. package/dist/hooks/useWorklet.d.ts +66 -0
  114. package/dist/hooks/useWorklet.js +42 -0
  115. package/dist/hooks/useWorklet.js.map +1 -0
  116. package/dist/index.d.ts +33 -0
  117. package/dist/index.js +36 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/provider/WdkAppProvider.d.ts +112 -0
  120. package/dist/provider/WdkAppProvider.js +558 -0
  121. package/dist/provider/WdkAppProvider.js.map +1 -0
  122. package/dist/services/accountService.d.ts +52 -0
  123. package/dist/services/accountService.js +119 -0
  124. package/dist/services/accountService.js.map +1 -0
  125. package/dist/services/addressService.d.ts +22 -0
  126. package/dist/services/addressService.js +110 -0
  127. package/dist/services/addressService.js.map +1 -0
  128. package/dist/services/balanceService.d.ts +114 -0
  129. package/dist/services/balanceService.js +208 -0
  130. package/dist/services/balanceService.js.map +1 -0
  131. package/dist/services/walletSetupService.d.ts +217 -0
  132. package/dist/services/walletSetupService.js +535 -0
  133. package/dist/services/walletSetupService.js.map +1 -0
  134. package/dist/services/walletSwitchingService.d.ts +48 -0
  135. package/dist/services/walletSwitchingService.js +143 -0
  136. package/dist/services/walletSwitchingService.js.map +1 -0
  137. package/dist/services/workletLifecycleService.d.ts +117 -0
  138. package/dist/services/workletLifecycleService.js +433 -0
  139. package/dist/services/workletLifecycleService.js.map +1 -0
  140. package/dist/storage/mmkvStorage.d.ts +44 -0
  141. package/dist/storage/mmkvStorage.js +348 -0
  142. package/dist/storage/mmkvStorage.js.map +1 -0
  143. package/dist/store/walletStore.d.ts +130 -0
  144. package/dist/store/walletStore.js +204 -0
  145. package/dist/store/walletStore.js.map +1 -0
  146. package/dist/store/workletStore.d.ts +121 -0
  147. package/dist/store/workletStore.js +256 -0
  148. package/dist/store/workletStore.js.map +1 -0
  149. package/dist/types/hrpc.d.ts +60 -0
  150. package/dist/types/hrpc.js +26 -0
  151. package/dist/types/hrpc.js.map +1 -0
  152. package/dist/types.d.ts +168 -0
  153. package/dist/types.js +7 -0
  154. package/dist/types.js.map +1 -0
  155. package/dist/utils/balanceUtils.d.ts +30 -0
  156. package/dist/utils/balanceUtils.js +86 -0
  157. package/dist/utils/balanceUtils.js.map +1 -0
  158. package/dist/utils/constants.d.ts +66 -0
  159. package/dist/utils/constants.js +73 -0
  160. package/dist/utils/constants.js.map +1 -0
  161. package/dist/utils/errorHandling.d.ts +47 -0
  162. package/dist/utils/errorHandling.js +54 -0
  163. package/dist/utils/errorHandling.js.map +1 -0
  164. package/dist/utils/errorUtils.d.ts +89 -0
  165. package/dist/utils/errorUtils.js +264 -0
  166. package/dist/utils/errorUtils.js.map +1 -0
  167. package/dist/utils/initializationState.d.ts +129 -0
  168. package/dist/utils/initializationState.js +220 -0
  169. package/dist/utils/initializationState.js.map +1 -0
  170. package/dist/utils/jsonUtils.d.ts +21 -0
  171. package/dist/utils/jsonUtils.js +67 -0
  172. package/dist/utils/jsonUtils.js.map +1 -0
  173. package/dist/utils/logger.d.ts +27 -0
  174. package/dist/utils/logger.js +50 -0
  175. package/dist/utils/logger.js.map +1 -0
  176. package/dist/utils/mmkvKeyManager.d.ts +57 -0
  177. package/dist/utils/mmkvKeyManager.js +214 -0
  178. package/dist/utils/mmkvKeyManager.js.map +1 -0
  179. package/dist/utils/mnemonicUtils.d.ts +17 -0
  180. package/dist/utils/mnemonicUtils.js +28 -0
  181. package/dist/utils/mnemonicUtils.js.map +1 -0
  182. package/dist/utils/operationMutex.d.ts +69 -0
  183. package/dist/utils/operationMutex.js +150 -0
  184. package/dist/utils/operationMutex.js.map +1 -0
  185. package/dist/utils/result.d.ts +34 -0
  186. package/dist/utils/result.js +43 -0
  187. package/dist/utils/result.js.map +1 -0
  188. package/dist/utils/schemas.d.ts +365 -0
  189. package/dist/utils/schemas.js +152 -0
  190. package/dist/utils/schemas.js.map +1 -0
  191. package/dist/utils/storeHelpers.d.ts +112 -0
  192. package/dist/utils/storeHelpers.js +198 -0
  193. package/dist/utils/storeHelpers.js.map +1 -0
  194. package/dist/utils/typeGuards.d.ts +70 -0
  195. package/dist/utils/typeGuards.js +95 -0
  196. package/dist/utils/typeGuards.js.map +1 -0
  197. package/dist/utils/validation.d.ts +53 -0
  198. package/dist/utils/validation.js +152 -0
  199. package/dist/utils/validation.js.map +1 -0
  200. package/dist/utils/walletStateHelpers.d.ts +62 -0
  201. package/dist/utils/walletStateHelpers.js +81 -0
  202. package/dist/utils/walletStateHelpers.js.map +1 -0
  203. package/dist/utils/walletUtils.d.ts +38 -0
  204. package/dist/utils/walletUtils.js +87 -0
  205. package/dist/utils/walletUtils.js.map +1 -0
  206. package/package.json +79 -0
  207. package/src/__mocks__/react.ts +34 -0
  208. package/src/__mocks__/secureStorage.ts +87 -0
  209. package/src/__tests__/hooks/useBalance.test.ts +286 -0
  210. package/src/__tests__/hooks/useWallet.test.ts +122 -0
  211. package/src/__tests__/hooks/useWalletManager.test.ts +271 -0
  212. package/src/__tests__/hooks/useWdkApp.test.tsx +59 -0
  213. package/src/__tests__/hooks/useWorklet.test.ts +195 -0
  214. package/src/__tests__/provider/WdkAppProvider.test.tsx +67 -0
  215. package/src/__tests__/services/accountService.test.ts +337 -0
  216. package/src/__tests__/services/addressService.test.ts +312 -0
  217. package/src/__tests__/services/balanceService.test.ts +411 -0
  218. package/src/__tests__/services/walletSetupService.test.ts +463 -0
  219. package/src/__tests__/services/walletSwitchingService.test.ts +258 -0
  220. package/src/__tests__/services/workletLifecycleService.test.ts +514 -0
  221. package/src/__tests__/setup.ts +54 -0
  222. package/src/__tests__/storage/mmkvStorage.test.ts +325 -0
  223. package/src/__tests__/store/walletStore.test.ts +101 -0
  224. package/src/__tests__/store/workletStore.test.ts +329 -0
  225. package/src/__tests__/types/hrpc.test.ts +125 -0
  226. package/src/__tests__/utils/balanceUtils.test.ts +151 -0
  227. package/src/__tests__/utils/errorHandling.test.ts +147 -0
  228. package/src/__tests__/utils/errorUtils.test.ts +170 -0
  229. package/src/__tests__/utils/initializationState.test.ts +337 -0
  230. package/src/__tests__/utils/jsonUtils.test.ts +131 -0
  231. package/src/__tests__/utils/mmkvKeyManager.test.ts +205 -0
  232. package/src/__tests__/utils/mnemonicUtils.test.ts +48 -0
  233. package/src/__tests__/utils/raceConditions.test.ts +292 -0
  234. package/src/__tests__/utils/result.test.ts +73 -0
  235. package/src/__tests__/utils/schemas.test.ts +82 -0
  236. package/src/__tests__/utils/storeHelpers.test.ts +323 -0
  237. package/src/__tests__/utils/typeGuards.test.ts +209 -0
  238. package/src/__tests__/utils/validation.test.ts +152 -0
  239. package/src/__tests__/utils/walletUtils.test.ts +195 -0
  240. package/src/hooks/useBalance.ts +597 -0
  241. package/src/hooks/useWallet.ts +288 -0
  242. package/src/hooks/useWalletManager.ts +654 -0
  243. package/src/hooks/useWdkApp.ts +78 -0
  244. package/src/hooks/useWorklet.ts +109 -0
  245. package/src/index.ts +112 -0
  246. package/src/provider/WdkAppProvider.tsx +764 -0
  247. package/src/services/accountService.ts +134 -0
  248. package/src/services/addressService.ts +125 -0
  249. package/src/services/balanceService.ts +291 -0
  250. package/src/services/walletSetupService.ts +695 -0
  251. package/src/services/walletSwitchingService.ts +157 -0
  252. package/src/services/workletLifecycleService.ts +561 -0
  253. package/src/storage/mmkvStorage.ts +417 -0
  254. package/src/store/walletStore.ts +301 -0
  255. package/src/store/workletStore.ts +340 -0
  256. package/src/types/hrpc.ts +78 -0
  257. package/src/types.ts +188 -0
  258. package/src/utils/balanceUtils.ts +96 -0
  259. package/src/utils/constants.ts +84 -0
  260. package/src/utils/errorHandling.md +163 -0
  261. package/src/utils/errorHandling.ts +61 -0
  262. package/src/utils/errorUtils.ts +300 -0
  263. package/src/utils/initializationState.ts +242 -0
  264. package/src/utils/jsonUtils.ts +75 -0
  265. package/src/utils/logger.ts +54 -0
  266. package/src/utils/mmkvKeyManager.ts +256 -0
  267. package/src/utils/mnemonicUtils.ts +35 -0
  268. package/src/utils/operationMutex.ts +181 -0
  269. package/src/utils/result.ts +57 -0
  270. package/src/utils/schemas.ts +190 -0
  271. package/src/utils/storeHelpers.ts +236 -0
  272. package/src/utils/typeGuards.ts +129 -0
  273. package/src/utils/validation.ts +174 -0
  274. package/src/utils/walletStateHelpers.ts +104 -0
  275. package/src/utils/walletUtils.ts +107 -0
@@ -0,0 +1,764 @@
1
+ /**
2
+ * WdkAppProvider
3
+ *
4
+ * App-level orchestration provider that manages initialization state.
5
+ * Provides app-level status and initialization state via useWdkApp() hook.
6
+ *
7
+ * Responsibilities:
8
+ * - Manages worklet initialization state
9
+ * - Manages wallet initialization state
10
+ * - Provides combined app status (isReady, isInitializing, error)
11
+ * - Sets up QueryClientProvider for TanStack Query
12
+ * - Sets up SecureStorage
13
+ * - Handles AppState listeners for security
14
+ *
15
+ * Architecture:
16
+ * - Worklet state: Managed by workletStore (global, initialized once)
17
+ * - Wallet state: Managed by separate state machine (per-identifier, can load multiple)
18
+ * - Combined status: Derived from both worklet and wallet states
19
+ */
20
+
21
+ import React, { createContext, useCallback, useEffect, useMemo, useRef } from 'react'
22
+ import { AppState, type AppStateStatus } from 'react-native'
23
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
24
+
25
+ import { createSecureStorage } from '@tetherto/wdk-react-native-secure-storage'
26
+
27
+ import { useWalletManager } from '../hooks/useWalletManager'
28
+ import { useWorklet } from '../hooks/useWorklet'
29
+ import { getWalletStore } from '../store/walletStore'
30
+ import type { WalletStore } from '../store/walletStore'
31
+ import {
32
+ updateWalletLoadingState,
33
+ getWalletIdFromLoadingState,
34
+ isWalletLoadingState,
35
+ isWalletErrorState,
36
+ } from '../store/walletStore'
37
+ import {
38
+ shouldResetToNotLoaded,
39
+ getWalletSwitchDecision,
40
+ shouldMarkWalletAsReady,
41
+ shouldHandleError,
42
+ } from '../utils/walletStateHelpers'
43
+ import { clearAllSensitiveData } from '../store/workletStore'
44
+ import { WalletSetupService } from '../services/walletSetupService'
45
+ import { WorkletLifecycleService } from '../services/workletLifecycleService'
46
+ import { normalizeError } from '../utils/errorUtils'
47
+ import { log, logError } from '../utils/logger'
48
+ import { validateNetworkConfigs, validateTokenConfigs } from '../utils/validation'
49
+ import { DEFAULT_QUERY_STALE_TIME_MS, DEFAULT_QUERY_GC_TIME_MS } from '../utils/constants'
50
+ import { InitializationStatus, AppStatus, isAppReadyStatus, isAppInProgressStatus, getCombinedStatus, getWorkletStatus } from '../utils/initializationState'
51
+ import type { NetworkConfigs, TokenConfigs } from '../types'
52
+
53
+
54
+
55
+ /**
56
+ * Context state exposed to consumers
57
+ *
58
+ * Purpose: App-level initialization state only. For wallet operations, use useWallet().
59
+ * For wallet lifecycle (create, load, import, delete), use useWalletManager().
60
+ *
61
+ * API Design:
62
+ * - `status`: Combined status for convenience (most common use case: "is app ready?")
63
+ * - `workletState`: Separate worklet state for flexibility (check worklet-specific conditions)
64
+ * - `walletState`: Separate wallet state for flexibility (check wallet-specific conditions)
65
+ *
66
+ * For simple cases, use `status`. For advanced cases needing granular control,
67
+ * use `workletState` and `walletState` directly.
68
+ */
69
+ export interface WdkAppContextValue {
70
+ /**
71
+ * Combined app status (convenience helper)
72
+ * Derived from workletState and walletState.
73
+ * Use this for common "is app ready?" checks.
74
+ */
75
+ status: AppStatus
76
+
77
+ /**
78
+ * Worklet initialization status (global, initialized once)
79
+ * Use this when you need to check worklet initialization state specifically.
80
+ */
81
+ workletStatus: InitializationStatus
82
+
83
+ /**
84
+ * Worklet state (global, initialized once)
85
+ * Use this when you need to check worklet-specific conditions.
86
+ */
87
+ workletState: {
88
+ /** Worklet is ready (started, not loading, no error) */
89
+ isReady: boolean
90
+ /** Worklet is currently loading */
91
+ isLoading: boolean
92
+ /** Worklet error message (null if no error) */
93
+ error: string | null
94
+ }
95
+
96
+ /**
97
+ * Wallet state (per-identifier, can load multiple wallets)
98
+ * Use this when you need to check wallet-specific conditions.
99
+ */
100
+ walletState: {
101
+ /** Current wallet state */
102
+ status: 'not_loaded' | 'checking' | 'loading' | 'ready' | 'error'
103
+ /** Wallet identifier being operated on (null if not_loaded) */
104
+ identifier: string | null
105
+ /** Wallet error (null if no error) */
106
+ error: Error | null
107
+ }
108
+
109
+ /** Initialization in progress (convenience getter, equivalent to isInProgressStatus(status)) */
110
+ isInitializing: boolean
111
+ /** App is ready (convenience getter, equivalent to isReadyStatus(status)) */
112
+ isReady: boolean
113
+
114
+ /** Currently active wallet identifier from walletStore (null if no wallet is loaded) */
115
+ activeWalletId: string | null
116
+ /** Wallet identifier being loaded (transient, only during loading operations) */
117
+ loadingWalletId: string | null
118
+ /** Whether the wallet being loaded exists in secure storage (null = not checked yet, true = exists, false = doesn't exist) */
119
+ walletExists: boolean | null
120
+ /** Initialization error if any (worklet or wallet error) - convenience getter */
121
+ error: Error | null
122
+
123
+ /** Retry initialization after an error */
124
+ retry: () => void
125
+ }
126
+
127
+ const WdkAppContext = createContext<WdkAppContextValue | null>(null)
128
+
129
+ /**
130
+ * Provider props
131
+ */
132
+ export interface WdkAppProviderProps {
133
+ /** Network configurations */
134
+ networkConfigs: NetworkConfigs
135
+ /** Token configurations for balance fetching */
136
+ tokenConfigs: TokenConfigs
137
+ /** Enable automatic wallet initialization on app restart (default: true) */
138
+ enableAutoInitialization?: boolean
139
+ /**
140
+ * Current user's identifier (typically email)
141
+ * Auto-initialization will NOT proceed if:
142
+ * - currentUserId is undefined/null (user identity not yet confirmed)
143
+ * - currentUserId doesn't match activeWalletId (wrong user's wallet)
144
+ *
145
+ * This prevents initializing with wrong user's wallet during account switches
146
+ */
147
+ currentUserId?: string | null
148
+ /** Child components (app content) */
149
+ children: React.ReactNode
150
+ }
151
+
152
+ /**
153
+ * WdkAppProvider - Orchestrates WDK initialization flow
154
+ *
155
+ * Composes useWorklet and useWalletManager hooks into a unified initialization flow.
156
+ * Automatically fetches balances when wallet is ready.
157
+ */
158
+ /**
159
+ * Create QueryClient singleton for TanStack Query
160
+ * This is created once and reused across the app lifecycle
161
+ */
162
+ const queryClient = new QueryClient({
163
+ defaultOptions: {
164
+ queries: {
165
+ retry: 1,
166
+ staleTime: DEFAULT_QUERY_STALE_TIME_MS,
167
+ gcTime: DEFAULT_QUERY_GC_TIME_MS,
168
+ },
169
+ },
170
+ })
171
+
172
+ // Custom deep equality for walletLoadingState comparison
173
+ // Defined outside component to prevent recreation on every render
174
+ const deepEqualityFn = (a: any, b: any) => {
175
+ if (a === b) return true
176
+ if (!a || !b) return false
177
+ if (typeof a !== 'object' || typeof b !== 'object') return a === b
178
+
179
+ const keysA = Object.keys(a)
180
+ const keysB = Object.keys(b)
181
+ if (keysA.length !== keysB.length) return false
182
+
183
+ for (const key of keysA) {
184
+ if (!keysB.includes(key)) return false
185
+ if (a[key] !== b[key]) return false
186
+ }
187
+ return true
188
+ }
189
+
190
+ export function WdkAppProvider({
191
+ networkConfigs,
192
+ tokenConfigs,
193
+ enableAutoInitialization = true,
194
+ currentUserId,
195
+ children,
196
+ }: WdkAppProviderProps) {
197
+ // Create secureStorage singleton and register synchronously so child effects
198
+ // (e.g. useWallet wallet switching) can access it on the first paint.
199
+ const secureStorage = useMemo(() => {
200
+ const storage = createSecureStorage()
201
+ WalletSetupService.setSecureStorage(storage)
202
+ return storage
203
+ }, [])
204
+
205
+ // Clear sensitive data on mount AND when app goes to background
206
+ // This ensures biometrics are always required on app restart or foreground
207
+ useEffect(() => {
208
+ // CRITICAL: Clear cache on mount to handle true app restarts (not hot reloads)
209
+ // When app is killed and reopened:
210
+ // - walletStore rehydrates with 'not_loaded' state
211
+ // - But in dev mode with hot reload, JS context persists and cache remains
212
+ // - Clearing here ensures cache is empty even in dev mode
213
+ log('[WdkAppProvider] Clearing credentials cache on mount (app restart)')
214
+ clearAllSensitiveData()
215
+
216
+ const appStateRef = { current: AppState.currentState }
217
+
218
+ const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {
219
+ const previousState = appStateRef.current
220
+ appStateRef.current = nextAppState
221
+
222
+ // When going to background: clear cache and mark wallet for re-authentication
223
+ if ((nextAppState === 'background' || nextAppState === 'inactive') && previousState === 'active') {
224
+ log('[WdkAppProvider] App going to background - clearing sensitive data and marking for re-auth')
225
+ clearAllSensitiveData()
226
+
227
+ // Reset wallet state to trigger re-authentication on foreground
228
+ // This ensures biometrics are required when app comes back
229
+ // CRITICAL: Only reset if wallet is 'ready' - don't interrupt 'loading' or 'checking' states
230
+ // Interrupting these states would cancel biometric authentication in progress
231
+ const currentState = walletStore.getState()
232
+ const currentStateType = currentState.walletLoadingState.type
233
+
234
+ if (currentStateType === 'ready' && currentState.activeWalletId) {
235
+ log('[WdkAppProvider] Resetting wallet state to trigger biometrics on foreground')
236
+ walletStore.setState((prev) => updateWalletLoadingState(prev, {
237
+ type: 'not_loaded'
238
+ }))
239
+ } else if (currentStateType === 'loading' || currentStateType === 'checking') {
240
+ log('[WdkAppProvider] Preserving wallet loading state during background transition', {
241
+ currentState: currentStateType,
242
+ })
243
+ // Do not reset - allow biometric authentication to complete
244
+ }
245
+ }
246
+
247
+ // When coming to foreground: wallet will auto-initialize with biometrics
248
+ // (handled by the consolidated effect below)
249
+ if (nextAppState === 'active' && (previousState === 'background' || previousState === 'inactive')) {
250
+ log('[WdkAppProvider] App coming to foreground - auto-initialization will trigger biometrics')
251
+ }
252
+ })
253
+
254
+ return () => subscription.remove()
255
+ }, [])
256
+
257
+ // Validate props on mount and when props change
258
+ useEffect(() => {
259
+ try {
260
+ validateNetworkConfigs(networkConfigs)
261
+ validateTokenConfigs(tokenConfigs)
262
+ } catch (error) {
263
+ const err = normalizeError(error, true, { component: 'WdkAppProvider', operation: 'propsValidation' })
264
+ logError('[WdkAppProvider] Invalid props:', err)
265
+ // Always throw validation errors - they indicate programming errors
266
+ throw err
267
+ }
268
+ }, [networkConfigs, tokenConfigs])
269
+
270
+ // Worklet state - read from workletStore via hook
271
+ const workletHookState = useWorklet()
272
+ const {
273
+ isWorkletStarted,
274
+ isInitialized: isWorkletInitialized,
275
+ isLoading: isWorkletLoading,
276
+ error: workletError,
277
+ } = workletHookState
278
+
279
+ // Wallet state - read from walletStore (single source of truth)
280
+ const walletStore = getWalletStore()
281
+
282
+ // Subscribe to primitive values directly
283
+ const activeWalletId = walletStore((state: WalletStore) => state.activeWalletId)
284
+
285
+ // For walletLoadingState, use a ref to manually check equality and prevent unnecessary re-renders
286
+ const walletLoadingStateRef = useRef(walletStore.getState().walletLoadingState)
287
+ const [walletLoadingState, setWalletLoadingState] = React.useState(walletStore.getState().walletLoadingState)
288
+
289
+ useEffect(() => {
290
+ const unsubscribe = walletStore.subscribe((state: WalletStore) => {
291
+ const newState = state.walletLoadingState
292
+ // Only update if content actually changed (deep equality check)
293
+ if (!deepEqualityFn(walletLoadingStateRef.current, newState)) {
294
+ walletLoadingStateRef.current = newState
295
+ setWalletLoadingState(newState)
296
+ }
297
+ })
298
+ return unsubscribe
299
+ }, [walletStore])
300
+
301
+ const walletAddresses = walletStore((state: WalletStore) =>
302
+ state.activeWalletId ? state.addresses[state.activeWalletId] : undefined
303
+ )
304
+
305
+ // Hooks for wallet operations
306
+ const {
307
+ initializeWallet,
308
+ hasWallet,
309
+ error: walletManagerError,
310
+ } = useWalletManager()
311
+
312
+ // Store initializeWallet in a ref to avoid it being a dependency of the effect
313
+ // This breaks the infinite loop: effect runs → component re-renders → initializeWallet recreated → effect runs again
314
+ const initializeWalletRef = useRef(initializeWallet)
315
+ useEffect(() => {
316
+ initializeWalletRef.current = initializeWallet
317
+ }, [initializeWallet])
318
+
319
+ // Track authentication errors to prevent infinite retry loops
320
+ // When biometric authentication fails, we shouldn't automatically retry
321
+ const authErrorRef = useRef<string | null>(null)
322
+
323
+ // Derive isWalletInitializing from walletLoadingState (single source of truth)
324
+ const isWalletInitializing = useMemo(() => {
325
+ return isWalletLoadingState(walletLoadingState)
326
+ }, [walletLoadingState])
327
+
328
+ // Worklet state object (exposed separately for flexibility)
329
+ const workletState = useMemo(() => ({
330
+ isReady: isWorkletStarted && !isWorkletLoading && !workletError,
331
+ isLoading: isWorkletLoading,
332
+ error: workletError,
333
+ }), [isWorkletStarted, isWorkletLoading, workletError])
334
+
335
+ // Wallet state object (exposed separately for flexibility)
336
+ const walletStateObject = useMemo(() => ({
337
+ status: (walletLoadingState.type === 'not_loaded' ? 'not_loaded' :
338
+ walletLoadingState.type === 'checking' ? 'checking' :
339
+ walletLoadingState.type === 'loading' ? 'loading' :
340
+ walletLoadingState.type === 'ready' ? 'ready' :
341
+ 'error') as 'not_loaded' | 'checking' | 'loading' | 'ready' | 'error',
342
+ identifier: getWalletIdFromLoadingState(walletLoadingState),
343
+ error: walletLoadingState.type === 'error' ? walletLoadingState.error : null,
344
+ }), [walletLoadingState])
345
+
346
+ // Worklet initialization status (worklet-specific)
347
+ const workletStatus = useMemo(() => {
348
+ return getWorkletStatus({
349
+ isWorkletStarted,
350
+ isLoading: isWorkletLoading,
351
+ error: workletError,
352
+ })
353
+ }, [isWorkletStarted, isWorkletLoading, workletError])
354
+
355
+ // Combined app status - derived from both worklet and wallet states (convenience)
356
+ const status = useMemo(() => {
357
+ return getCombinedStatus(
358
+ {
359
+ isWorkletStarted,
360
+ isLoading: isWorkletLoading,
361
+ error: workletError,
362
+ },
363
+ walletLoadingState
364
+ )
365
+ }, [isWorkletStarted, isWorkletLoading, workletError, walletLoadingState])
366
+
367
+ // Automatically initialize worklet when component mounts
368
+ useEffect(() => {
369
+ log('[WdkAppProvider] Checking initialization conditions', {
370
+ isWorkletInitialized,
371
+ isWorkletLoading,
372
+ isWorkletStarted,
373
+ })
374
+
375
+ // Skip if worklet is loading
376
+ if (isWorkletLoading) {
377
+ log('[WdkAppProvider] Initialization skipped', { reason: 'already loading' })
378
+ return
379
+ }
380
+
381
+ // If worklet is already started/initialized, nothing to do
382
+ if (isWorkletStarted || isWorkletInitialized) {
383
+ log('[WdkAppProvider] Worklet already started, ready to load wallets')
384
+ return
385
+ }
386
+
387
+ let cancelled = false
388
+
389
+ const initializeWorklet = async () => {
390
+ try {
391
+ log('[WdkAppProvider] Starting worklet initialization...')
392
+ await WorkletLifecycleService.startWorklet(networkConfigs)
393
+ if (!cancelled) {
394
+ log('[WdkAppProvider] Worklet started successfully')
395
+ }
396
+ } catch (error) {
397
+ if (!cancelled) {
398
+ const err = normalizeError(error, true, {
399
+ component: 'WdkAppProvider',
400
+ operation: 'workletInitialization',
401
+ })
402
+ logError('[WdkAppProvider] Failed to initialize worklet:', error)
403
+ }
404
+ }
405
+ }
406
+
407
+ initializeWorklet()
408
+
409
+ // Cleanup function to prevent state updates if component unmounts
410
+ return () => {
411
+ cancelled = true
412
+ }
413
+ }, [isWorkletInitialized, isWorkletLoading, isWorkletStarted])
414
+ // Note: networkConfigs removed from deps - it's a prop that should be stable for app lifetime
415
+ // and doesn't need to trigger worklet re-initialization
416
+
417
+ // Consolidated effect: Sync wallet loading state with activeWalletId, addresses, and errors
418
+ //
419
+ // IMPORTANT: This effect MUST remain consolidated to prevent race conditions.
420
+ // Multiple interdependent state changes (activeWalletId, addresses, loadingState, errors)
421
+ // must be evaluated atomically in a single effect. Splitting into multiple effects would
422
+ // allow them to run with stale state, causing race conditions.
423
+ //
424
+ // State synchronization logic:
425
+ // 1. If activeWalletId is cleared, reset to not_loaded
426
+ // 2. If activeWalletId changes, handle wallet switch (check if addresses exist)
427
+ // 3. If wallet has addresses but state is not_loaded, transition to loading first
428
+ // 4. If wallet is loading and addresses appear, mark as ready (initialization complete)
429
+ // 5. If error occurs, update error state (only if tracking the correct wallet)
430
+ //
431
+ // All conditions are checked in order with early returns to ensure atomic evaluation.
432
+ useEffect(() => {
433
+ // EARLY EXIT: Skip automatic wallet initialization if disabled (e.g., when logged out)
434
+ if (!enableAutoInitialization) {
435
+ // Clear authentication error flag when auto-init is disabled (e.g., logout)
436
+ if (authErrorRef.current) {
437
+ log('[WdkAppProvider] Clearing authentication error flag - auto-init disabled')
438
+ authErrorRef.current = null
439
+ }
440
+ return
441
+ }
442
+
443
+ // VALIDATION 1: User identity must be confirmed before auto-initialization
444
+ // If currentUserId is undefined/null, we don't know which user is logged in yet
445
+ // Wait until user identity is confirmed to prevent wrong wallet initialization
446
+ if (currentUserId === undefined || currentUserId === null) {
447
+ log('[WdkAppProvider] Waiting for user identity confirmation before auto-init', {
448
+ hasActiveWalletId: !!activeWalletId,
449
+ })
450
+ return
451
+ }
452
+
453
+ // VALIDATION 2: If activeWalletId doesn't match currentUserId, set it to correct user
454
+ // This allows useOnboarding to initialize the correct wallet without interference
455
+ if (activeWalletId !== currentUserId) {
456
+ log('[WdkAppProvider] Setting activeWalletId to current user', {
457
+ activeWalletId,
458
+ currentUserId,
459
+ })
460
+
461
+ // Set activeWalletId to current user - let useOnboarding handle initialization
462
+ walletStore.setState({
463
+ activeWalletId: currentUserId,
464
+ })
465
+
466
+ // Clear auth error flag to allow fresh authentication
467
+ if (authErrorRef.current) {
468
+ authErrorRef.current = null
469
+ }
470
+
471
+ // Return and let the effect re-run with correct activeWalletId
472
+ return
473
+ }
474
+
475
+ // EARLY EXIT: Skip if we have an authentication error to prevent infinite retry loop
476
+ // Authentication errors (biometric failures) require user intervention, not automatic retry
477
+ if (authErrorRef.current) {
478
+ log('[WdkAppProvider] Skipping auto-initialization due to authentication error', {
479
+ error: authErrorRef.current,
480
+ })
481
+ return
482
+ }
483
+
484
+ const currentWalletId = getWalletIdFromLoadingState(walletLoadingState)
485
+ const hasAddresses = !!(walletAddresses && Object.keys(walletAddresses).length > 0)
486
+
487
+ // Handle activeWalletId cleared
488
+ if (shouldResetToNotLoaded(activeWalletId, walletLoadingState)) {
489
+ log('[WdkAppProvider] Active wallet cleared, resetting wallet state')
490
+ // Clear authentication error flag when wallet is reset
491
+ if (authErrorRef.current) {
492
+ log('[WdkAppProvider] Clearing authentication error flag on wallet reset')
493
+ authErrorRef.current = null
494
+ }
495
+ walletStore.setState((prev) => updateWalletLoadingState(prev, { type: 'not_loaded' }))
496
+ return
497
+ }
498
+
499
+ // Ensure activeWalletId is not null before proceeding (shouldn't happen after check above, but TypeScript needs it)
500
+ if (!activeWalletId) {
501
+ return
502
+ }
503
+
504
+ // Handle wallet switching (activeWalletId changed to different wallet)
505
+ const switchDecision = getWalletSwitchDecision(currentWalletId, activeWalletId, hasAddresses)
506
+ if (switchDecision.shouldSwitch) {
507
+ log('[WdkAppProvider] Active wallet changed', {
508
+ from: currentWalletId,
509
+ to: activeWalletId,
510
+ hasAddresses,
511
+ isWorkletStarted,
512
+ shouldMarkReady: switchDecision.shouldMarkReady,
513
+ })
514
+
515
+ // When switching to a wallet, trigger proper initialization with biometrics
516
+ if (isWorkletStarted) {
517
+ // Skip if wallet is already initializing to prevent duplicate biometric prompts
518
+ if (isWalletInitializing) {
519
+ log('[WdkAppProvider] Skipping wallet switch initialization - already in progress', {
520
+ activeWalletId,
521
+ walletLoadingState: walletLoadingState.type,
522
+ })
523
+ return
524
+ }
525
+
526
+ log('[WdkAppProvider] Wallet switch detected - triggering initialization', {
527
+ activeWalletId,
528
+ hasAddresses,
529
+ })
530
+
531
+ // Check if wallet already exists before deciding to create new or load existing
532
+ ;(async () => {
533
+ try {
534
+ const walletExists = await hasWallet(activeWalletId)
535
+ const shouldCreateNew = !walletExists
536
+
537
+ log('[WdkAppProvider] Wallet existence check', {
538
+ activeWalletId,
539
+ walletExists,
540
+ shouldCreateNew,
541
+ })
542
+
543
+ // Call initializeWallet to trigger biometrics and properly load the wallet
544
+ // This will transition state to 'checking' immediately, preventing duplicate calls
545
+ await initializeWalletRef.current({ createNew: shouldCreateNew, walletId: activeWalletId })
546
+ log('[WdkAppProvider] Wallet initialized successfully after switch')
547
+ } catch (error) {
548
+ logError('[WdkAppProvider] Failed to initialize wallet after switch:', error)
549
+ // Error will be handled by the error handling logic below
550
+ }
551
+ })()
552
+ } else {
553
+ // Worklet not started yet - reset to not_loaded and wait
554
+ walletStore.setState((prev) => updateWalletLoadingState(prev, {
555
+ type: 'not_loaded'
556
+ }))
557
+ }
558
+ // This effect will run again when isWorkletStarted becomes true
559
+ return
560
+ }
561
+
562
+ // Handle case where addresses exist but state is not_loaded (from persistence/app restart)
563
+ // This means wallet was cached but worklet is not initialized with credentials
564
+ // We need to trigger proper initialization with biometrics
565
+ // Only trigger if we're not already in the process of loading (checking/loading state)
566
+ if (walletLoadingState.type === 'not_loaded' && hasAddresses && activeWalletId && isWorkletStarted) {
567
+ // Skip if wallet is already initializing to prevent duplicate biometric prompts
568
+ if (isWalletInitializing) {
569
+ log('[WdkAppProvider] Skipping cached wallet initialization - already in progress', {
570
+ activeWalletId,
571
+ walletLoadingState: walletLoadingState.type,
572
+ })
573
+ return
574
+ }
575
+
576
+ log('[WdkAppProvider] Cached wallet detected on restart - triggering initialization with biometrics', {
577
+ activeWalletId,
578
+ hasAddresses,
579
+ isWorkletStarted,
580
+ isWorkletInitialized,
581
+ walletLoadingState: walletLoadingState.type
582
+ })
583
+
584
+ // Check if wallet already exists before deciding to create new or load existing
585
+ ;(async () => {
586
+ try {
587
+ const walletExists = await hasWallet(activeWalletId)
588
+ const shouldCreateNew = !walletExists
589
+
590
+ log('[WdkAppProvider] Wallet existence check', {
591
+ activeWalletId,
592
+ walletExists,
593
+ shouldCreateNew,
594
+ })
595
+
596
+ // Call initializeWallet to trigger biometrics and properly load the wallet
597
+ // This will transition state to 'checking' immediately, preventing duplicate calls
598
+ // Then it will go through: checking -> loading -> ready
599
+ await initializeWalletRef.current({ createNew: shouldCreateNew, walletId: activeWalletId })
600
+ log('[WdkAppProvider] Wallet initialized successfully from cache')
601
+ } catch (error) {
602
+ logError('[WdkAppProvider] Failed to initialize wallet from cache:', error)
603
+ // Error will be handled by the error handling logic below
604
+ }
605
+ })()
606
+
607
+ return
608
+ }
609
+
610
+ // Handle case where activeWalletId exists but no addresses (after logout/app restart)
611
+ // This means the user logged in but addresses were cleared during logout
612
+ // We need to trigger initialization to load the wallet
613
+ if (walletLoadingState.type === 'not_loaded' && !hasAddresses && activeWalletId && isWorkletStarted) {
614
+ // Skip if wallet is already initializing to prevent duplicate biometric prompts
615
+ if (isWalletInitializing) {
616
+ log('[WdkAppProvider] Skipping wallet initialization - already in progress', {
617
+ activeWalletId,
618
+ walletLoadingState: walletLoadingState.type,
619
+ })
620
+ return
621
+ }
622
+
623
+ log('[WdkAppProvider] Active wallet detected without addresses - triggering initialization', {
624
+ activeWalletId,
625
+ hasAddresses,
626
+ isWorkletStarted,
627
+ walletLoadingState: walletLoadingState.type
628
+ })
629
+
630
+ // Check if wallet already exists before deciding to create new or load existing
631
+ ;(async () => {
632
+ try {
633
+ const walletExists = await hasWallet(activeWalletId)
634
+ const shouldCreateNew = !walletExists
635
+
636
+ log('[WdkAppProvider] Wallet existence check', {
637
+ activeWalletId,
638
+ walletExists,
639
+ shouldCreateNew,
640
+ })
641
+
642
+ // Call initializeWallet to trigger biometrics and properly load the wallet
643
+ // This will transition state to 'checking' immediately, preventing duplicate calls
644
+ await initializeWalletRef.current({ createNew: shouldCreateNew, walletId: activeWalletId })
645
+ log('[WdkAppProvider] Wallet initialized successfully')
646
+ } catch (error) {
647
+ logError('[WdkAppProvider] Failed to initialize wallet:', error)
648
+ // Error will be handled by the error handling logic below
649
+ }
650
+ })()
651
+
652
+ return
653
+ }
654
+
655
+ // Handle ready state transitions
656
+ if (shouldMarkWalletAsReady(walletLoadingState, hasAddresses, currentWalletId, activeWalletId, isWorkletInitialized)) {
657
+ log('[WdkAppProvider] Wallet ready', { activeWalletId })
658
+ walletStore.setState((prev) => updateWalletLoadingState(prev, {
659
+ type: 'ready',
660
+ identifier: activeWalletId
661
+ }))
662
+ return
663
+ }
664
+
665
+ // Handle errors from useWalletManager
666
+ if (shouldHandleError(walletManagerError, currentWalletId, activeWalletId, walletLoadingState)) {
667
+ log('[WdkAppProvider] Wallet operation error detected', {
668
+ activeWalletId,
669
+ error: walletManagerError
670
+ })
671
+ const error = new Error(walletManagerError!)
672
+
673
+ // Check if this is an authentication error (biometric failure)
674
+ const errorMessage = walletManagerError?.toLowerCase() || ''
675
+ const isAuthError =
676
+ errorMessage.includes('authentication') ||
677
+ errorMessage.includes('biometric') ||
678
+ errorMessage.includes('user cancel')
679
+
680
+ if (isAuthError && !authErrorRef.current) {
681
+ log('[WdkAppProvider] Authentication error detected - preventing auto-retry', {
682
+ error: walletManagerError,
683
+ })
684
+ authErrorRef.current = walletManagerError || 'Authentication failed'
685
+ }
686
+
687
+ walletStore.setState((prev) => updateWalletLoadingState(prev, {
688
+ type: 'error',
689
+ identifier: activeWalletId,
690
+ error
691
+ }))
692
+ }
693
+ }, [enableAutoInitialization, currentUserId, activeWalletId, walletLoadingState, walletAddresses, walletManagerError, isWalletInitializing, isWorkletStarted, isWorkletInitialized])
694
+ // Note: walletStore removed from deps - it's a singleton that never changes
695
+ // Note: initializeWallet removed from deps and accessed via ref to prevent infinite loop
696
+
697
+ // Retry initialization
698
+ const retry = useCallback(() => {
699
+ log('[WdkAppProvider] Retrying initialization...')
700
+ // Clear authentication error flag to allow retry
701
+ if (authErrorRef.current) {
702
+ log('[WdkAppProvider] Clearing authentication error flag for retry')
703
+ authErrorRef.current = null
704
+ }
705
+ if (isWalletErrorState(walletLoadingState)) {
706
+ walletStore.setState((prev) => updateWalletLoadingState(prev, { type: 'not_loaded' }))
707
+ }
708
+ }, [walletLoadingState])
709
+ // Note: walletStore removed from deps - it's a singleton that never changes
710
+
711
+ // Convenience getters
712
+ const isInitializing = useMemo(() => isAppInProgressStatus(status), [status])
713
+ const isReady = useMemo(() => isAppReadyStatus(status), [status])
714
+
715
+ // Get wallet error from wallet state
716
+ const walletError = walletLoadingState.type === 'error' ? walletLoadingState.error : null
717
+ const initializationError = workletError ? new Error(workletError) : walletError
718
+
719
+ // Get walletExists from wallet state
720
+ const walletExists = useMemo(() => {
721
+ if (walletLoadingState.type === 'loading') {
722
+ return walletLoadingState.walletExists
723
+ }
724
+ if (walletLoadingState.type === 'ready') {
725
+ return true // Wallet is loaded, so it exists
726
+ }
727
+ return null
728
+ }, [walletLoadingState])
729
+
730
+ // Loading wallet ID (transient, during loading operations)
731
+ const loadingWalletId = useMemo(() => {
732
+ if (walletLoadingState.type === 'checking' || walletLoadingState.type === 'loading') {
733
+ return walletLoadingState.identifier
734
+ }
735
+ return null
736
+ }, [walletLoadingState])
737
+
738
+ const contextValue: WdkAppContextValue = useMemo(
739
+ () => ({
740
+ status,
741
+ workletStatus,
742
+ workletState,
743
+ walletState: walletStateObject,
744
+ isInitializing,
745
+ isReady,
746
+ activeWalletId,
747
+ loadingWalletId,
748
+ walletExists,
749
+ error: initializationError,
750
+ retry,
751
+ }),
752
+ [status, workletStatus, workletState, walletStateObject, isInitializing, isReady, activeWalletId, loadingWalletId, walletExists, initializationError, retry]
753
+ )
754
+
755
+ return (
756
+ <QueryClientProvider client={queryClient}>
757
+ <WdkAppContext.Provider value={contextValue}>{children}</WdkAppContext.Provider>
758
+ </QueryClientProvider>
759
+ )
760
+ }
761
+
762
+ // Export context for use by the useWdkApp hook
763
+ export { WdkAppContext }
764
+