@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,134 @@
1
+ /**
2
+ * Account Service
3
+ *
4
+ * Handles account method calls through the worklet.
5
+ * This service provides a generic interface for calling account methods
6
+ * like getBalance, getTokenBalance, signMessage, signTransaction, etc.
7
+ */
8
+
9
+ import { convertBigIntToString } from '../utils/balanceUtils'
10
+ import { handleServiceError } from '../utils/errorHandling'
11
+ import { safeStringify } from '../utils/jsonUtils'
12
+ import { workletResponseSchema } from '../utils/schemas'
13
+ import { requireInitialized } from '../utils/storeHelpers'
14
+ import { validateAccountIndex, validateNetworkName } from '../utils/validation'
15
+
16
+ /**
17
+ * Account Service
18
+ *
19
+ * Provides methods for calling account operations through the worklet.
20
+ */
21
+ export class AccountService {
22
+ /**
23
+ * Call a method on a wallet account
24
+ * Generic method for calling any account method through the worklet
25
+ *
26
+ * The worklet should already have the correct wallet loaded via `initializeWDK`.
27
+ * Wallet switching is handled at the hook level before calling this service.
28
+ *
29
+ * @param network - Network name
30
+ * @param accountIndex - Account index
31
+ * @param methodName - Method name
32
+ * @param args - Optional arguments for the method
33
+ * @param walletId - Optional wallet identifier (for consistency, worklet should already have correct wallet loaded)
34
+ * @returns Promise with the method result
35
+ * @throws Error if validation fails
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // Get balance
40
+ * const balance = await AccountService.callAccountMethod('ethereum', 0, 'getBalance', null)
41
+ *
42
+ * // Get token balance
43
+ * const tokenBalance = await AccountService.callAccountMethod(
44
+ * 'ethereum',
45
+ * 0,
46
+ * 'getTokenBalance',
47
+ * '0x...'
48
+ * )
49
+ *
50
+ * // Sign a message
51
+ * const signature = await AccountService.callAccountMethod(
52
+ * 'ethereum',
53
+ * 0,
54
+ * 'signMessage',
55
+ * { message: 'Hello World' }
56
+ * )
57
+ * ```
58
+ */
59
+ static async callAccountMethod<T = unknown>(
60
+ network: string,
61
+ accountIndex: number,
62
+ methodName: string,
63
+ args?: unknown,
64
+ walletId?: string
65
+ ): Promise<T> {
66
+ // Validate methodName parameter
67
+ if (typeof methodName !== 'string' || methodName.trim().length === 0) {
68
+ throw new Error('methodName must be a non-empty string')
69
+ }
70
+
71
+ // Validate inputs
72
+ validateNetworkName(network)
73
+ validateAccountIndex(accountIndex)
74
+
75
+ // Require initialized worklet
76
+ const hrpc = requireInitialized()
77
+
78
+ // Validate and sanitize args before stringification
79
+ let argsString: string | null = null
80
+ if (args !== undefined && args !== null) {
81
+ // Validate structure and stringify safely
82
+ argsString = safeStringify(args)
83
+ }
84
+
85
+ try {
86
+ const response = await hrpc.callMethod({
87
+ methodName,
88
+ network,
89
+ accountIndex,
90
+ args: argsString,
91
+ })
92
+
93
+ // Validate response structure
94
+ const validatedResponse = workletResponseSchema.parse(response)
95
+
96
+ if (!validatedResponse.result) {
97
+ throw new Error(`Method ${methodName} returned no result`)
98
+ }
99
+
100
+ // Parse the result and handle BigInt values
101
+ let parsed: T
102
+ try {
103
+ parsed = JSON.parse(validatedResponse.result) as T
104
+ // Basic validation: ensure parsed is not null/undefined
105
+ if (parsed === null || parsed === undefined) {
106
+ throw new Error('Parsed result is null or undefined')
107
+ }
108
+ } catch (error) {
109
+ if (error instanceof Error && error.message.includes('Parsed result is null')) {
110
+ throw error
111
+ }
112
+ throw new Error(`Failed to parse result from ${methodName}: ${error instanceof Error ? error.message : String(error)}`)
113
+ }
114
+
115
+ // Runtime type validation based on method type
116
+ if (methodName === 'getBalance' || methodName === 'getTokenBalance') {
117
+ // Validate balance format
118
+ if (typeof parsed !== 'string' || !/^\d+$/.test(parsed)) {
119
+ throw new Error(`Invalid balance format: ${parsed}`)
120
+ }
121
+ }
122
+
123
+ // Recursively convert BigInt values to strings to prevent serialization errors
124
+ return convertBigIntToString(parsed) as T
125
+ } catch (error) {
126
+ handleServiceError(error, 'AccountService', `callAccountMethod:${methodName}`, {
127
+ network,
128
+ accountIndex,
129
+ methodName,
130
+ })
131
+ }
132
+ }
133
+ }
134
+
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Address Service
3
+ *
4
+ * Handles address retrieval and caching operations.
5
+ * This service is focused solely on address management.
6
+ */
7
+
8
+ import { getWalletStore } from '../store/walletStore'
9
+ import { handleServiceError } from '../utils/errorHandling'
10
+ import { requireInitialized, resolveWalletId, updateAddressInState } from '../utils/storeHelpers'
11
+ import { isValidAddress } from '../utils/typeGuards'
12
+ import { validateAccountIndex, validateNetworkName } from '../utils/validation'
13
+
14
+ /**
15
+ * Address Service
16
+ *
17
+ * Provides methods for retrieving and caching wallet addresses.
18
+ */
19
+ export class AddressService {
20
+ /**
21
+ * Get address for a specific network and account index
22
+ * Caches the address in walletStore for future use
23
+ *
24
+ * @param network - Network name
25
+ * @param accountIndex - Account index (default: 0)
26
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
27
+ */
28
+ static async getAddress(
29
+ network: string,
30
+ accountIndex = 0,
31
+ walletId?: string
32
+ ): Promise<string> {
33
+ // Validate inputs
34
+ validateNetworkName(network)
35
+ validateAccountIndex(accountIndex)
36
+
37
+ const walletStore = getWalletStore()
38
+ const walletState = walletStore.getState()
39
+
40
+ // Resolve walletId from parameter or store
41
+ const targetWalletId = resolveWalletId(walletId)
42
+
43
+ // Check cache first (per-wallet)
44
+ const cachedAddress = walletState.addresses[targetWalletId]?.[network]?.[accountIndex]
45
+ if (cachedAddress) {
46
+ // Validate cached address format
47
+ if (!isValidAddress(cachedAddress)) {
48
+ throw new Error(`Cached address for ${targetWalletId}:${network}:${accountIndex} has invalid format`)
49
+ }
50
+ return cachedAddress
51
+ }
52
+
53
+ // Require initialized worklet
54
+ const hrpc = requireInitialized()
55
+
56
+ const loadingKey = `${network}-${accountIndex}`
57
+
58
+ try {
59
+ // Update loading state (per-wallet)
60
+ walletStore.setState((prev) => ({
61
+ walletLoading: {
62
+ ...prev.walletLoading,
63
+ [targetWalletId]: {
64
+ ...(prev.walletLoading[targetWalletId] || {}),
65
+ [loadingKey]: true,
66
+ },
67
+ },
68
+ }))
69
+
70
+ // Call getAddress method on the account
71
+ const response = await hrpc.callMethod({
72
+ methodName: 'getAddress',
73
+ network,
74
+ accountIndex,
75
+ args: null,
76
+ })
77
+
78
+ if (!response.result) {
79
+ throw new Error('Failed to get address from worklet')
80
+ }
81
+
82
+ let address: string
83
+ try {
84
+ const parsed = JSON.parse(response.result)
85
+ if (typeof parsed !== 'string') {
86
+ throw new Error('Address must be a string')
87
+ }
88
+ // Runtime validation of address format
89
+ if (!isValidAddress(parsed)) {
90
+ throw new Error(`Address from worklet has invalid format: ${parsed}`)
91
+ }
92
+ address = parsed
93
+ } catch (error) {
94
+ throw new Error(`Failed to parse address from worklet response: ${error instanceof Error ? error.message : String(error)}`)
95
+ }
96
+
97
+ // Cache the address using helper (per-wallet)
98
+ walletStore.setState((prev) => ({
99
+ ...updateAddressInState(prev, targetWalletId, network, accountIndex, address),
100
+ walletLoading: {
101
+ ...prev.walletLoading,
102
+ [targetWalletId]: {
103
+ ...(prev.walletLoading[targetWalletId] || {}),
104
+ [loadingKey]: false,
105
+ },
106
+ },
107
+ }))
108
+
109
+ return address
110
+ } catch (error) {
111
+ // Update loading state on error (per-wallet)
112
+ walletStore.setState((prev) => ({
113
+ walletLoading: {
114
+ ...prev.walletLoading,
115
+ [targetWalletId]: {
116
+ ...(prev.walletLoading[targetWalletId] || {}),
117
+ [loadingKey]: false,
118
+ },
119
+ },
120
+ }))
121
+ handleServiceError(error, 'AddressService', 'getAddress', { network, accountIndex, walletId: targetWalletId })
122
+ }
123
+ }
124
+ }
125
+
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Balance Service
3
+ *
4
+ * Handles balance operations: getting, setting, updating, and managing balance state.
5
+ * This service is focused solely on balance management.
6
+ *
7
+ * ## Storage Strategy - Single Source of Truth
8
+ *
9
+ * This service manages balances in the Zustand store (walletStore.balances), which is
10
+ * the **single source of truth** for all balance data.
11
+ *
12
+ * **Architecture**:
13
+ * - **Zustand Store (Single Source of Truth)**: Stores and persists balances across app restarts (via MMKV)
14
+ * - **TanStack Query (Fetching Layer)**: Handles fetching, caching, and refetching (reads from and updates Zustand)
15
+ *
16
+ * **Data Flow**:
17
+ * 1. TanStack Query reads initial data from Zustand (via `initialData` in useBalance hooks)
18
+ * 2. TanStack Query fetches fresh balance from worklet when needed
19
+ * 3. After successful fetch, TanStack Query updates Zustand via this service (single source of truth update)
20
+ * 4. Components read from TanStack Query, which ensures consistency with Zustand
21
+ *
22
+ * **Sync Guarantees**:
23
+ * - Zustand is always the source of truth - all reads and writes go through Zustand
24
+ * - TanStack Query updates Zustand directly after fetch (no separate sync step)
25
+ * - Initial data consistency - TanStack Query uses Zustand's persisted data on app start
26
+ * - No race conditions - all updates are atomic through Zustand
27
+ *
28
+ * **Usage**:
29
+ * - Direct access: Use `BalanceService.getBalance()` to read from Zustand directly (rare, prefer TanStack Query hooks)
30
+ * - Updates: TanStack Query calls `BalanceService.updateBalance()` after fetch (automatic)
31
+ * - Preferred: Use `useBalance()` hooks which handle Zustand integration automatically
32
+ */
33
+
34
+ import { getWalletStore } from '../store/walletStore'
35
+ import { resolveWalletId, updateBalanceInState, getNestedState } from '../utils/storeHelpers'
36
+ import { NATIVE_TOKEN_KEY } from '../utils/constants'
37
+ import { validateBalance, validateWalletParams } from '../utils/validation'
38
+
39
+ /**
40
+ * Balance Service
41
+ *
42
+ * Provides methods for managing wallet balances.
43
+ */
44
+ export class BalanceService {
45
+ /**
46
+ * Validate wallet parameters and balance (if provided)
47
+ * Helper to reduce repetitive validation calls
48
+ */
49
+ private static validateBalanceParams(
50
+ network: string,
51
+ accountIndex: number,
52
+ tokenAddress?: string | null,
53
+ balance?: string
54
+ ): void {
55
+ validateWalletParams(network, accountIndex, tokenAddress)
56
+ if (balance !== undefined) {
57
+ validateBalance(balance)
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Get token key from token address (native or token address)
63
+ */
64
+ private static getTokenKey(tokenAddress: string | null): string {
65
+ return tokenAddress || NATIVE_TOKEN_KEY
66
+ }
67
+ /**
68
+ * Update balance for a specific wallet, network, and token
69
+ *
70
+ * @param accountIndex - Account index
71
+ * @param network - Network name
72
+ * @param tokenAddress - Token address (null for native)
73
+ * @param balance - Balance value
74
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
75
+ */
76
+ static updateBalance(
77
+ accountIndex: number,
78
+ network: string,
79
+ tokenAddress: string | null,
80
+ balance: string,
81
+ walletId?: string
82
+ ): void {
83
+ this.validateBalanceParams(network, accountIndex, tokenAddress, balance)
84
+
85
+ const walletStore = getWalletStore()
86
+ const walletState = walletStore.getState()
87
+ const targetWalletId = resolveWalletId(walletId)
88
+ const tokenKey = this.getTokenKey(tokenAddress)
89
+
90
+ walletStore.setState((prev) => ({
91
+ ...updateBalanceInState(prev, targetWalletId, network, accountIndex, tokenKey, balance),
92
+ }))
93
+ }
94
+
95
+ /**
96
+ * Get balance for a specific wallet, network, and token
97
+ *
98
+ * @param accountIndex - Account index
99
+ * @param network - Network name
100
+ * @param tokenAddress - Token address (null for native)
101
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
102
+ */
103
+ static getBalance(
104
+ accountIndex: number,
105
+ network: string,
106
+ tokenAddress: string | null,
107
+ walletId?: string
108
+ ): string | null {
109
+ this.validateBalanceParams(network, accountIndex, tokenAddress)
110
+
111
+ const walletStore = getWalletStore()
112
+ const walletState = walletStore.getState()
113
+ const targetWalletId = resolveWalletId(walletId)
114
+ const tokenKey = this.getTokenKey(tokenAddress)
115
+
116
+ return getNestedState(
117
+ walletState.balances,
118
+ [targetWalletId, network, accountIndex, tokenKey],
119
+ null
120
+ )
121
+ }
122
+
123
+ /**
124
+ * Get all balances for a specific wallet and network
125
+ *
126
+ * @param accountIndex - Account index
127
+ * @param network - Network name
128
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
129
+ */
130
+ static getBalancesForWallet(
131
+ accountIndex: number,
132
+ network: string,
133
+ walletId?: string
134
+ ): Record<string, string> | null {
135
+ // Validate inputs
136
+ validateWalletParams(network, accountIndex)
137
+
138
+ const walletStore = getWalletStore()
139
+ const walletState = walletStore.getState()
140
+ const targetWalletId = resolveWalletId(walletId)
141
+
142
+ return getNestedState(
143
+ walletState.balances,
144
+ [targetWalletId, network, accountIndex],
145
+ null
146
+ )
147
+ }
148
+
149
+ /**
150
+ * Set balance loading state
151
+ *
152
+ * @param network - Network name
153
+ * @param accountIndex - Account index
154
+ * @param tokenAddress - Token address (null for native)
155
+ * @param loading - Loading state
156
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
157
+ */
158
+ static setBalanceLoading(
159
+ network: string,
160
+ accountIndex: number,
161
+ tokenAddress: string | null,
162
+ loading: boolean,
163
+ walletId?: string
164
+ ): void {
165
+ this.validateBalanceParams(network, accountIndex, tokenAddress)
166
+
167
+ const walletStore = getWalletStore()
168
+ const walletState = walletStore.getState()
169
+ const targetWalletId = resolveWalletId(walletId)
170
+ const tokenKey = this.getTokenKey(tokenAddress)
171
+ const loadingKey = `${network}-${accountIndex}-${tokenKey}`
172
+
173
+ walletStore.setState((prev) => ({
174
+ balanceLoading: {
175
+ ...prev.balanceLoading,
176
+ [targetWalletId]: loading
177
+ ? { ...(prev.balanceLoading[targetWalletId] || {}), [loadingKey]: true }
178
+ : Object.fromEntries(
179
+ Object.entries(prev.balanceLoading[targetWalletId] || {}).filter(([key]) => key !== loadingKey)
180
+ ),
181
+ },
182
+ }))
183
+ }
184
+
185
+ /**
186
+ * Check if balance is loading
187
+ *
188
+ * @param network - Network name
189
+ * @param accountIndex - Account index
190
+ * @param tokenAddress - Token address (null for native)
191
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
192
+ */
193
+ static isBalanceLoading(
194
+ network: string,
195
+ accountIndex: number,
196
+ tokenAddress: string | null,
197
+ walletId?: string
198
+ ): boolean {
199
+ this.validateBalanceParams(network, accountIndex, tokenAddress)
200
+
201
+ const walletStore = getWalletStore()
202
+ const walletState = walletStore.getState()
203
+ const targetWalletId = walletId || walletState.activeWalletId
204
+ if (!targetWalletId) {
205
+ return false
206
+ }
207
+ const tokenKey = this.getTokenKey(tokenAddress)
208
+ const loadingKey = `${network}-${accountIndex}-${tokenKey}`
209
+
210
+ return getNestedState(
211
+ walletState.balanceLoading,
212
+ [targetWalletId, loadingKey],
213
+ false
214
+ )
215
+ }
216
+
217
+ /**
218
+ * Update last balance update timestamp
219
+ *
220
+ * @param network - Network name
221
+ * @param accountIndex - Account index
222
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
223
+ */
224
+ static updateLastBalanceUpdate(
225
+ network: string,
226
+ accountIndex: number,
227
+ walletId?: string
228
+ ): void {
229
+ // Validate inputs
230
+ validateWalletParams(network, accountIndex)
231
+
232
+ const walletStore = getWalletStore()
233
+ const walletState = walletStore.getState()
234
+ const targetWalletId = resolveWalletId(walletId)
235
+ const now = Date.now()
236
+
237
+ walletStore.setState((prev) => ({
238
+ lastBalanceUpdate: {
239
+ ...prev.lastBalanceUpdate,
240
+ [targetWalletId]: {
241
+ ...(prev.lastBalanceUpdate[targetWalletId] || {}),
242
+ [network]: {
243
+ ...(prev.lastBalanceUpdate[targetWalletId]?.[network] || {}),
244
+ [accountIndex]: now,
245
+ },
246
+ },
247
+ },
248
+ }))
249
+ }
250
+
251
+ /**
252
+ * Get last balance update timestamp
253
+ *
254
+ * @param network - Network name
255
+ * @param accountIndex - Account index
256
+ * @param walletId - Optional wallet identifier (defaults to activeWalletId from store)
257
+ */
258
+ static getLastBalanceUpdate(
259
+ network: string,
260
+ accountIndex: number,
261
+ walletId?: string
262
+ ): number | null {
263
+ // Validate inputs
264
+ validateWalletParams(network, accountIndex)
265
+
266
+ const walletStore = getWalletStore()
267
+ const walletState = walletStore.getState()
268
+ const targetWalletId = resolveWalletId(walletId)
269
+
270
+ return getNestedState(
271
+ walletState.lastBalanceUpdate,
272
+ [targetWalletId, network, accountIndex],
273
+ null
274
+ )
275
+ }
276
+
277
+ /**
278
+ * Clear all balances (useful for wallet reset)
279
+ */
280
+ static clearBalances(): void {
281
+ const walletStore = getWalletStore()
282
+
283
+ walletStore.setState({
284
+ balances: {},
285
+ balanceLoading: {},
286
+ lastBalanceUpdate: {},
287
+ })
288
+ }
289
+ }
290
+
291
+