@spacesops/wdk-react-native-provider 1.0.0-beta.10
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.
- package/LICENSE +176 -0
- package/README.md +841 -0
- package/lib/module/contexts/constants.js +20 -0
- package/lib/module/contexts/constants.js.map +1 -0
- package/lib/module/contexts/reducer.js +87 -0
- package/lib/module/contexts/reducer.js.map +1 -0
- package/lib/module/contexts/types.js +4 -0
- package/lib/module/contexts/types.js.map +1 -0
- package/lib/module/contexts/wallet-context.js +409 -0
- package/lib/module/contexts/wallet-context.js.map +1 -0
- package/lib/module/index.js +15 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/polyfills.js +34 -0
- package/lib/module/polyfills.js.map +1 -0
- package/lib/module/services/wdk-service/bare-api.js +69 -0
- package/lib/module/services/wdk-service/bare-api.js.map +1 -0
- package/lib/module/services/wdk-service/index.js +506 -0
- package/lib/module/services/wdk-service/index.js.map +1 -0
- package/lib/module/services/wdk-service/types.js +48 -0
- package/lib/module/services/wdk-service/types.js.map +1 -0
- package/lib/module/services/wdk-service/wdk-encryption-salt.js +29 -0
- package/lib/module/services/wdk-service/wdk-encryption-salt.js.map +1 -0
- package/lib/module/services/wdk-service/wdk-secret-manager-storage.js +64 -0
- package/lib/module/services/wdk-service/wdk-secret-manager-storage.js.map +1 -0
- package/lib/module/services/wdk-service/wdk-secret-manager-worklet.bundle.js +1 -0
- package/lib/module/services/wdk-service/wdk-worklet.mobile.bundle.js +1 -0
- package/lib/module/spec/hrpc/hrpc.json +66 -0
- package/lib/module/spec/hrpc/index.js +121 -0
- package/lib/module/spec/hrpc/index.js.map +1 -0
- package/lib/module/spec/hrpc/messages.js +291 -0
- package/lib/module/spec/hrpc/messages.js.map +1 -0
- package/lib/module/spec/schema/index.js +291 -0
- package/lib/module/spec/schema/index.js.map +1 -0
- package/lib/module/spec/schema/schema.json +186 -0
- package/lib/module/utils/get-balances-from-balance-map.js +18 -0
- package/lib/module/utils/get-balances-from-balance-map.js.map +1 -0
- package/lib/module/utils/get-transactions-from-transaction-map.js +10 -0
- package/lib/module/utils/get-transactions-from-transaction-map.js.map +1 -0
- package/lib/module/worklet/wdk-secret-manager-worklet.js +106 -0
- package/lib/module/worklet/wdk-secret-manager-worklet.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/contexts/constants.d.ts +3 -0
- package/lib/typescript/src/contexts/constants.d.ts.map +1 -0
- package/lib/typescript/src/contexts/reducer.d.ts +38 -0
- package/lib/typescript/src/contexts/reducer.d.ts.map +1 -0
- package/lib/typescript/src/contexts/types.d.ts +40 -0
- package/lib/typescript/src/contexts/types.d.ts.map +1 -0
- package/lib/typescript/src/contexts/wallet-context.d.ts +10 -0
- package/lib/typescript/src/contexts/wallet-context.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +7 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/polyfills.d.ts +7 -0
- package/lib/typescript/src/polyfills.d.ts.map +1 -0
- package/lib/typescript/src/services/wdk-service/bare-api.d.ts +32 -0
- package/lib/typescript/src/services/wdk-service/bare-api.d.ts.map +1 -0
- package/lib/typescript/src/services/wdk-service/index.d.ts +68 -0
- package/lib/typescript/src/services/wdk-service/index.d.ts.map +1 -0
- package/lib/typescript/src/services/wdk-service/types.d.ts +125 -0
- package/lib/typescript/src/services/wdk-service/types.d.ts.map +1 -0
- package/lib/typescript/src/services/wdk-service/wdk-encryption-salt.d.ts +6 -0
- package/lib/typescript/src/services/wdk-service/wdk-encryption-salt.d.ts.map +1 -0
- package/lib/typescript/src/services/wdk-service/wdk-secret-manager-storage.d.ts +13 -0
- package/lib/typescript/src/services/wdk-service/wdk-secret-manager-storage.d.ts.map +1 -0
- package/lib/typescript/src/utils/get-balances-from-balance-map.d.ts +4 -0
- package/lib/typescript/src/utils/get-balances-from-balance-map.d.ts.map +1 -0
- package/lib/typescript/src/utils/get-transactions-from-transaction-map.d.ts +4 -0
- package/lib/typescript/src/utils/get-transactions-from-transaction-map.d.ts.map +1 -0
- package/metro-polyfills.js +89 -0
- package/package.json +152 -0
- package/src/contexts/constants.ts +19 -0
- package/src/contexts/reducer.ts +103 -0
- package/src/contexts/types.ts +51 -0
- package/src/contexts/wallet-context.tsx +411 -0
- package/src/index.tsx +28 -0
- package/src/polyfills.ts +31 -0
- package/src/services/wdk-service/bare-api.ts +88 -0
- package/src/services/wdk-service/index.ts +765 -0
- package/src/services/wdk-service/types.ts +137 -0
- package/src/services/wdk-service/wdk-encryption-salt.ts +30 -0
- package/src/services/wdk-service/wdk-secret-manager-storage.ts +102 -0
- package/src/spec/hrpc/hrpc.json +66 -0
- package/src/spec/hrpc/index.js +228 -0
- package/src/spec/hrpc/messages.js +328 -0
- package/src/spec/schema/index.js +328 -0
- package/src/spec/schema/schema.json +186 -0
- package/src/utils/get-balances-from-balance-map.ts +22 -0
- package/src/utils/get-transactions-from-transaction-map.ts +18 -0
- package/src/worklet/wdk-secret-manager-worklet.js +118 -0
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
import { HRPC as WdkManager } from '@tetherto/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 };
|