@spacesops/wdk-react-native-core 1.0.0-beta.68 → 1.0.0-beta.70

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 (147) hide show
  1. package/dist/hooks/useBalance.js +50 -7
  2. package/dist/hooks/useBalance.js.map +1 -1
  3. package/dist/hooks/useTransactions.d.ts +21 -0
  4. package/dist/hooks/useTransactions.js +64 -0
  5. package/dist/hooks/useTransactions.js.map +1 -0
  6. package/dist/index.d.ts +5 -2
  7. package/dist/index.js +3 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/provider/WdkAppProvider.d.ts +7 -2
  10. package/dist/provider/WdkAppProvider.js +6 -1
  11. package/dist/provider/WdkAppProvider.js.map +1 -1
  12. package/dist/services/transactionService.d.ts +27 -0
  13. package/dist/services/transactionService.js +154 -0
  14. package/dist/services/transactionService.js.map +1 -0
  15. package/dist/store/indexerConfigStore.d.ts +7 -0
  16. package/dist/store/indexerConfigStore.js +14 -0
  17. package/dist/store/indexerConfigStore.js.map +1 -0
  18. package/dist/types.d.ts +33 -0
  19. package/dist/utils/constants.d.ts +18 -0
  20. package/dist/utils/constants.js +18 -0
  21. package/dist/utils/constants.js.map +1 -1
  22. package/dist/utils/mmkvKeyManager.js +3 -3
  23. package/dist/utils/mmkvKeyManager.js.map +1 -1
  24. package/dist/utils/operationMutex.js.map +1 -1
  25. package/dist/utils/retryUtils.d.ts +19 -0
  26. package/dist/utils/retryUtils.js +55 -0
  27. package/dist/utils/retryUtils.js.map +1 -0
  28. package/dist/utils/schemas.d.ts +15 -3
  29. package/dist/utils/schemas.js +27 -2
  30. package/dist/utils/schemas.js.map +1 -1
  31. package/dist/utils/typeGuards.d.ts +1 -1
  32. package/dist/utils/typeGuards.js +3 -3
  33. package/dist/utils/typeGuards.js.map +1 -1
  34. package/package.json +2 -1
  35. package/src/__tests__/services/transactionService.test.ts +166 -0
  36. package/src/hooks/useBalance.ts +15 -12
  37. package/src/hooks/useTransactions.ts +98 -0
  38. package/src/index.ts +6 -0
  39. package/src/provider/WdkAppProvider.tsx +13 -1
  40. package/src/services/transactionService.ts +195 -0
  41. package/src/store/indexerConfigStore.ts +19 -0
  42. package/src/types.ts +36 -0
  43. package/src/utils/constants.ts +6 -0
  44. package/src/utils/mmkvKeyManager.ts +3 -3
  45. package/src/utils/operationMutex.ts +1 -1
  46. package/dist/__mocks__/react.d.ts +0 -40
  47. package/dist/__mocks__/react.js +0 -33
  48. package/dist/__mocks__/react.js.map +0 -1
  49. package/dist/__mocks__/secureStorage.d.ts +0 -27
  50. package/dist/__mocks__/secureStorage.js +0 -81
  51. package/dist/__mocks__/secureStorage.js.map +0 -1
  52. package/dist/__tests__/hooks/useBalance.test.d.ts +0 -6
  53. package/dist/__tests__/hooks/useBalance.test.js +0 -217
  54. package/dist/__tests__/hooks/useBalance.test.js.map +0 -1
  55. package/dist/__tests__/hooks/useWallet.test.d.ts +0 -6
  56. package/dist/__tests__/hooks/useWallet.test.js +0 -105
  57. package/dist/__tests__/hooks/useWallet.test.js.map +0 -1
  58. package/dist/__tests__/hooks/useWalletManager.test.d.ts +0 -6
  59. package/dist/__tests__/hooks/useWalletManager.test.js +0 -193
  60. package/dist/__tests__/hooks/useWalletManager.test.js.map +0 -1
  61. package/dist/__tests__/hooks/useWdkApp.test.d.ts +0 -6
  62. package/dist/__tests__/hooks/useWdkApp.test.js +0 -55
  63. package/dist/__tests__/hooks/useWdkApp.test.js.map +0 -1
  64. package/dist/__tests__/hooks/useWorklet.test.d.ts +0 -6
  65. package/dist/__tests__/hooks/useWorklet.test.js +0 -168
  66. package/dist/__tests__/hooks/useWorklet.test.js.map +0 -1
  67. package/dist/__tests__/provider/WdkAppProvider.test.d.ts +0 -6
  68. package/dist/__tests__/provider/WdkAppProvider.test.js +0 -57
  69. package/dist/__tests__/provider/WdkAppProvider.test.js.map +0 -1
  70. package/dist/__tests__/services/accountService.test.d.ts +0 -6
  71. package/dist/__tests__/services/accountService.test.js +0 -263
  72. package/dist/__tests__/services/accountService.test.js.map +0 -1
  73. package/dist/__tests__/services/addressService.test.d.ts +0 -6
  74. package/dist/__tests__/services/addressService.test.js +0 -258
  75. package/dist/__tests__/services/addressService.test.js.map +0 -1
  76. package/dist/__tests__/services/balanceService.test.d.ts +0 -6
  77. package/dist/__tests__/services/balanceService.test.js +0 -333
  78. package/dist/__tests__/services/balanceService.test.js.map +0 -1
  79. package/dist/__tests__/services/walletSetupService.test.d.ts +0 -6
  80. package/dist/__tests__/services/walletSetupService.test.js +0 -304
  81. package/dist/__tests__/services/walletSetupService.test.js.map +0 -1
  82. package/dist/__tests__/services/walletSwitchingService.test.d.ts +0 -6
  83. package/dist/__tests__/services/walletSwitchingService.test.js +0 -209
  84. package/dist/__tests__/services/walletSwitchingService.test.js.map +0 -1
  85. package/dist/__tests__/services/workletLifecycleService.test.d.ts +0 -6
  86. package/dist/__tests__/services/workletLifecycleService.test.js +0 -451
  87. package/dist/__tests__/services/workletLifecycleService.test.js.map +0 -1
  88. package/dist/__tests__/setup.d.ts +0 -4
  89. package/dist/__tests__/setup.js +0 -49
  90. package/dist/__tests__/setup.js.map +0 -1
  91. package/dist/__tests__/storage/mmkvStorage.test.d.ts +0 -6
  92. package/dist/__tests__/storage/mmkvStorage.test.js +0 -213
  93. package/dist/__tests__/storage/mmkvStorage.test.js.map +0 -1
  94. package/dist/__tests__/store/walletStore.test.d.ts +0 -4
  95. package/dist/__tests__/store/walletStore.test.js +0 -86
  96. package/dist/__tests__/store/walletStore.test.js.map +0 -1
  97. package/dist/__tests__/store/workletStore.test.d.ts +0 -4
  98. package/dist/__tests__/store/workletStore.test.js +0 -255
  99. package/dist/__tests__/store/workletStore.test.js.map +0 -1
  100. package/dist/__tests__/types/hrpc.test.d.ts +0 -4
  101. package/dist/__tests__/types/hrpc.test.js +0 -99
  102. package/dist/__tests__/types/hrpc.test.js.map +0 -1
  103. package/dist/__tests__/utils/balanceUtils.test.d.ts +0 -4
  104. package/dist/__tests__/utils/balanceUtils.test.js +0 -133
  105. package/dist/__tests__/utils/balanceUtils.test.js.map +0 -1
  106. package/dist/__tests__/utils/errorHandling.test.d.ts +0 -6
  107. package/dist/__tests__/utils/errorHandling.test.js +0 -119
  108. package/dist/__tests__/utils/errorHandling.test.js.map +0 -1
  109. package/dist/__tests__/utils/errorUtils.test.d.ts +0 -4
  110. package/dist/__tests__/utils/errorUtils.test.js +0 -133
  111. package/dist/__tests__/utils/errorUtils.test.js.map +0 -1
  112. package/dist/__tests__/utils/initializationState.test.d.ts +0 -6
  113. package/dist/__tests__/utils/initializationState.test.js +0 -243
  114. package/dist/__tests__/utils/initializationState.test.js.map +0 -1
  115. package/dist/__tests__/utils/jsonUtils.test.d.ts +0 -4
  116. package/dist/__tests__/utils/jsonUtils.test.js +0 -118
  117. package/dist/__tests__/utils/jsonUtils.test.js.map +0 -1
  118. package/dist/__tests__/utils/mmkvKeyManager.test.d.ts +0 -6
  119. package/dist/__tests__/utils/mmkvKeyManager.test.js +0 -158
  120. package/dist/__tests__/utils/mmkvKeyManager.test.js.map +0 -1
  121. package/dist/__tests__/utils/mnemonicUtils.test.d.ts +0 -4
  122. package/dist/__tests__/utils/mnemonicUtils.test.js +0 -40
  123. package/dist/__tests__/utils/mnemonicUtils.test.js.map +0 -1
  124. package/dist/__tests__/utils/raceConditions.test.d.ts +0 -6
  125. package/dist/__tests__/utils/raceConditions.test.js +0 -239
  126. package/dist/__tests__/utils/raceConditions.test.js.map +0 -1
  127. package/dist/__tests__/utils/result.test.d.ts +0 -4
  128. package/dist/__tests__/utils/result.test.js +0 -66
  129. package/dist/__tests__/utils/result.test.js.map +0 -1
  130. package/dist/__tests__/utils/schemas.test.d.ts +0 -4
  131. package/dist/__tests__/utils/schemas.test.js +0 -66
  132. package/dist/__tests__/utils/schemas.test.js.map +0 -1
  133. package/dist/__tests__/utils/storeHelpers.test.d.ts +0 -4
  134. package/dist/__tests__/utils/storeHelpers.test.js +0 -227
  135. package/dist/__tests__/utils/storeHelpers.test.js.map +0 -1
  136. package/dist/__tests__/utils/typeGuards.test.d.ts +0 -4
  137. package/dist/__tests__/utils/typeGuards.test.js +0 -175
  138. package/dist/__tests__/utils/typeGuards.test.js.map +0 -1
  139. package/dist/__tests__/utils/validation.test.d.ts +0 -4
  140. package/dist/__tests__/utils/validation.test.js +0 -167
  141. package/dist/__tests__/utils/validation.test.js.map +0 -1
  142. package/dist/__tests__/utils/walletUtils.test.d.ts +0 -4
  143. package/dist/__tests__/utils/walletUtils.test.js +0 -156
  144. package/dist/__tests__/utils/walletUtils.test.js.map +0 -1
  145. package/dist/utils/types.js +0 -6
  146. package/dist/utils/utils/schemas.js +0 -173
  147. package/dist/utils/utils/validation.js +0 -151
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Transaction hooks with TanStack Query
3
+ *
4
+ * Fetches wallet activity from the WDK Indexer (token-transfers per network/token/address).
5
+ * Requires indexerConfig on WdkAppProvider (typically from EXPO_PUBLIC_WDK_INDEXER_* in the app).
6
+ */
7
+
8
+ import { useQuery } from '@tanstack/react-query'
9
+ import { useShallow } from 'zustand/react/shallow'
10
+
11
+ import { TransactionService } from '../services/transactionService'
12
+ import { getWalletStore } from '../store/walletStore'
13
+ import { getWorkletStore } from '../store/workletStore'
14
+ import { isIndexerConfigured } from '../store/indexerConfigStore'
15
+ import { resolveWalletId } from '../utils/storeHelpers'
16
+ import {
17
+ DEFAULT_QUERY_GC_TIME_MS,
18
+ DEFAULT_QUERY_STALE_TIME_MS,
19
+ } from '../utils/constants'
20
+ import type { TokenConfigProvider, WalletTransaction } from '../types'
21
+
22
+ export interface WalletTransactionsQueryOptions {
23
+ enabled?: boolean
24
+ staleTime?: number
25
+ refetchInterval?: number | false
26
+ walletId?: string
27
+ }
28
+
29
+ export const transactionQueryKeys = {
30
+ all: ['transactions'] as const,
31
+ byWallet: (walletId: string, accountIndex: number) =>
32
+ ['transactions', 'wallet', walletId, accountIndex] as const,
33
+ }
34
+
35
+ function resolveTokenConfigs(tokenConfigs: TokenConfigProvider) {
36
+ return typeof tokenConfigs === 'function' ? tokenConfigs() : tokenConfigs
37
+ }
38
+
39
+ function addressesFingerprint(
40
+ addresses: Record<string, Record<number, string>>,
41
+ accountIndex: number
42
+ ): string {
43
+ const parts: string[] = []
44
+ for (const network of Object.keys(addresses).sort()) {
45
+ const address = addresses[network]?.[accountIndex]
46
+ if (address) {
47
+ parts.push(`${network}:${address.toLowerCase()}`)
48
+ }
49
+ }
50
+ return parts.join('|')
51
+ }
52
+
53
+ async function fetchWalletTransactionsForAccount(
54
+ walletId: string,
55
+ accountIndex: number,
56
+ tokenConfigs: TokenConfigProvider
57
+ ): Promise<WalletTransaction[]> {
58
+ const walletStore = getWalletStore()
59
+ const addresses = walletStore.getState().addresses[walletId] ?? {}
60
+
61
+ return TransactionService.resolveWalletTransactions({
62
+ addresses,
63
+ accountIndex,
64
+ tokenConfigs: resolveTokenConfigs(tokenConfigs),
65
+ })
66
+ }
67
+
68
+ /**
69
+ * Fetch aggregated wallet transactions from the WDK Indexer.
70
+ */
71
+ export function useWalletTransactions(
72
+ accountIndex: number,
73
+ tokenConfigs: TokenConfigProvider,
74
+ options?: WalletTransactionsQueryOptions
75
+ ) {
76
+ const workletStore = getWorkletStore()
77
+ const walletStore = getWalletStore()
78
+ const isInitialized = workletStore.getState().isInitialized
79
+ const walletId = resolveWalletId(options?.walletId)
80
+ const addresses = walletStore(
81
+ useShallow((state) => state.addresses[walletId] ?? {})
82
+ )
83
+ const addressKey = addressesFingerprint(addresses, accountIndex)
84
+ const indexerReady = isIndexerConfigured()
85
+
86
+ return useQuery({
87
+ queryKey: [...transactionQueryKeys.byWallet(walletId, accountIndex), addressKey],
88
+ queryFn: () => fetchWalletTransactionsForAccount(walletId, accountIndex, tokenConfigs),
89
+ enabled:
90
+ (options?.enabled !== false) &&
91
+ isInitialized &&
92
+ indexerReady &&
93
+ addressKey.length > 0,
94
+ staleTime: options?.staleTime ?? DEFAULT_QUERY_STALE_TIME_MS,
95
+ gcTime: DEFAULT_QUERY_GC_TIME_MS,
96
+ refetchInterval: options?.refetchInterval,
97
+ })
98
+ }
package/src/index.ts CHANGED
@@ -20,6 +20,9 @@ export type {
20
20
  TokenConfigProvider,
21
21
  TokenHelpers,
22
22
  WalletStore,
23
+ IndexerConfig,
24
+ WalletTransaction,
25
+ IndexerTokenTransfersResponse,
23
26
  } from './types'
24
27
 
25
28
  // HRPC Type Extensions (for extending HRPC functionality)
@@ -38,6 +41,8 @@ export { useWdkApp } from './hooks/useWdkApp'
38
41
  export { useWalletManager } from './hooks/useWalletManager'
39
42
  export type { UseWalletManagerResult, WalletInfo } from './hooks/useWalletManager'
40
43
  export { useBalance, useBalancesForWallet, useBalancesForWallets, useRefreshBalance, balanceQueryKeys } from './hooks/useBalance'
44
+ export { useWalletTransactions, transactionQueryKeys } from './hooks/useTransactions'
45
+ export type { WalletTransactionsQueryOptions } from './hooks/useTransactions'
41
46
  export type { AccountInfo } from './store/walletStore'
42
47
 
43
48
  // Validation Utilities (for validating configs before use)
@@ -84,6 +89,7 @@ export { WorkletLifecycleService } from './services/workletLifecycleService'
84
89
  export { AddressService } from './services/addressService'
85
90
  export { AccountService } from './services/accountService'
86
91
  export { BalanceService } from './services/balanceService'
92
+ export { TransactionService } from './services/transactionService'
87
93
  export { WalletSetupService } from './services/walletSetupService'
88
94
  export { WalletSwitchingService } from './services/walletSwitchingService'
89
95
 
@@ -48,7 +48,8 @@ import { log, logError } from '../utils/logger'
48
48
  import { validateNetworkConfigs, validateTokenConfigs } from '../utils/validation'
49
49
  import { DEFAULT_QUERY_STALE_TIME_MS, DEFAULT_QUERY_GC_TIME_MS } from '../utils/constants'
50
50
  import { InitializationStatus, AppStatus, isAppReadyStatus, isAppInProgressStatus, getCombinedStatus, getWorkletStatus } from '../utils/initializationState'
51
- import type { NetworkConfigs, TokenConfigs } from '../types'
51
+ import type { IndexerConfig, NetworkConfigs, TokenConfigs } from '../types'
52
+ import { setIndexerConfig } from '../store/indexerConfigStore'
52
53
 
53
54
 
54
55
 
@@ -134,6 +135,11 @@ export interface WdkAppProviderProps {
134
135
  networkConfigs: NetworkConfigs
135
136
  /** Token configurations for balance fetching */
136
137
  tokenConfigs: TokenConfigs
138
+ /**
139
+ * WDK Indexer configuration for transaction history.
140
+ * Apps typically pass EXPO_PUBLIC_WDK_INDEXER_BASE_URL and EXPO_PUBLIC_WDK_INDEXER_API_KEY.
141
+ */
142
+ indexerConfig?: IndexerConfig
137
143
  /** Enable automatic wallet initialization on app restart (default: true) */
138
144
  enableAutoInitialization?: boolean
139
145
  /**
@@ -190,6 +196,7 @@ const deepEqualityFn = (a: any, b: any) => {
190
196
  export function WdkAppProvider({
191
197
  networkConfigs,
192
198
  tokenConfigs,
199
+ indexerConfig,
193
200
  enableAutoInitialization = true,
194
201
  currentUserId,
195
202
  children,
@@ -267,6 +274,11 @@ export function WdkAppProvider({
267
274
  }
268
275
  }, [networkConfigs, tokenConfigs])
269
276
 
277
+ useEffect(() => {
278
+ setIndexerConfig(indexerConfig ?? null)
279
+ return () => setIndexerConfig(null)
280
+ }, [indexerConfig])
281
+
270
282
  // Worklet state - read from workletStore via hook
271
283
  const workletHookState = useWorklet()
272
284
  const {
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Transaction Service
3
+ *
4
+ * Fetches wallet transaction history from the WDK Indexer API.
5
+ * Endpoint: GET /api/v1/{network}/{token}/{address}/token-transfers
6
+ */
7
+
8
+ import { getIndexerConfig } from '../store/indexerConfigStore'
9
+ import { BALANCE_FETCH_INTRA_NETWORK_STAGGER_MS, BALANCE_FETCH_STAGGER_MS } from '../utils/constants'
10
+ import { logError, logWarn } from '../utils/logger'
11
+ import { delay, withTransientRetry } from '../utils/retryUtils'
12
+ import type { IndexerTokenTransfersResponse, TokenConfigs, WalletTransaction } from '../types'
13
+
14
+ export interface FetchWalletTransactionsParams {
15
+ addresses: Record<string, Record<number, string>>
16
+ accountIndex: number
17
+ tokenConfigs: TokenConfigs
18
+ }
19
+
20
+ function normalizeBaseUrl(baseUrl: string): string {
21
+ return baseUrl.replace(/\/$/, '')
22
+ }
23
+
24
+ function tokenSlugFromConfig(symbol: string): string {
25
+ return symbol.toLowerCase()
26
+ }
27
+
28
+ function transactionDedupeKey(tx: WalletTransaction): string {
29
+ return [
30
+ tx.blockchain,
31
+ tx.transactionHash,
32
+ tx.token,
33
+ tx.transferIndex ?? '',
34
+ tx.logIndex ?? '',
35
+ tx.from,
36
+ tx.to,
37
+ tx.amount,
38
+ ].join('|')
39
+ }
40
+
41
+ function parseIndexerTransfers(payload: unknown): WalletTransaction[] {
42
+ if (!payload || typeof payload !== 'object') {
43
+ return []
44
+ }
45
+ const transfers = (payload as IndexerTokenTransfersResponse).transfers
46
+ if (!Array.isArray(transfers)) {
47
+ return []
48
+ }
49
+
50
+ const parsed: WalletTransaction[] = []
51
+ for (const item of transfers) {
52
+ if (!item || typeof item !== 'object') continue
53
+ const tx = item as Partial<WalletTransaction>
54
+ if (
55
+ typeof tx.transactionHash !== 'string' ||
56
+ typeof tx.token !== 'string' ||
57
+ typeof tx.amount !== 'string' ||
58
+ typeof tx.timestamp !== 'number' ||
59
+ typeof tx.from !== 'string' ||
60
+ typeof tx.to !== 'string'
61
+ ) {
62
+ continue
63
+ }
64
+ parsed.push({
65
+ blockchain: typeof tx.blockchain === 'string' ? tx.blockchain : '',
66
+ blockNumber: typeof tx.blockNumber === 'number' ? tx.blockNumber : undefined,
67
+ transactionHash: tx.transactionHash,
68
+ transferIndex: typeof tx.transferIndex === 'number' ? tx.transferIndex : undefined,
69
+ token: tx.token.toLowerCase(),
70
+ amount: tx.amount,
71
+ timestamp: tx.timestamp,
72
+ transactionIndex: typeof tx.transactionIndex === 'number' ? tx.transactionIndex : undefined,
73
+ logIndex: typeof tx.logIndex === 'number' ? tx.logIndex : undefined,
74
+ from: tx.from,
75
+ to: tx.to,
76
+ label: typeof tx.label === 'string' ? tx.label : undefined,
77
+ })
78
+ }
79
+ return parsed
80
+ }
81
+
82
+ export class TransactionService {
83
+ /**
84
+ * Fetch token transfers for one network/token/address from the indexer.
85
+ */
86
+ static async fetchTokenTransfers(
87
+ network: string,
88
+ tokenSlug: string,
89
+ address: string
90
+ ): Promise<WalletTransaction[]> {
91
+ const config = getIndexerConfig()
92
+ if (!config?.baseUrl || !config.apiKey) {
93
+ throw new Error('WDK Indexer is not configured')
94
+ }
95
+
96
+ const url = `${normalizeBaseUrl(config.baseUrl)}/api/v1/${encodeURIComponent(network)}/${encodeURIComponent(tokenSlug)}/${encodeURIComponent(address)}/token-transfers`
97
+
98
+ const response = await withTransientRetry(
99
+ async () => {
100
+ const res = await fetch(url, {
101
+ method: 'GET',
102
+ headers: {
103
+ 'x-api-key': config.apiKey,
104
+ Accept: 'application/json',
105
+ },
106
+ })
107
+
108
+ if (res.status === 404) {
109
+ return { ok: true as const, transfers: [] as WalletTransaction[] }
110
+ }
111
+
112
+ if (!res.ok) {
113
+ const text = await res.text().catch(() => '')
114
+ throw new Error(
115
+ `Indexer token-transfers failed (${res.status}) for ${network}/${tokenSlug}: ${text.slice(0, 200)}`
116
+ )
117
+ }
118
+
119
+ const json: unknown = await res.json()
120
+ return { ok: true as const, transfers: parseIndexerTransfers(json) }
121
+ },
122
+ { label: `${network}/${tokenSlug} token-transfers` }
123
+ )
124
+
125
+ return response.transfers
126
+ }
127
+
128
+ /**
129
+ * Fetch transfers for all configured networks/tokens for one account index.
130
+ */
131
+ static async fetchWalletTransactions(
132
+ params: FetchWalletTransactionsParams
133
+ ): Promise<WalletTransaction[]> {
134
+ const { addresses, accountIndex, tokenConfigs } = params
135
+ const networks = Object.keys(tokenConfigs)
136
+ const deduped = new Map<string, WalletTransaction>()
137
+ let isFirstNetwork = true
138
+
139
+ for (const network of networks) {
140
+ const address = addresses[network]?.[accountIndex]
141
+ if (!address) {
142
+ continue
143
+ }
144
+
145
+ if (!isFirstNetwork) {
146
+ await delay(BALANCE_FETCH_STAGGER_MS)
147
+ }
148
+ isFirstNetwork = false
149
+
150
+ const networkTokens = tokenConfigs[network]
151
+ if (!networkTokens) continue
152
+
153
+ const tokens = [networkTokens.native, ...networkTokens.tokens]
154
+ for (let i = 0; i < tokens.length; i++) {
155
+ const token = tokens[i]
156
+ if (!token) continue
157
+ if (i > 0) {
158
+ await delay(BALANCE_FETCH_INTRA_NETWORK_STAGGER_MS)
159
+ }
160
+
161
+ const tokenSlug = tokenSlugFromConfig(token.symbol)
162
+ try {
163
+ const transfers = await this.fetchTokenTransfers(network, tokenSlug, address)
164
+ for (const tx of transfers) {
165
+ const withBlockchain = tx.blockchain
166
+ ? tx
167
+ : { ...tx, blockchain: network }
168
+ deduped.set(transactionDedupeKey(withBlockchain), withBlockchain)
169
+ }
170
+ } catch (error) {
171
+ logError(
172
+ `Failed to fetch token transfers for ${network}/${tokenSlug}:`,
173
+ error
174
+ )
175
+ }
176
+ }
177
+ }
178
+
179
+ return Array.from(deduped.values()).sort((a, b) => b.timestamp - a.timestamp)
180
+ }
181
+
182
+ /**
183
+ * Resolve wallet transactions (alias for fetchWalletTransactions).
184
+ * Matches the legacy resolveWalletTransactions name used by consuming apps.
185
+ */
186
+ static async resolveWalletTransactions(
187
+ params: FetchWalletTransactionsParams
188
+ ): Promise<WalletTransaction[]> {
189
+ if (!getIndexerConfig()) {
190
+ logWarn('[TransactionService] Indexer not configured — returning empty transaction list')
191
+ return []
192
+ }
193
+ return this.fetchWalletTransactions(params)
194
+ }
195
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Runtime WDK Indexer configuration (set by WdkAppProvider from app env).
3
+ */
4
+
5
+ import type { IndexerConfig } from '../types'
6
+
7
+ let indexerConfig: IndexerConfig | null = null
8
+
9
+ export function setIndexerConfig(config: IndexerConfig | null): void {
10
+ indexerConfig = config
11
+ }
12
+
13
+ export function getIndexerConfig(): IndexerConfig | null {
14
+ return indexerConfig
15
+ }
16
+
17
+ export function isIndexerConfigured(): boolean {
18
+ return Boolean(indexerConfig?.baseUrl && indexerConfig?.apiKey)
19
+ }
package/src/types.ts CHANGED
@@ -157,6 +157,42 @@ export interface BalanceFetchResult {
157
157
  */
158
158
  export type TokenConfigProvider = TokenConfigs | (() => TokenConfigs)
159
159
 
160
+ /**
161
+ * WDK Indexer API configuration (balance/transaction history).
162
+ */
163
+ export interface IndexerConfig {
164
+ /** Base URL, e.g. https://wdk-api.tether.io */
165
+ baseUrl: string
166
+ /** API key sent as x-api-key header */
167
+ apiKey: string
168
+ }
169
+
170
+ /**
171
+ * Token transfer from the WDK Indexer token-transfers endpoint.
172
+ */
173
+ export interface WalletTransaction {
174
+ blockchain: string
175
+ blockNumber?: number
176
+ transactionHash: string
177
+ transferIndex?: number
178
+ token: string
179
+ amount: string
180
+ /** Unix timestamp in seconds (indexer convention) */
181
+ timestamp: number
182
+ transactionIndex?: number
183
+ logIndex?: number
184
+ from: string
185
+ to: string
186
+ label?: string
187
+ }
188
+
189
+ /**
190
+ * Raw indexer response for token-transfers.
191
+ */
192
+ export interface IndexerTokenTransfersResponse {
193
+ transfers?: WalletTransaction[]
194
+ }
195
+
160
196
  /**
161
197
  * Token Helpers
162
198
  *
@@ -73,6 +73,12 @@ export const DEFAULT_QUERY_GC_TIME_MS = 5 * 60 * 1000
73
73
  */
74
74
  export const BALANCE_FETCH_STAGGER_MS = 400
75
75
 
76
+ /**
77
+ * Delay between token balance fetches within the same network (ms).
78
+ * TON jetton balances issue multiple RPC calls each; serializing avoids 429 bursts.
79
+ */
80
+ export const BALANCE_FETCH_INTRA_NETWORK_STAGGER_MS = 300
81
+
76
82
  /**
77
83
  * Max attempts for transient blockchain/RPC errors (429, 5xx, network timeouts).
78
84
  */
@@ -28,9 +28,9 @@ export type AccountIdentifier = string
28
28
  * @returns Base64 encoded string
29
29
  */
30
30
  function bytesToBase64(bytes: Uint8Array): string {
31
- // Try to use Buffer if available (common in React Native with polyfills)
32
- if (typeof Buffer !== 'undefined') {
33
- return Buffer.from(bytes).toString('base64')
31
+ const bufferCtor = (globalThis as { Buffer?: { from(data: Uint8Array): { toString(encoding: string): string } } }).Buffer
32
+ if (bufferCtor) {
33
+ return bufferCtor.from(bytes).toString('base64')
34
34
  }
35
35
 
36
36
  // Fallback to manual encoding for environments without Buffer
@@ -124,7 +124,7 @@ export async function withOperationMutex<T>(
124
124
  }
125
125
 
126
126
  // Set up timeout protection
127
- let timeoutId: NodeJS.Timeout | null = null
127
+ let timeoutId: ReturnType<typeof setTimeout> | null = null
128
128
  let timeoutExceeded = false
129
129
 
130
130
  const timeoutPromise = new Promise<never>((_, reject) => {
@@ -1,40 +0,0 @@
1
- /**
2
- * Mock React module for Jest tests
3
- * Required by Zustand v5 which imports React
4
- */
5
- export declare const useState: jest.Mock<any[], [initial: any], any>;
6
- export declare const useEffect: jest.Mock<any, any, any>;
7
- export declare const useCallback: jest.Mock<any, [fn: any], any>;
8
- export declare const useMemo: jest.Mock<any, [fn: any], any>;
9
- export declare const useRef: jest.Mock<{
10
- current: any;
11
- }, [initial: any], any>;
12
- export declare const createContext: jest.Mock<{
13
- Provider: ({ children }: any) => any;
14
- Consumer: ({ children }: any) => any;
15
- }, [], any>;
16
- export declare const useContext: jest.Mock<any, any, any>;
17
- export declare const useReducer: jest.Mock<any[], [reducer: any, initial: any], any>;
18
- export declare class Component {
19
- }
20
- export declare const Fragment: ({ children }: any) => any;
21
- export declare const version = "18.0.0";
22
- declare const _default: {
23
- useState: jest.Mock<any[], [initial: any], any>;
24
- useEffect: jest.Mock<any, any, any>;
25
- useCallback: jest.Mock<any, [fn: any], any>;
26
- useMemo: jest.Mock<any, [fn: any], any>;
27
- useRef: jest.Mock<{
28
- current: any;
29
- }, [initial: any], any>;
30
- createContext: jest.Mock<{
31
- Provider: ({ children }: any) => any;
32
- Consumer: ({ children }: any) => any;
33
- }, [], any>;
34
- useContext: jest.Mock<any, any, any>;
35
- useReducer: jest.Mock<any[], [reducer: any, initial: any], any>;
36
- Component: typeof Component;
37
- Fragment: ({ children }: any) => any;
38
- version: string;
39
- };
40
- export default _default;
@@ -1,33 +0,0 @@
1
- /**
2
- * Mock React module for Jest tests
3
- * Required by Zustand v5 which imports React
4
- */
5
- export const useState = jest.fn((initial) => [initial, jest.fn()]);
6
- export const useEffect = jest.fn();
7
- export const useCallback = jest.fn((fn) => fn);
8
- export const useMemo = jest.fn((fn) => fn());
9
- export const useRef = jest.fn((initial) => ({ current: initial }));
10
- export const createContext = jest.fn(() => ({
11
- Provider: ({ children }) => children,
12
- Consumer: ({ children }) => children,
13
- }));
14
- export const useContext = jest.fn();
15
- export const useReducer = jest.fn((reducer, initial) => [initial, jest.fn()]);
16
- export class Component {
17
- }
18
- export const Fragment = ({ children }) => children;
19
- export const version = '18.0.0';
20
- export default {
21
- useState,
22
- useEffect,
23
- useCallback,
24
- useMemo,
25
- useRef,
26
- createContext,
27
- useContext,
28
- useReducer,
29
- Component,
30
- Fragment,
31
- version,
32
- };
33
- //# sourceMappingURL=react.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/__mocks__/react.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,OAAY,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AACvE,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;AAClC,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;AACnD,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AACjD,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,OAAY,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;AACvE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1C,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAO,EAAE,EAAE,CAAC,QAAQ;IACzC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAO,EAAE,EAAE,CAAC,QAAQ;CAC1C,CAAC,CAAC,CAAA;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;AACnC,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,OAAY,EAAE,OAAY,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AACvF,MAAM,OAAO,SAAS;CAAG;AACzB,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAE,QAAQ,EAAO,EAAE,EAAE,CAAC,QAAQ,CAAA;AACvD,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAA;AAE/B,eAAe;IACb,QAAQ;IACR,SAAS;IACT,WAAW;IACX,OAAO;IACP,MAAM;IACN,aAAa;IACb,UAAU;IACV,UAAU;IACV,SAAS;IACT,QAAQ;IACR,OAAO;CACR,CAAA"}
@@ -1,27 +0,0 @@
1
- /**
2
- * Mock SecureStorage for testing
3
- *
4
- * Supports identifier parameter for multi-wallet testing
5
- */
6
- export declare const mockSecureStorage: {
7
- authenticate: jest.Mock<Promise<boolean>, [], any>;
8
- hasWallet: jest.Mock<Promise<boolean>, [identifier?: string | undefined], any>;
9
- setEncryptionKey: jest.Mock<Promise<void>, [key: string, identifier?: string | undefined], any>;
10
- getEncryptionKey: jest.Mock<Promise<string | null>, [identifier?: string | undefined], any>;
11
- setEncryptedSeed: jest.Mock<Promise<void>, [seed: string, identifier?: string | undefined], any>;
12
- getEncryptedSeed: jest.Mock<Promise<string | null>, [identifier?: string | undefined], any>;
13
- setEncryptedEntropy: jest.Mock<Promise<void>, [entropy: string, identifier?: string | undefined], any>;
14
- getEncryptedEntropy: jest.Mock<Promise<string | null>, [identifier?: string | undefined], any>;
15
- getAllEncrypted: jest.Mock<Promise<{
16
- encryptedSeed: string | null;
17
- encryptedEntropy: string | null;
18
- encryptionKey: string | null;
19
- }>, [identifier?: string | undefined], any>;
20
- clearAll: jest.Mock<Promise<void>, [], any>;
21
- isBiometricAvailable: jest.Mock<Promise<boolean>, [], any>;
22
- isDeviceSecurityEnabled: jest.Mock<Promise<boolean>, [], any>;
23
- deleteWallet: jest.Mock<Promise<void>, [identifier?: string | undefined], any>;
24
- cleanup: jest.Mock<any, any, any>;
25
- _clearStorage: () => void;
26
- };
27
- export default mockSecureStorage;
@@ -1,81 +0,0 @@
1
- /**
2
- * Mock SecureStorage for testing
3
- *
4
- * Supports identifier parameter for multi-wallet testing
5
- */
6
- // Internal storage to simulate per-identifier wallet storage
7
- const storage = {};
8
- const getStorageKey = (identifier) => {
9
- return identifier || 'default';
10
- };
11
- export const mockSecureStorage = {
12
- authenticate: jest.fn(() => Promise.resolve(true)),
13
- hasWallet: jest.fn((identifier) => {
14
- const key = getStorageKey(identifier);
15
- const wallet = storage[key];
16
- return Promise.resolve(wallet !== undefined && wallet.encryptionKey !== null);
17
- }),
18
- setEncryptionKey: jest.fn((key, identifier) => {
19
- const storageKey = getStorageKey(identifier);
20
- if (!storage[storageKey]) {
21
- storage[storageKey] = { encryptionKey: null, encryptedSeed: null, encryptedEntropy: null };
22
- }
23
- storage[storageKey].encryptionKey = key;
24
- return Promise.resolve();
25
- }),
26
- getEncryptionKey: jest.fn((identifier) => {
27
- const storageKey = getStorageKey(identifier);
28
- return Promise.resolve(storage[storageKey]?.encryptionKey || null);
29
- }),
30
- setEncryptedSeed: jest.fn((seed, identifier) => {
31
- const storageKey = getStorageKey(identifier);
32
- if (!storage[storageKey]) {
33
- storage[storageKey] = { encryptionKey: null, encryptedSeed: null, encryptedEntropy: null };
34
- }
35
- storage[storageKey].encryptedSeed = seed;
36
- return Promise.resolve();
37
- }),
38
- getEncryptedSeed: jest.fn((identifier) => {
39
- const storageKey = getStorageKey(identifier);
40
- return Promise.resolve(storage[storageKey]?.encryptedSeed || null);
41
- }),
42
- setEncryptedEntropy: jest.fn((entropy, identifier) => {
43
- const storageKey = getStorageKey(identifier);
44
- if (!storage[storageKey]) {
45
- storage[storageKey] = { encryptionKey: null, encryptedSeed: null, encryptedEntropy: null };
46
- }
47
- storage[storageKey].encryptedEntropy = entropy;
48
- return Promise.resolve();
49
- }),
50
- getEncryptedEntropy: jest.fn((identifier) => {
51
- const storageKey = getStorageKey(identifier);
52
- return Promise.resolve(storage[storageKey]?.encryptedEntropy || null);
53
- }),
54
- getAllEncrypted: jest.fn((identifier) => {
55
- const storageKey = getStorageKey(identifier);
56
- const wallet = storage[storageKey];
57
- return Promise.resolve({
58
- encryptedSeed: wallet?.encryptedSeed || null,
59
- encryptedEntropy: wallet?.encryptedEntropy || null,
60
- encryptionKey: wallet?.encryptionKey || null,
61
- });
62
- }),
63
- clearAll: jest.fn(() => {
64
- Object.keys(storage).forEach(key => delete storage[key]);
65
- return Promise.resolve();
66
- }),
67
- isBiometricAvailable: jest.fn(() => Promise.resolve(true)),
68
- isDeviceSecurityEnabled: jest.fn(() => Promise.resolve(true)),
69
- deleteWallet: jest.fn((identifier) => {
70
- const storageKey = getStorageKey(identifier);
71
- delete storage[storageKey];
72
- return Promise.resolve();
73
- }),
74
- cleanup: jest.fn(),
75
- // Helper method to clear storage between tests
76
- _clearStorage: () => {
77
- Object.keys(storage).forEach(key => delete storage[key]);
78
- },
79
- };
80
- export default mockSecureStorage;
81
- //# sourceMappingURL=secureStorage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secureStorage.js","sourceRoot":"","sources":["../../src/__mocks__/secureStorage.ts"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- /**
2
- * Tests for useBalance hook
3
- *
4
- * Tests balance hook logic with TanStack Query integration
5
- */
6
- export {};