@spacesops/wdk-react-native-provider 1.0.0-beta.3

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 (89) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +841 -0
  3. package/lib/module/contexts/constants.js +20 -0
  4. package/lib/module/contexts/constants.js.map +1 -0
  5. package/lib/module/contexts/reducer.js +87 -0
  6. package/lib/module/contexts/reducer.js.map +1 -0
  7. package/lib/module/contexts/types.js +4 -0
  8. package/lib/module/contexts/types.js.map +1 -0
  9. package/lib/module/contexts/wallet-context.js +409 -0
  10. package/lib/module/contexts/wallet-context.js.map +1 -0
  11. package/lib/module/index.js +15 -0
  12. package/lib/module/index.js.map +1 -0
  13. package/lib/module/package.json +1 -0
  14. package/lib/module/polyfills.js +34 -0
  15. package/lib/module/polyfills.js.map +1 -0
  16. package/lib/module/services/wdk-service/bare-api.js +69 -0
  17. package/lib/module/services/wdk-service/bare-api.js.map +1 -0
  18. package/lib/module/services/wdk-service/index.js +506 -0
  19. package/lib/module/services/wdk-service/index.js.map +1 -0
  20. package/lib/module/services/wdk-service/types.js +48 -0
  21. package/lib/module/services/wdk-service/types.js.map +1 -0
  22. package/lib/module/services/wdk-service/wdk-encryption-salt.js +29 -0
  23. package/lib/module/services/wdk-service/wdk-encryption-salt.js.map +1 -0
  24. package/lib/module/services/wdk-service/wdk-secret-manager-storage.js +64 -0
  25. package/lib/module/services/wdk-service/wdk-secret-manager-storage.js.map +1 -0
  26. package/lib/module/services/wdk-service/wdk-secret-manager-worklet.bundle.js +1 -0
  27. package/lib/module/services/wdk-service/wdk-worklet.mobile.bundle.js +1 -0
  28. package/lib/module/spec/hrpc/hrpc.json +66 -0
  29. package/lib/module/spec/hrpc/index.js +121 -0
  30. package/lib/module/spec/hrpc/index.js.map +1 -0
  31. package/lib/module/spec/hrpc/messages.js +291 -0
  32. package/lib/module/spec/hrpc/messages.js.map +1 -0
  33. package/lib/module/spec/schema/index.js +291 -0
  34. package/lib/module/spec/schema/index.js.map +1 -0
  35. package/lib/module/spec/schema/schema.json +186 -0
  36. package/lib/module/utils/get-balances-from-balance-map.js +18 -0
  37. package/lib/module/utils/get-balances-from-balance-map.js.map +1 -0
  38. package/lib/module/utils/get-transactions-from-transaction-map.js +10 -0
  39. package/lib/module/utils/get-transactions-from-transaction-map.js.map +1 -0
  40. package/lib/module/worklet/wdk-secret-manager-worklet.js +106 -0
  41. package/lib/module/worklet/wdk-secret-manager-worklet.js.map +1 -0
  42. package/lib/typescript/package.json +1 -0
  43. package/lib/typescript/src/contexts/constants.d.ts +3 -0
  44. package/lib/typescript/src/contexts/constants.d.ts.map +1 -0
  45. package/lib/typescript/src/contexts/reducer.d.ts +38 -0
  46. package/lib/typescript/src/contexts/reducer.d.ts.map +1 -0
  47. package/lib/typescript/src/contexts/types.d.ts +40 -0
  48. package/lib/typescript/src/contexts/types.d.ts.map +1 -0
  49. package/lib/typescript/src/contexts/wallet-context.d.ts +10 -0
  50. package/lib/typescript/src/contexts/wallet-context.d.ts.map +1 -0
  51. package/lib/typescript/src/index.d.ts +7 -0
  52. package/lib/typescript/src/index.d.ts.map +1 -0
  53. package/lib/typescript/src/polyfills.d.ts +7 -0
  54. package/lib/typescript/src/polyfills.d.ts.map +1 -0
  55. package/lib/typescript/src/services/wdk-service/bare-api.d.ts +32 -0
  56. package/lib/typescript/src/services/wdk-service/bare-api.d.ts.map +1 -0
  57. package/lib/typescript/src/services/wdk-service/index.d.ts +68 -0
  58. package/lib/typescript/src/services/wdk-service/index.d.ts.map +1 -0
  59. package/lib/typescript/src/services/wdk-service/types.d.ts +125 -0
  60. package/lib/typescript/src/services/wdk-service/types.d.ts.map +1 -0
  61. package/lib/typescript/src/services/wdk-service/wdk-encryption-salt.d.ts +6 -0
  62. package/lib/typescript/src/services/wdk-service/wdk-encryption-salt.d.ts.map +1 -0
  63. package/lib/typescript/src/services/wdk-service/wdk-secret-manager-storage.d.ts +13 -0
  64. package/lib/typescript/src/services/wdk-service/wdk-secret-manager-storage.d.ts.map +1 -0
  65. package/lib/typescript/src/utils/get-balances-from-balance-map.d.ts +4 -0
  66. package/lib/typescript/src/utils/get-balances-from-balance-map.d.ts.map +1 -0
  67. package/lib/typescript/src/utils/get-transactions-from-transaction-map.d.ts +4 -0
  68. package/lib/typescript/src/utils/get-transactions-from-transaction-map.d.ts.map +1 -0
  69. package/metro-polyfills.js +89 -0
  70. package/package.json +152 -0
  71. package/src/contexts/constants.ts +19 -0
  72. package/src/contexts/reducer.ts +103 -0
  73. package/src/contexts/types.ts +51 -0
  74. package/src/contexts/wallet-context.tsx +411 -0
  75. package/src/index.tsx +28 -0
  76. package/src/polyfills.ts +31 -0
  77. package/src/services/wdk-service/bare-api.ts +88 -0
  78. package/src/services/wdk-service/index.ts +765 -0
  79. package/src/services/wdk-service/types.ts +137 -0
  80. package/src/services/wdk-service/wdk-encryption-salt.ts +30 -0
  81. package/src/services/wdk-service/wdk-secret-manager-storage.ts +102 -0
  82. package/src/spec/hrpc/hrpc.json +66 -0
  83. package/src/spec/hrpc/index.js +228 -0
  84. package/src/spec/hrpc/messages.js +328 -0
  85. package/src/spec/schema/index.js +328 -0
  86. package/src/spec/schema/schema.json +186 -0
  87. package/src/utils/get-balances-from-balance-map.ts +22 -0
  88. package/src/utils/get-transactions-from-transaction-map.ts +18 -0
  89. package/src/worklet/wdk-secret-manager-worklet.js +118 -0
@@ -0,0 +1,765 @@
1
+ import { HRPC as WdkManager } from '@spacesops/pear-wrk-wdk';
2
+ // @ts-expect-error - bundle file doesn't have type definitions
3
+ import wdkWorkletBundle from './wdk-worklet.mobile.bundle.js';
4
+ import b4a from 'b4a';
5
+ import * as bip39 from 'bip39';
6
+ import Decimal from 'decimal.js';
7
+ // @ts-expect-error - bundle file doesn't have type definitions
8
+ import secretManagerWorkletBundle from './wdk-secret-manager-worklet.bundle.js';
9
+ import { BareWorkletApi, InstanceEnum } from './bare-api';
10
+ import type { ChainsConfig, Transaction, Wallet } from './types';
11
+ import {
12
+ AssetAddressMap,
13
+ AssetBalanceMap,
14
+ AssetTicker,
15
+ NetworkType,
16
+ } from './types';
17
+ import { wdkEncryptionSalt } from './wdk-encryption-salt';
18
+ import {
19
+ WDK_STORAGE_ENTROPY,
20
+ WDK_STORAGE_SALT,
21
+ WDK_STORAGE_SEED,
22
+ WdkSecretManagerStorage,
23
+ } from './wdk-secret-manager-storage';
24
+
25
+ export const SMART_CONTRACT_BALANCE_ADDRESSES = {
26
+ [AssetTicker.USDT]: {
27
+ ethereum: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
28
+ polygon: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
29
+ arbitrum: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
30
+ ton: 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs',
31
+ },
32
+ [AssetTicker.XAUT]: {
33
+ ethereum: '0x68749665FF8D2d112Fa859AA293F07A622782F38',
34
+ polygon: '0xF1815bd50389c46847f0Bda824eC8da914045D14',
35
+ arbitrum: '0x40461291347e1eCbb09499F3371D3f17f10d7159',
36
+ ton: 'EQA1R_LuQCLHlMgOo1S4G7Y7W1cd0FrAkbA10Zq7rddKxi9k',
37
+ },
38
+ };
39
+
40
+ const toNetwork = (n: NetworkType): string => {
41
+ switch (n) {
42
+ case NetworkType.SEGWIT:
43
+ return 'bitcoin';
44
+ case NetworkType.ETHEREUM:
45
+ return 'ethereum';
46
+ case NetworkType.TON:
47
+ return 'ton';
48
+ case NetworkType.POLYGON:
49
+ return 'polygon';
50
+ case NetworkType.ARBITRUM:
51
+ return 'arbitrum';
52
+ case NetworkType.SOLANA:
53
+ return 'solana';
54
+ case NetworkType.TRON:
55
+ return 'tron';
56
+ default:
57
+ return 'bitcoin';
58
+ }
59
+ };
60
+
61
+ interface WalletCache {
62
+ wdk: WdkManager;
63
+ data: Wallet;
64
+ }
65
+
66
+ interface WDKServiceConfig {
67
+ indexer: {
68
+ apiKey: string;
69
+ url: string;
70
+ version: string;
71
+ };
72
+ chains: ChainsConfig;
73
+ }
74
+
75
+ class WDKService {
76
+ private static instance: WDKService;
77
+ private walletManagerCache: Map<string, WalletCache> = new Map();
78
+ private isInitialized = false;
79
+ private wdkManager: WdkManager | null = null;
80
+ private secretManager: any | null = null;
81
+ private config: WDKServiceConfig | undefined;
82
+
83
+ private constructor() {}
84
+
85
+ static getInstance(): WDKService {
86
+ if (!WDKService.instance) {
87
+ WDKService.instance = new WDKService();
88
+ }
89
+ return WDKService.instance;
90
+ }
91
+
92
+ setConfig(config: WDKServiceConfig): void {
93
+ this.config = { ...this.config, ...config };
94
+ }
95
+
96
+ private getIndexerUrl(): string {
97
+ if (!this.config) {
98
+ throw new Error('WDK Service config not set');
99
+ }
100
+
101
+ return this.config.indexer.url;
102
+ }
103
+
104
+ private getIndexerVersion(): string {
105
+ if (!this.config) {
106
+ throw new Error('WDK Service config not set');
107
+ }
108
+
109
+ return this.config.indexer.version;
110
+ }
111
+
112
+ private getIndexerApiKey(): string {
113
+ if (!this.config) {
114
+ throw new Error('WDK Service config not set');
115
+ }
116
+
117
+ return this.config.indexer.apiKey;
118
+ }
119
+
120
+ private getChainsConfig(): any {
121
+ if (!this.config) {
122
+ throw new Error('WDK Service config not set');
123
+ }
124
+
125
+ if (!this.config.chains) {
126
+ throw new Error('Chains config not set');
127
+ }
128
+
129
+ return this.config.chains;
130
+ }
131
+
132
+ async initialize(): Promise<void> {
133
+ if (this.isInitialized) return;
134
+
135
+ try {
136
+ // Start both worklets
137
+ BareWorkletApi.startWorklet(
138
+ InstanceEnum.wdkSecretManager,
139
+ '/secret.manager.worklet.bundle',
140
+ secretManagerWorkletBundle
141
+ );
142
+ BareWorkletApi.startWorklet(
143
+ InstanceEnum.wdkManager,
144
+ '/wdk.manager.worklet.bundle',
145
+ wdkWorkletBundle
146
+ );
147
+
148
+ await new Promise((resolve) => setTimeout(resolve, 200));
149
+
150
+ // Verify both HRPC instances are available
151
+ this.secretManager = BareWorkletApi.hrpcInstances.wdkSecretManager;
152
+ this.wdkManager = BareWorkletApi.hrpcInstances.wdkManager;
153
+
154
+ if (!this.secretManager) {
155
+ throw new Error(
156
+ 'Failed to initialize WDK Secret Manager HRPC instance'
157
+ );
158
+ }
159
+
160
+ if (!this.wdkManager) {
161
+ throw new Error('Failed to initialize WDK Manager HRPC instance');
162
+ }
163
+
164
+ this.isInitialized = true;
165
+ } catch (error) {
166
+ console.error('Failed to initialize WDK services:', error);
167
+ this.isInitialized = false;
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ async clearWallet(): Promise<void> {
173
+ try {
174
+ // 1. Gracefully stop worklet operations if they're running
175
+ // Note: wdkManager HRPC doesn't have a workletStop method, only secretManager does
176
+ if (this.secretManager) {
177
+ try {
178
+ await this.secretManager.commandWorkletStop();
179
+ } catch (error) {
180
+ // Worklet might not be started, ignore
181
+ console.warn('Could not stop secretManager worklet:', error);
182
+ }
183
+ }
184
+
185
+ // 3. Clear all keychain storage (entropy, seed, salt)
186
+ await WdkSecretManagerStorage.resetAllData();
187
+
188
+ // 4. Reset internal state (but keep worklets alive)
189
+ this.walletManagerCache.clear();
190
+
191
+ // Note: We keep this.wdkManager and this.secretManager references intact
192
+ // as the worklets are still running and can be reused
193
+ // We also keep this.isInitialized = true since worklets are still initialized
194
+ } catch (error) {
195
+ console.error('Failed to clear wallet:', error);
196
+ throw error;
197
+ }
198
+ }
199
+
200
+ // WDK API Methods
201
+ async createSeed(params: {
202
+ prf: Buffer | ArrayBuffer | string;
203
+ }): Promise<string> {
204
+ try {
205
+ const salt = wdkEncryptionSalt.generateWdkSalt(params.prf);
206
+ const rpc = BareWorkletApi.hrpcInstances.wdkSecretManager;
207
+
208
+ const workletStatus = await rpc.commandWorkletStart({
209
+ enableDebugLogs: 0,
210
+ });
211
+
212
+ if (workletStatus.status === 'started') {
213
+ const encryptedData = await rpc.commandGenerateAndEncrypt({
214
+ passkey: params.prf,
215
+ salt: b4a.toString(salt, 'hex'),
216
+ });
217
+
218
+ await WdkSecretManagerStorage.saveData(
219
+ WDK_STORAGE_ENTROPY,
220
+ encryptedData.encryptedEntropy
221
+ );
222
+ await WdkSecretManagerStorage.saveData(
223
+ WDK_STORAGE_SEED,
224
+ encryptedData.encryptedSeed
225
+ );
226
+ await WdkSecretManagerStorage.saveData(WDK_STORAGE_SALT, salt);
227
+
228
+ const decryptedData = await rpc.commandDecrypt({
229
+ passkey: params.prf,
230
+ salt: b4a.toString(salt, 'hex'),
231
+ encryptedData: encryptedData.encryptedEntropy,
232
+ });
233
+ const seed = bip39.entropyToMnemonic(
234
+ b4a.from(decryptedData.result, 'hex') as Buffer
235
+ );
236
+ return seed;
237
+ } else {
238
+ throw new Error('Error while starting the worklet.');
239
+ }
240
+ } catch (error: any) {
241
+ throw new Error(error.message);
242
+ }
243
+ }
244
+
245
+ async importSeedPhrase(params: {
246
+ prf: Buffer | ArrayBuffer | string;
247
+ seedPhrase: string;
248
+ }): Promise<boolean> {
249
+ try {
250
+ const salt = wdkEncryptionSalt.generateWdkSalt(params.prf);
251
+ const rpc = BareWorkletApi.hrpcInstances.wdkSecretManager;
252
+
253
+ const workletStatus = await rpc.commandWorkletStart({
254
+ enableDebugLogs: 0,
255
+ });
256
+
257
+ if (workletStatus.status === 'started') {
258
+ const encryptedData = await rpc.commandGenerateAndEncrypt({
259
+ passkey: params.prf,
260
+ salt: b4a.toString(salt, 'hex'),
261
+ seedPhrase: params.seedPhrase,
262
+ });
263
+
264
+ await WdkSecretManagerStorage.saveData(
265
+ WDK_STORAGE_ENTROPY,
266
+ encryptedData.encryptedEntropy
267
+ );
268
+ await WdkSecretManagerStorage.saveData(
269
+ WDK_STORAGE_SEED,
270
+ encryptedData.encryptedSeed
271
+ );
272
+ await WdkSecretManagerStorage.saveData(WDK_STORAGE_SALT, salt);
273
+ } else {
274
+ throw new Error('Error while starting the worklet.');
275
+ }
276
+ } catch (error: any) {
277
+ throw new Error(error.message);
278
+ }
279
+ return true;
280
+ }
281
+
282
+ async retrieveSeed(
283
+ passkey: Buffer | ArrayBuffer | string
284
+ ): Promise<string | null> {
285
+ let encryptedEntropy:
286
+ | boolean
287
+ | null
288
+ | Buffer<ArrayBufferLike>
289
+ | Uint8Array<ArrayBufferLike> = null;
290
+ let encryptedSeed:
291
+ | Buffer<ArrayBufferLike>
292
+ | Uint8Array<ArrayBufferLike>
293
+ | boolean
294
+ | null = null;
295
+ let salt:
296
+ | Buffer<ArrayBufferLike>
297
+ | Uint8Array<ArrayBufferLike>
298
+ | boolean
299
+ | null = null;
300
+
301
+ if (await WdkSecretManagerStorage.hasKey(WDK_STORAGE_ENTROPY)) {
302
+ encryptedEntropy =
303
+ await WdkSecretManagerStorage.retrieveData(WDK_STORAGE_ENTROPY);
304
+ }
305
+ if (await WdkSecretManagerStorage.hasKey(WDK_STORAGE_SEED)) {
306
+ encryptedSeed =
307
+ await WdkSecretManagerStorage.retrieveData(WDK_STORAGE_SEED);
308
+ }
309
+ if (await WdkSecretManagerStorage.hasKey(WDK_STORAGE_SALT)) {
310
+ salt = await WdkSecretManagerStorage.retrieveData(WDK_STORAGE_SALT);
311
+ }
312
+
313
+ if (!encryptedSeed || !encryptedEntropy || !salt) {
314
+ return null;
315
+ }
316
+
317
+ try {
318
+ const rpc = BareWorkletApi.hrpcInstances.wdkSecretManager;
319
+ const workletStatus = await rpc.commandWorkletStart({
320
+ enableDebugLogs: 0,
321
+ });
322
+
323
+ if (workletStatus.status === 'started') {
324
+ const decryptedData = await rpc.commandDecrypt({
325
+ passkey: passkey,
326
+ salt: b4a.toString(salt, 'hex'),
327
+ encryptedData: b4a.toString(encryptedEntropy, 'hex'),
328
+ });
329
+ const seed = bip39.entropyToMnemonic(
330
+ b4a.from(decryptedData.result, 'hex') as Buffer
331
+ );
332
+ return seed;
333
+ } else {
334
+ throw new Error('Error while starting the worklet.');
335
+ }
336
+ } catch (error: any) {
337
+ throw new Error(error.message);
338
+ }
339
+ }
340
+
341
+ async getAssetAddress(
342
+ network: NetworkType,
343
+ index: number
344
+ ): Promise<{ address: string }> {
345
+ if (!this.wdkManager) {
346
+ throw new Error('WDK Manager not initialized');
347
+ }
348
+
349
+ if (network === NetworkType.SEGWIT) {
350
+ return await this.wdkManager.getAddress({
351
+ network: toNetwork(network),
352
+ accountIndex: index,
353
+ });
354
+ } else {
355
+ return await this.wdkManager.getAbstractedAddress({
356
+ network: toNetwork(network),
357
+ accountIndex: index,
358
+ });
359
+ }
360
+ }
361
+
362
+ async resolveWalletAddresses(
363
+ enabledAssets: AssetTicker[],
364
+ index: number = 0
365
+ ): Promise<Partial<Record<NetworkType, string>>> {
366
+ if (!this.wdkManager) {
367
+ throw new Error('WDK Manager not initialized');
368
+ }
369
+
370
+ const addressPromises = [];
371
+ const networkAddresses: Partial<Record<NetworkType, string>> = {};
372
+ const addressesArr = [];
373
+
374
+ for (const asset of enabledAssets) {
375
+ for (const networkType of Object.keys(AssetAddressMap[asset])) {
376
+ addressesArr.push({ [networkType]: null });
377
+ addressPromises.push(
378
+ this.getAssetAddress(networkType as NetworkType, index)
379
+ );
380
+ }
381
+ }
382
+
383
+ const addresses = await Promise.allSettled(addressPromises);
384
+ addressesArr.forEach((obj, i) => {
385
+ const key = Object.keys(obj)[0];
386
+ if (addresses[i]?.status === 'fulfilled') {
387
+ // @ts-expect-error
388
+ networkAddresses[key] = (addresses[i] as any).value.address;
389
+ } else {
390
+ // @ts-expect-error
391
+ networkAddresses[key] = null;
392
+ console.error(
393
+ `Error while resolving wallet address ${key} - err - ${(addresses[i] as any).reason}`
394
+ );
395
+ }
396
+ });
397
+
398
+ networkAddresses[NetworkType.POLYGON] =
399
+ networkAddresses[NetworkType.ETHEREUM];
400
+ networkAddresses[NetworkType.ARBITRUM] =
401
+ networkAddresses[NetworkType.ETHEREUM];
402
+
403
+ return networkAddresses;
404
+ }
405
+
406
+ async quoteSendByNetwork(
407
+ network: NetworkType,
408
+ index: number,
409
+ amount: number,
410
+ recipientAddress: string,
411
+ asset: AssetTicker
412
+ ) {
413
+ try {
414
+ if (!this.wdkManager) {
415
+ throw new Error('WDK Manager not initialized');
416
+ }
417
+
418
+ if (network === NetworkType.SEGWIT) {
419
+ const value = new Decimal(amount)
420
+ .mul(this.getDenominationValue(AssetTicker.BTC))
421
+ .toNumber();
422
+ const quote = await this.wdkManager.quoteSendTransaction({
423
+ network: 'bitcoin',
424
+ accountIndex: index,
425
+ options: {
426
+ to: recipientAddress,
427
+ value: value.toString(),
428
+ },
429
+ });
430
+
431
+ return Number(quote.fee) / this.getDenominationValue(AssetTicker.BTC);
432
+ } else if (
433
+ [
434
+ NetworkType.ETHEREUM,
435
+ NetworkType.POLYGON,
436
+ NetworkType.ARBITRUM,
437
+ NetworkType.TON,
438
+ ].includes(network)
439
+ ) {
440
+ const sendAmount = 1000;
441
+
442
+ const config = {
443
+ paymasterToken: {
444
+ // @ts-expect-error
445
+ address: SMART_CONTRACT_BALANCE_ADDRESSES[asset][network],
446
+ },
447
+ };
448
+
449
+ const quote = await this.wdkManager.abstractedAccountQuoteTransfer({
450
+ network: network,
451
+ accountIndex: index,
452
+ options: {
453
+ recipient: recipientAddress,
454
+ // @ts-expect-error
455
+ token: SMART_CONTRACT_BALANCE_ADDRESSES[asset][network],
456
+ amount: sendAmount.toString(),
457
+ },
458
+ config: config,
459
+ });
460
+
461
+ return Number(quote.fee) / this.getDenominationValue(AssetTicker.USDT);
462
+ } else {
463
+ throw new Error('Unsupported network');
464
+ }
465
+ } catch (error) {
466
+ const insufficientBalancePatterns = [
467
+ 'Insufficient balance',
468
+ 'Details: validator: callData reverts',
469
+ 'JSON is not a valid request object',
470
+ ];
471
+
472
+ if (
473
+ insufficientBalancePatterns.some((pattern) =>
474
+ (error as any)?.message?.includes(pattern)
475
+ )
476
+ ) {
477
+ throw new Error('Insufficient balance');
478
+ }
479
+
480
+ throw error;
481
+ }
482
+ }
483
+
484
+ async sendByNetwork(
485
+ network: NetworkType,
486
+ index: number,
487
+ amount: number,
488
+ recipientAddress: string,
489
+ asset: AssetTicker
490
+ ): Promise<any> {
491
+ if (!this.wdkManager) {
492
+ throw new Error('WDK Manager not initialized');
493
+ }
494
+
495
+ // Check if any wallet exists and the WDK Manager is started with a seed
496
+ const hasWallet = this.walletManagerCache.size > 0;
497
+ if (!hasWallet) {
498
+ throw new Error(
499
+ 'No wallet found. Please create or import a wallet first before sending transactions.'
500
+ );
501
+ }
502
+
503
+ if (network === NetworkType.SEGWIT) {
504
+ const sendParams = {
505
+ to: recipientAddress,
506
+ value: new Decimal(amount)
507
+ .mul(this.getDenominationValue(AssetTicker.BTC))
508
+ .round()
509
+ .toString(),
510
+ };
511
+
512
+ const response = await this.wdkManager.sendTransaction({
513
+ network: network,
514
+ accountIndex: index,
515
+ options: sendParams,
516
+ });
517
+
518
+ return response;
519
+ } else if (
520
+ [
521
+ NetworkType.ETHEREUM,
522
+ NetworkType.POLYGON,
523
+ NetworkType.ARBITRUM,
524
+ NetworkType.TON,
525
+ ].includes(network)
526
+ ) {
527
+ const sendParams = {
528
+ recipient: recipientAddress,
529
+ // @ts-expect-error
530
+ token: SMART_CONTRACT_BALANCE_ADDRESSES[asset][network],
531
+ amount: new Decimal(amount)
532
+ .mul(this.getDenominationValue(AssetTicker.USDT))
533
+ .round()
534
+ .toString(),
535
+ };
536
+
537
+ const config = {
538
+ paymasterToken: {
539
+ // @ts-expect-error
540
+ address: SMART_CONTRACT_BALANCE_ADDRESSES[asset][network],
541
+ },
542
+ };
543
+
544
+ const response = await this.wdkManager.abstractedAccountTransfer({
545
+ network: network,
546
+ accountIndex: index,
547
+ options: sendParams,
548
+ config,
549
+ });
550
+
551
+ return response;
552
+ } else {
553
+ throw new Error('Unsupported network');
554
+ }
555
+ }
556
+
557
+ getDenominationValue(asset: AssetTicker): number {
558
+ switch (asset) {
559
+ case AssetTicker.BTC:
560
+ return 100000000;
561
+ case AssetTicker.USDT:
562
+ return 1000000;
563
+ case AssetTicker.XAUT:
564
+ return 1000000;
565
+ default:
566
+ return 1000000;
567
+ }
568
+ }
569
+
570
+ async createWallet(params: {
571
+ walletName: string;
572
+ prf: Buffer | string;
573
+ }): Promise<Wallet> {
574
+ let seed = await this.retrieveSeed(params.prf);
575
+
576
+ if (!seed) {
577
+ seed = await this.createSeed(params);
578
+ }
579
+
580
+ const walletName = params.walletName;
581
+ const availableAssets = [
582
+ AssetTicker.BTC,
583
+ AssetTicker.USDT,
584
+ AssetTicker.XAUT,
585
+ ];
586
+
587
+ const wallet: Wallet = {
588
+ id: `wallet_${Date.now()}`,
589
+ name: walletName,
590
+ enabledAssets: availableAssets,
591
+ };
592
+
593
+ const wdkManager = BareWorkletApi.hrpcInstances.wdkManager;
594
+ await wdkManager.workletStart({
595
+ enableDebugLogs: 0,
596
+ seedPhrase: seed,
597
+ config: JSON.stringify(this.getChainsConfig()),
598
+ });
599
+
600
+ // Update our local reference
601
+ this.wdkManager = BareWorkletApi.hrpcInstances.wdkManager;
602
+
603
+ if (!this.wdkManager) {
604
+ throw new Error('WDK Manager not initialized after wallet creation');
605
+ }
606
+
607
+ this.walletManagerCache.set(wallet.id, {
608
+ wdk: this.wdkManager,
609
+ data: wallet,
610
+ });
611
+
612
+ return wallet;
613
+ }
614
+
615
+ async resolveWalletTransactions(
616
+ enabledAssets: AssetTicker[],
617
+ networkAddresses: Partial<Record<NetworkType, string>>
618
+ ): Promise<Record<string, Transaction[]>> {
619
+ const headers = new Headers();
620
+ headers.append('accept', 'application/json');
621
+ headers.append('x-api-key', this.getIndexerApiKey());
622
+ headers.append('Content-Type', 'application/json');
623
+
624
+ const payload: {
625
+ blockchain: string;
626
+ token: AssetTicker;
627
+ address: string;
628
+ limit: number;
629
+ fromTs?: number;
630
+ toTs?: number;
631
+ }[] = [];
632
+
633
+ const transactionMap: Record<string, Transaction[]> = {};
634
+ const payloadKeys: string[] = []; // Track the keys in order to match with response array
635
+
636
+ enabledAssets.forEach((asset) => {
637
+ const networks = AssetBalanceMap[asset];
638
+ if (!networks) return;
639
+
640
+ Object.keys(networks).forEach((networkType) => {
641
+ const key = `${networkType}_${asset}`;
642
+ transactionMap[key] = [];
643
+
644
+ const address = networkAddresses[networkType as NetworkType];
645
+
646
+ if (address) {
647
+ payload.push({
648
+ blockchain: networkType,
649
+ token: asset,
650
+ address,
651
+ limit: 100,
652
+ });
653
+ payloadKeys.push(key); // Track which key this payload corresponds to
654
+ }
655
+ });
656
+ });
657
+
658
+ const requestOptions = {
659
+ method: 'POST',
660
+ headers: headers,
661
+ body: JSON.stringify(payload),
662
+ };
663
+
664
+ const response = await fetch(
665
+ `${this.getIndexerUrl()}/api/${this.getIndexerVersion()}/batch/token-transfers`,
666
+ requestOptions
667
+ );
668
+
669
+ if (!response.ok) {
670
+ throw new Error(`Failed to fetch transactions: ${response.status}`);
671
+ }
672
+
673
+ const data = await response.json();
674
+
675
+ // Process the batch response array - each element corresponds to a payload item by index
676
+ (data as Array<{ transfers: Transaction[] }>).forEach((item, index) => {
677
+ const key = payloadKeys[index];
678
+ if (key) {
679
+ transactionMap[key] = item.transfers || [];
680
+ }
681
+ });
682
+
683
+ return transactionMap;
684
+ }
685
+
686
+ async resolveWalletBalances(
687
+ enabledAssets: AssetTicker[],
688
+ networkAddresses: Partial<Record<NetworkType, string>>
689
+ ): Promise<
690
+ Record<
691
+ string,
692
+ {
693
+ balance: number;
694
+ asset: AssetTicker;
695
+ }
696
+ >
697
+ > {
698
+ const headers = new Headers();
699
+ headers.append('accept', 'application/json');
700
+ headers.append('x-api-key', this.getIndexerApiKey());
701
+ headers.append('Content-Type', 'application/json');
702
+
703
+ const payload: {
704
+ blockchain: string;
705
+ token: AssetTicker;
706
+ address: string;
707
+ }[] = [];
708
+
709
+ enabledAssets.forEach((asset) => {
710
+ const networks = AssetBalanceMap[asset];
711
+ if (!networks) return;
712
+
713
+ Object.keys(networks).forEach((networkType) => {
714
+ const address = networkAddresses[networkType as NetworkType];
715
+ if (address) {
716
+ payload.push({
717
+ blockchain: networkType,
718
+ token: asset,
719
+ address,
720
+ });
721
+ }
722
+ });
723
+ });
724
+
725
+ const requestOptions = {
726
+ method: 'POST',
727
+ headers: headers,
728
+ body: JSON.stringify(payload),
729
+ };
730
+
731
+ const response = await fetch(
732
+ `${this.getIndexerUrl()}/api/${this.getIndexerVersion()}/batch/token-balances`,
733
+ requestOptions
734
+ );
735
+
736
+ if (!response.ok) {
737
+ throw new Error(`Failed to fetch balances: ${response.status}`);
738
+ }
739
+
740
+ const data = await response.json();
741
+
742
+ const balanceMap = Object.entries(
743
+ data as Record<
744
+ string,
745
+ { tokenBalance: { amount: number; blockchain: string; token: string } }
746
+ >
747
+ ).reduce(
748
+ (allBalances, [_, value]) => {
749
+ const obj = (value as any).tokenBalance;
750
+ allBalances[`${obj.blockchain}_${obj.token}`] = {
751
+ balance: parseFloat(obj.amount),
752
+ asset: obj.token as AssetTicker,
753
+ };
754
+ return allBalances;
755
+ },
756
+ {} as Record<string, { balance: number; asset: AssetTicker }>
757
+ );
758
+
759
+ return balanceMap;
760
+ }
761
+ }
762
+
763
+ export const wdkService = WDKService.getInstance();
764
+
765
+ export { wdkService as WDKService };