@dynamic-labs-wallet/browser 0.0.8 → 0.0.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/index.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/index.cjs.js ADDED
@@ -0,0 +1,799 @@
1
+ 'use strict';
2
+
3
+ var core = require('@dynamic-labs-wallet/core');
4
+ var libMpcWeb = require('@dynamic-labs-wallet/lib-mpc-web');
5
+
6
+ const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = core.MPC_RELAY_PROD_API_URL })=>{
7
+ switch(signingAlgorithm){
8
+ case core.SigningAlgorithm.ECDSA:
9
+ return new libMpcWeb.Ecdsa(baseRelayUrl);
10
+ case core.SigningAlgorithm.ED25519:
11
+ return new libMpcWeb.Ed25519(baseRelayUrl);
12
+ case core.SigningAlgorithm.BIP340:
13
+ return new libMpcWeb.BIP340(baseRelayUrl);
14
+ default:
15
+ throw new Error(`Unsupported signing algorithm: ${signingAlgorithm}`);
16
+ }
17
+ };
18
+ const getMPCSigner = ({ chainName, baseRelayUrl })=>{
19
+ const chainConfig = core.getMPCChainConfig(chainName);
20
+ const signatureScheme = getMPCSignatureScheme({
21
+ signingAlgorithm: chainConfig.signingAlgorithm,
22
+ baseRelayUrl
23
+ });
24
+ return signatureScheme;
25
+ };
26
+
27
+ const bytesToBase64 = (arr)=>{
28
+ return btoa(Array.from(arr, (b)=>String.fromCharCode(b)).join(''));
29
+ };
30
+ const stringToBytes = (str)=>{
31
+ return new TextEncoder().encode(str);
32
+ };
33
+ const base64ToBytes = (base64)=>{
34
+ return new Uint8Array(Buffer.from(base64, 'base64'));
35
+ };
36
+ // Helper function to ensure proper base64 padding
37
+ const ensureBase64Padding = (str)=>{
38
+ return str.padEnd(Math.ceil(str.length / 4) * 4, '=');
39
+ };
40
+
41
+ const getKey = async ({ password, salt })=>{
42
+ const passwordBytes = stringToBytes(password);
43
+ const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
44
+ name: 'PBKDF2'
45
+ }, false, [
46
+ 'deriveKey'
47
+ ]);
48
+ return crypto.subtle.deriveKey({
49
+ name: 'PBKDF2',
50
+ salt,
51
+ iterations: 100000,
52
+ hash: 'SHA-256'
53
+ }, initialKey, {
54
+ name: 'AES-GCM',
55
+ length: 256
56
+ }, false, [
57
+ 'encrypt',
58
+ 'decrypt'
59
+ ]);
60
+ };
61
+ const encryptData = async ({ data, password })=>{
62
+ try {
63
+ // Generate a random salt and IV
64
+ const salt = crypto.getRandomValues(new Uint8Array(16));
65
+ const iv = crypto.getRandomValues(new Uint8Array(12)); // AES-GCM requires 12 bytes
66
+ const key = await getKey({
67
+ password,
68
+ salt
69
+ });
70
+ // Convert the input string to bytes
71
+ const dataBytes = new TextEncoder().encode(data);
72
+ // Encrypt the data
73
+ const encryptedData = await crypto.subtle.encrypt({
74
+ name: 'AES-GCM',
75
+ iv
76
+ }, key, dataBytes);
77
+ // Convert to base64 strings, ensure proper padding
78
+ return {
79
+ salt: bytesToBase64(salt),
80
+ iv: bytesToBase64(iv),
81
+ cipher: bytesToBase64(new Uint8Array(encryptedData))
82
+ };
83
+ } catch (error) {
84
+ console.error('Error encrypting data:', error);
85
+ throw new Error('Error encrypting data');
86
+ }
87
+ };
88
+ const decryptData = async ({ data, password })=>{
89
+ try {
90
+ const { salt, iv, cipher } = data;
91
+ // Ensure proper base64 padding for all values
92
+ const paddedSalt = ensureBase64Padding(salt);
93
+ const paddedIv = ensureBase64Padding(iv);
94
+ const paddedCipher = ensureBase64Padding(cipher);
95
+ const saltBytes = base64ToBytes(paddedSalt);
96
+ const ivBytes = base64ToBytes(paddedIv);
97
+ const cipherBytes = base64ToBytes(paddedCipher);
98
+ const key = await getKey({
99
+ password,
100
+ salt: saltBytes
101
+ });
102
+ const decryptedData = await crypto.subtle.decrypt({
103
+ name: 'AES-GCM',
104
+ iv: ivBytes
105
+ }, key, cipherBytes);
106
+ return new TextDecoder().decode(decryptedData);
107
+ } catch (error) {
108
+ console.error('Decryption error details:', error);
109
+ throw new Error(`Decryption failed`);
110
+ }
111
+ };
112
+
113
+ const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
114
+ const uploadFileToGoogleDrive = async ({ accessToken, fileName, jsonData })=>{
115
+ const metadata = {
116
+ name: fileName,
117
+ mimeType: 'application/json',
118
+ parents: [
119
+ 'appDataFolder'
120
+ ]
121
+ };
122
+ const form = new FormData();
123
+ form.append('metadata', new Blob([
124
+ JSON.stringify(metadata)
125
+ ], {
126
+ type: 'application/json'
127
+ }));
128
+ form.append('file', new Blob([
129
+ JSON.stringify(jsonData)
130
+ ], {
131
+ type: 'application/json'
132
+ }));
133
+ const response = await fetch(`${GOOGLE_DRIVE_UPLOAD_API}/upload/drive/v3/files?uploadType=multipart`, {
134
+ method: 'POST',
135
+ headers: {
136
+ Authorization: `Bearer ${accessToken}`
137
+ },
138
+ body: form
139
+ });
140
+ if (!response.ok) {
141
+ throw new Error('Error uploading file');
142
+ }
143
+ const result = await response.json();
144
+ return result; // Return file metadata, including file ID
145
+ };
146
+ const listFilesFromGoogleDrive = async ({ accessToken, name })=>{
147
+ // Step 1: List all files inside `appDataFolder` with the specified backup filename
148
+ const resp = await fetch(`${GOOGLE_DRIVE_UPLOAD_API}/drive/v3/files?q=${encodeURIComponent(`name='${name}'`)}&spaces=appDataFolder&orderBy=createdTime desc`, {
149
+ headers: {
150
+ Authorization: `Bearer ${accessToken}`
151
+ }
152
+ });
153
+ const data = await resp.json();
154
+ // If no files found, return null
155
+ if (!data.files || data.files.length === 0) {
156
+ return null;
157
+ }
158
+ const files = data.files;
159
+ return files;
160
+ };
161
+ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
162
+ const files = await listFilesFromGoogleDrive({
163
+ accessToken,
164
+ name
165
+ });
166
+ // Get the most recent file
167
+ const fileMetadata = files[0];
168
+ // Fetch the file data using the file ID
169
+ const fileRes = await fetch(`${GOOGLE_DRIVE_UPLOAD_API}/drive/v3/files/${fileMetadata.id}?alt=media`, {
170
+ headers: {
171
+ Authorization: `Bearer ${accessToken}`
172
+ }
173
+ });
174
+ // Read the file's raw data
175
+ const fileRawData = await fileRes.text();
176
+ if (fileRawData.length === 0) {
177
+ return null;
178
+ }
179
+ return fileRawData;
180
+ };
181
+
182
+ const BACKUP_FILENAME = 'dynamicWalletSecretBackup.json';
183
+
184
+ class DynamicWalletClient {
185
+ async serverInitializeKeyGen({ chainName, clientPrimaryKeygenId, clientSecondaryKeygenId, thresholdSignatureScheme }) {
186
+ // Initilize keygen, create room, and create the wallet account on the server
187
+ const data = await this.apiClient.createWalletAccount({
188
+ chainName,
189
+ clientKeygenIds: [
190
+ clientPrimaryKeygenId,
191
+ clientSecondaryKeygenId
192
+ ],
193
+ thresholdSignatureScheme
194
+ });
195
+ return data;
196
+ }
197
+ async clientInitializeKeyGen({ chainName }) {
198
+ // Get the mpc signer
199
+ const mpcSigner = getMPCSigner({
200
+ chainName,
201
+ baseRelayUrl: this.baseMPCRelayApiUrl
202
+ });
203
+ // Initialize the keygen for the primary client and the secondary client shares
204
+ const keygenInitResults = await Promise.all([
205
+ mpcSigner.initKeygen(),
206
+ mpcSigner.initKeygen()
207
+ ]);
208
+ const [clientPrimaryKeygenInitResult, clientSecondaryKeygenInitResult] = keygenInitResults;
209
+ return {
210
+ clientPrimaryKeygenInitResult,
211
+ clientSecondaryKeygenInitResult
212
+ };
213
+ }
214
+ async derivePublicKey({ chainName, keyShare }) {
215
+ const mpcSigner = getMPCSigner({
216
+ chainName,
217
+ baseRelayUrl: this.baseMPCRelayApiUrl
218
+ });
219
+ const chainConfig = core.getMPCChainConfig(chainName);
220
+ let publicKey;
221
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
222
+ publicKey = await mpcSigner.derivePubkey(keyShare, new Uint32Array(chainConfig.derivationPath));
223
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
224
+ publicKey = await mpcSigner.derivePubkey(keyShare, new Uint32Array(chainConfig.derivationPath));
225
+ }
226
+ return publicKey;
227
+ }
228
+ async clientKeyGen({ chainName, roomId, serverKeygenIds, clientPrimaryKeygenInitResult, clientSecondaryKeygenInitResult, thresholdSignatureScheme }) {
229
+ // Get the chain config and the mpc signer
230
+ const chainConfig = core.getMPCChainConfig(chainName);
231
+ const mpcSigner = getMPCSigner({
232
+ chainName,
233
+ baseRelayUrl: this.baseMPCRelayApiUrl
234
+ });
235
+ // All parties receive the keygenIds from all OTHER parties
236
+ const clientPrimaryKeygenId = clientPrimaryKeygenInitResult.keygenId;
237
+ const clientSecondaryKeygenId = clientSecondaryKeygenInitResult.keygenId;
238
+ const clientPrimaryKeygenIds = [
239
+ ...serverKeygenIds,
240
+ clientSecondaryKeygenId
241
+ ];
242
+ const clientSecondaryKeygenIds = [
243
+ ...serverKeygenIds,
244
+ clientPrimaryKeygenId
245
+ ];
246
+ // Get the MPC config for the threshold signature scheme
247
+ const mpcConfig = core.MPC_CONFIG[thresholdSignatureScheme];
248
+ // All parties join the keygen room
249
+ const keygenResults = await Promise.all([
250
+ mpcSigner.keygen(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, clientPrimaryKeygenInitResult, clientPrimaryKeygenIds),
251
+ mpcSigner.keygen(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, clientSecondaryKeygenInitResult, clientSecondaryKeygenIds)
252
+ ]);
253
+ const [clientPrimaryKeygenResult, clientSecondaryKeygenResult] = keygenResults;
254
+ // Pick the derivation path of the public key you want to sign for
255
+ const derivationPath = new Uint32Array(chainConfig.derivationPath);
256
+ // Get the public key for the derivation path
257
+ let rawPublicKey;
258
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
259
+ rawPublicKey = await mpcSigner.derivePubkey(clientPrimaryKeygenResult, derivationPath);
260
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
261
+ rawPublicKey = await mpcSigner.derivePubkey(clientPrimaryKeygenResult, derivationPath);
262
+ } else if (mpcSigner instanceof libMpcWeb.BIP340) {
263
+ rawPublicKey = await mpcSigner.deriveTweakPubkey(clientPrimaryKeygenResult, derivationPath);
264
+ }
265
+ return {
266
+ rawPublicKey,
267
+ primaryKeygenResult: clientPrimaryKeygenResult,
268
+ secondaryKeygenResult: clientSecondaryKeygenResult
269
+ };
270
+ }
271
+ async keyGen({ chainName, thresholdSignatureScheme }) {
272
+ try {
273
+ const { clientPrimaryKeygenInitResult, clientSecondaryKeygenInitResult } = await this.clientInitializeKeyGen({
274
+ chainName
275
+ });
276
+ const data = await this.serverInitializeKeyGen({
277
+ chainName,
278
+ clientPrimaryKeygenId: clientPrimaryKeygenInitResult.keygenId,
279
+ clientSecondaryKeygenId: clientSecondaryKeygenInitResult.keygenId,
280
+ thresholdSignatureScheme
281
+ });
282
+ const { rawPublicKey, primaryKeygenResult, secondaryKeygenResult } = await this.clientKeyGen({
283
+ chainName,
284
+ roomId: data.roomId,
285
+ serverKeygenIds: data.serverKeygenIds,
286
+ clientPrimaryKeygenInitResult,
287
+ clientSecondaryKeygenInitResult,
288
+ thresholdSignatureScheme
289
+ });
290
+ return {
291
+ rawPublicKey,
292
+ primaryKeygenResult,
293
+ secondaryKeygenResult
294
+ };
295
+ } catch (error) {
296
+ console.error('Error creating wallet account', error);
297
+ throw new Error('Error creating wallet account');
298
+ }
299
+ }
300
+ async serverSign({ walletId, message }) {
301
+ // Create the room and sign the message
302
+ const data = await this.apiClient.signMessage({
303
+ walletId,
304
+ message
305
+ });
306
+ return data;
307
+ }
308
+ async clientSign({ chainName, message, roomId, keyShare }) {
309
+ try {
310
+ const chainConfig = core.getMPCChainConfig(chainName);
311
+ const mpcSigner = getMPCSigner({
312
+ chainName,
313
+ baseRelayUrl: this.baseMPCRelayApiUrl
314
+ });
315
+ const derivationPath = new Uint32Array(chainConfig.derivationPath);
316
+ let formattedMessage;
317
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
318
+ formattedMessage = libMpcWeb.MessageHash.sha256(message);
319
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
320
+ formattedMessage = new TextEncoder().encode(message);
321
+ } else if (mpcSigner instanceof libMpcWeb.BIP340) {
322
+ formattedMessage = new TextEncoder().encode(message);
323
+ } else {
324
+ throw new Error('Unsupported signer type');
325
+ }
326
+ const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
327
+ return signature;
328
+ } catch (error) {
329
+ console.error('Error in clientSign:', error);
330
+ throw error;
331
+ }
332
+ }
333
+ async sign({ accountAddress, message, chainName }) {
334
+ const wallet = await this.getWallet({
335
+ accountAddress
336
+ });
337
+ // Perform the server sign
338
+ const data = await this.serverSign({
339
+ walletId: wallet.walletId,
340
+ message
341
+ });
342
+ // Perform the client sign and return the signature
343
+ const signature = await this.clientSign({
344
+ chainName,
345
+ message,
346
+ roomId: data.roomId,
347
+ keyShare: wallet.clientKeyShares[0]
348
+ });
349
+ return signature;
350
+ }
351
+ async refreshWalletAccountShares({ accountAddress }) {
352
+ const wallet = await this.getWallet({
353
+ accountAddress
354
+ });
355
+ const chainName = wallet.chainName;
356
+ const mpcSigner = getMPCSigner({
357
+ chainName,
358
+ baseRelayUrl: this.baseMPCRelayApiUrl
359
+ });
360
+ // Create the room and refresh the shares
361
+ const data = await this.apiClient.refreshWalletAccountShares({
362
+ walletId: wallet.walletId
363
+ });
364
+ const roomId = data.roomId;
365
+ const keygenResults = await Promise.all([
366
+ mpcSigner.refresh(roomId, wallet.clientKeyShares[0]),
367
+ mpcSigner.refresh(roomId, wallet.clientKeyShares[1])
368
+ ]);
369
+ return keygenResults;
370
+ }
371
+ async serverReshareRemainingParty({ walletId, clientKeygenIds }) {
372
+ const data = await this.apiClient.reshareRemainingParty({
373
+ walletId,
374
+ clientKeygenIds
375
+ });
376
+ return data;
377
+ }
378
+ async getExportId({ chainName, clientKeyShare }) {
379
+ const mpcSigner = getMPCSigner({
380
+ chainName,
381
+ baseRelayUrl: this.baseMPCRelayApiUrl
382
+ });
383
+ const exportId = await mpcSigner.exportID(clientKeyShare);
384
+ return exportId;
385
+ }
386
+ async reshareRemainingParty({ accountAddress, thresholdSignatureScheme }) {
387
+ const wallet = await this.getWallet({
388
+ accountAddress
389
+ });
390
+ const chainName = wallet.chainName;
391
+ const mpcSigner = getMPCSigner({
392
+ chainName,
393
+ baseRelayUrl: this.baseMPCRelayApiUrl
394
+ });
395
+ // Initialize the new party
396
+ const newPartyInitKeygen = await mpcSigner.initKeygen();
397
+ const newPartyInitKeygenId = newPartyInitKeygen.keygenId;
398
+ const clientKeygenId = await this.getExportId({
399
+ chainName,
400
+ clientKeyShare: wallet.clientKeyShares[0]
401
+ });
402
+ // Create the room and reshare the server share
403
+ const data = await this.serverReshareRemainingParty({
404
+ walletId: wallet.walletId,
405
+ clientKeygenIds: [
406
+ newPartyInitKeygenId,
407
+ clientKeygenId
408
+ ]
409
+ });
410
+ const roomId = data.roomId;
411
+ // Get the MPC config for the threshold signature scheme
412
+ const mpcConfig = core.MPC_CONFIG[thresholdSignatureScheme];
413
+ const newClientPrimaryKeygenIds = [
414
+ data.serverKeygenId,
415
+ clientKeygenId
416
+ ];
417
+ const clientSecondaryKeygenIds = [
418
+ data.serverKeygenId,
419
+ newPartyInitKeygenId
420
+ ];
421
+ console.log('newClientPrimaryKeygenIds', newClientPrimaryKeygenIds);
422
+ console.log('clientSecondaryKeygenIds', clientSecondaryKeygenIds);
423
+ const keygenResults = await Promise.all([
424
+ mpcSigner.reshareNewParty(roomId, mpcConfig.threshold, mpcConfig.threshold, newPartyInitKeygen, newClientPrimaryKeygenIds),
425
+ mpcSigner.reshareRemainingParty(roomId, mpcConfig.threshold, wallet.clientKeyShares[1], clientSecondaryKeygenIds)
426
+ ]);
427
+ return keygenResults;
428
+ }
429
+ async exportKey({ accountAddress, chainName }) {
430
+ const wallet = await this.getWallet({
431
+ accountAddress
432
+ });
433
+ const chainConfig = core.getMPCChainConfig(chainName);
434
+ const mpcSigner = getMPCSigner({
435
+ chainName,
436
+ baseRelayUrl: this.baseMPCRelayApiUrl
437
+ });
438
+ const exportId = await this.getExportId({
439
+ chainName,
440
+ clientKeyShare: wallet.clientKeyShares[0]
441
+ });
442
+ const data = await this.apiClient.exportKey({
443
+ walletId: wallet.walletId,
444
+ exportId
445
+ });
446
+ const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, wallet.clientKeyShares[0], exportId);
447
+ if (!keyExportRaw) {
448
+ throw new Error('Error exporting private key');
449
+ }
450
+ const derivationPath = new Uint32Array(chainConfig.derivationPath);
451
+ let derivedPrivateKey;
452
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
453
+ derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
454
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
455
+ derivedPrivateKey = keyExportRaw;
456
+ } else if (mpcSigner instanceof libMpcWeb.BIP340) {
457
+ derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
458
+ }
459
+ return {
460
+ derivedPrivateKey
461
+ };
462
+ }
463
+ async offlineExportKey({ chainName, keyShares }) {
464
+ try {
465
+ if (!keyShares || keyShares.length !== 2) {
466
+ throw new Error('Must provide 2 key shares');
467
+ }
468
+ const mpcSigner = getMPCSigner({
469
+ chainName,
470
+ baseRelayUrl: this.baseMPCRelayApiUrl
471
+ });
472
+ const walletKeyShares = keyShares.map((keyShare)=>{
473
+ return mpcSigner instanceof libMpcWeb.Ecdsa ? new libMpcWeb.EcdsaKeygenResult(keyShare.pubkey, keyShare.secretShare) : mpcSigner instanceof libMpcWeb.Ed25519 ? new libMpcWeb.Ed25519KeygenResult(keyShare.pubkey, keyShare.secretShare) : new libMpcWeb.BIP340KeygenResult(keyShare.pubkey, keyShare.secretShare);
474
+ });
475
+ const keyExportRaw = await mpcSigner.offlineExportFullPrivateKey(walletKeyShares);
476
+ if (!keyExportRaw) {
477
+ throw new Error('Error exporting private key: Export returned null');
478
+ }
479
+ const chainConfig = core.getMPCChainConfig(chainName);
480
+ const derivationPath = new Uint32Array(chainConfig.derivationPath);
481
+ let derivedPrivateKey;
482
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
483
+ derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
484
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
485
+ derivedPrivateKey = keyExportRaw;
486
+ } else if (mpcSigner instanceof libMpcWeb.BIP340) {
487
+ derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
488
+ }
489
+ return {
490
+ derivedPrivateKey
491
+ };
492
+ } catch (error) {
493
+ console.error('Error in offlineExportKey:', error);
494
+ throw error;
495
+ }
496
+ }
497
+ async encryptKeyShare({ keyShare, password }) {
498
+ const serializedKeyShare = JSON.stringify(keyShare);
499
+ const encryptedKeyShare = await encryptData({
500
+ data: serializedKeyShare,
501
+ password: password != null ? password : this.environmentId
502
+ });
503
+ // stringify the encrypted key share, convert to base64, and store it
504
+ const serializedEncryptedKeyShare = Buffer.from(JSON.stringify(encryptedKeyShare)).toString('base64');
505
+ return serializedEncryptedKeyShare;
506
+ }
507
+ async storeEncryptedBackupByWallet({ accountAddress, password }) {
508
+ await this.getWallet({
509
+ accountAddress
510
+ });
511
+ const encryptedKeyShares = await Promise.all(this.walletMap[accountAddress].clientKeyShares.map((keyShare)=>this.encryptKeyShare({
512
+ keyShare,
513
+ password
514
+ })));
515
+ const data = await this.apiClient.storeEncryptedBackupByWallet({
516
+ walletId: this.walletMap[accountAddress].walletId,
517
+ encryptedKeyShares,
518
+ passwordEncrypted: password ? true : false
519
+ });
520
+ return data;
521
+ }
522
+ async decryptKeyShare({ keyShare, password }) {
523
+ const decodedKeyShare = JSON.parse(Buffer.from(keyShare, 'base64').toString());
524
+ const decryptedKeyShare = await decryptData({
525
+ data: decodedKeyShare,
526
+ password: password != null ? password : this.environmentId
527
+ });
528
+ const deserializedKeyShare = JSON.parse(decryptedKeyShare);
529
+ return deserializedKeyShare;
530
+ }
531
+ async recoverEncryptedBackupByWallet({ accountAddress, password, keyShareIds }) {
532
+ const data = await this.apiClient.recoverEncryptedBackupByWallet({
533
+ walletId: this.walletMap[accountAddress].walletId,
534
+ keyShareIds
535
+ });
536
+ const decryptedKeyShares = await Promise.all(data.keyShares.map((keyShare)=>this.decryptKeyShare({
537
+ keyShare: keyShare.encryptedAccountCredential,
538
+ password: password != null ? password : this.environmentId
539
+ })));
540
+ decryptedKeyShares.forEach((keyShare)=>{
541
+ this.restoreBackupShare({
542
+ walletId: this.walletMap[accountAddress].walletId,
543
+ accountAddress,
544
+ chainName: data.chainName,
545
+ keyShare,
546
+ thresholdSignatureScheme: data.thresholdSignatureScheme
547
+ });
548
+ });
549
+ return decryptedKeyShares;
550
+ }
551
+ restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme }) {
552
+ var _this_walletMap_accountAddress;
553
+ this.walletMap[accountAddress] = {
554
+ walletId,
555
+ chainName,
556
+ accountAddress,
557
+ clientKeyShares: [
558
+ ...((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? undefined : _this_walletMap_accountAddress.clientKeyShares) || [],
559
+ keyShare
560
+ ],
561
+ thresholdSignatureScheme
562
+ };
563
+ }
564
+ async backupFileToGoogleDrive({ oauthAccountId, fileName = BACKUP_FILENAME, jsonData, password }) {
565
+ const encryptedKeyShare = await this.encryptKeyShare({
566
+ keyShare: jsonData,
567
+ password
568
+ });
569
+ const accessToken = await this.apiClient.getAccessToken({
570
+ oauthAccountId
571
+ });
572
+ const uploadMetadata = await uploadFileToGoogleDrive({
573
+ accessToken,
574
+ fileName,
575
+ jsonData: encryptedKeyShare
576
+ });
577
+ return uploadMetadata;
578
+ }
579
+ async getBackupFileFromGoogleDrive({ oauthAccountId, name = BACKUP_FILENAME, password }) {
580
+ const accessToken = await this.apiClient.getAccessToken({
581
+ oauthAccountId
582
+ });
583
+ const file = await downloadFileFromGoogleDrive({
584
+ accessToken,
585
+ name
586
+ });
587
+ if (!file) {
588
+ return null;
589
+ }
590
+ const decryptedKeyShare = await this.decryptKeyShare({
591
+ keyShare: file,
592
+ password: password != null ? password : this.environmentId
593
+ });
594
+ return decryptedKeyShare;
595
+ }
596
+ async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme }) {
597
+ const chainConfig = core.getMPCChainConfig(chainName);
598
+ const mpcSigner = getMPCSigner({
599
+ chainName,
600
+ baseRelayUrl: this.baseMPCRelayApiUrl
601
+ });
602
+ // 1. 2 parties on the client side create keygenInit
603
+ const { clientPrimaryKeygenInitResult, clientSecondaryKeygenInitResult } = await this.clientInitializeKeyGen({
604
+ chainName
605
+ });
606
+ const clientSecondaryKeygenId = clientSecondaryKeygenInitResult.keygenId;
607
+ const clientPrimaryKeygenId = clientPrimaryKeygenInitResult.keygenId;
608
+ // 2. server to create a room for importing the private key
609
+ // server will do 3 things:
610
+ // --- 1. init keygen for the server as a party
611
+ // --- 2. open a room and return the roomId for the ceremony
612
+ // --- 3. join the room as a party for the 2/3 ceremony
613
+ const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
614
+ chainName,
615
+ clientKeygenIds: [
616
+ clientPrimaryKeygenId,
617
+ clientSecondaryKeygenId
618
+ ],
619
+ thresholdSignatureScheme
620
+ });
621
+ const { threshold } = core.getTSSConfig(thresholdSignatureScheme);
622
+ // prep
623
+ const importerKeygenIds = [
624
+ ...serverKeygenIds,
625
+ clientSecondaryKeygenId
626
+ ];
627
+ const recipientKeygenIds = [
628
+ ...serverKeygenIds,
629
+ clientPrimaryKeygenId
630
+ ];
631
+ // 3. Join the keygen room for the ceremony
632
+ const [clientPrimaryKeygenResult, clientSecondaryKeygenResult] = await Promise.all([
633
+ mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, clientPrimaryKeygenInitResult, importerKeygenIds),
634
+ mpcSigner.importPrivateKeyRecipient(roomId, threshold, clientSecondaryKeygenInitResult, recipientKeygenIds)
635
+ ]);
636
+ const derivationPath = new Uint32Array(chainConfig.derivationPath);
637
+ // Get the public key for the derivation path
638
+ let rawPublicKey;
639
+ if (mpcSigner instanceof libMpcWeb.Ecdsa) {
640
+ rawPublicKey = await mpcSigner.derivePubkey(clientPrimaryKeygenResult, derivationPath);
641
+ } else if (mpcSigner instanceof libMpcWeb.Ed25519) {
642
+ rawPublicKey = await mpcSigner.derivePubkey(clientPrimaryKeygenResult, derivationPath);
643
+ } else if (mpcSigner instanceof libMpcWeb.BIP340) {
644
+ rawPublicKey = await mpcSigner.deriveTweakPubkey(clientPrimaryKeygenResult, derivationPath);
645
+ }
646
+ return {
647
+ rawPublicKey,
648
+ primaryKeygenResult: clientPrimaryKeygenResult,
649
+ secondaryKeygenResult: clientSecondaryKeygenResult
650
+ };
651
+ }
652
+ async exportClientKeyshares({ accountAddress }) {
653
+ const clientKeyshares = await this.getClientKeyshares({
654
+ accountAddress
655
+ });
656
+ const text = JSON.stringify(clientKeyshares);
657
+ const blob = new Blob([
658
+ text
659
+ ], {
660
+ type: 'text/plain'
661
+ });
662
+ const url = URL.createObjectURL(blob);
663
+ const a = document.createElement('a');
664
+ a.href = url;
665
+ a.download = 'clientKeyshare.txt';
666
+ a.click();
667
+ }
668
+ async getClientKeyshares({ accountAddress }) {
669
+ const wallet = await this.getWallet({
670
+ accountAddress
671
+ });
672
+ return wallet.clientKeyShares;
673
+ }
674
+ async getWallet({ accountAddress }) {
675
+ if (accountAddress) {
676
+ if (this.walletMap[accountAddress] && this.walletMap[accountAddress].clientKeyShares.length > 0) {
677
+ return this.walletMap[accountAddress];
678
+ } else {
679
+ var _user_verifiedCredentials;
680
+ const user = await this.apiClient.getUser();
681
+ const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? undefined : _user_verifiedCredentials.find((vc)=>vc.address === accountAddress);
682
+ console.log('need to restore wallet', wallet);
683
+ const clientShares = wallet.walletProperties.keyShares.filter((ks)=>ks.backupLocation === 'dynamic');
684
+ console.log('clientShares', clientShares);
685
+ // restore backup
686
+ const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
687
+ accountAddress,
688
+ password: this.environmentId
689
+ });
690
+ //todo: check to see if their are other backups ie google drive, etc
691
+ console.log('recovery decryptedKeyShares', decryptedKeyShares);
692
+ }
693
+ }
694
+ const walletCount = Object.keys(this.walletMap).length;
695
+ // if there are no wallets, throw an error
696
+ if (walletCount === 0) {
697
+ throw new Error('No wallets found');
698
+ }
699
+ // if there is only one wallet, return it by default
700
+ if (walletCount === 1) {
701
+ return Object.values(this.walletMap)[0];
702
+ }
703
+ if (!accountAddress) {
704
+ throw new Error('Must provide an account address');
705
+ }
706
+ return this.walletMap[accountAddress];
707
+ }
708
+ async getWallets() {
709
+ var _user_verifiedCredentials;
710
+ const user = await this.apiClient.getUser();
711
+ const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? undefined : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
712
+ const wallets = waasWallets.map((vc)=>({
713
+ walletId: vc.id,
714
+ chainName: vc.chain,
715
+ accountAddress: vc.address
716
+ }));
717
+ this.walletMap = wallets.reduce((acc, wallet)=>{
718
+ acc[wallet.accountAddress] = {
719
+ walletId: wallet.walletId,
720
+ chainName: wallet.chainName,
721
+ accountAddress: wallet.accountAddress,
722
+ clientKeyShares: [],
723
+ thresholdSignatureScheme: undefined
724
+ };
725
+ return acc;
726
+ }, {});
727
+ return wallets;
728
+ }
729
+ constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl }){
730
+ this.walletMap = {} // todo: store in session storage
731
+ ;
732
+ this.environmentId = environmentId;
733
+ this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
734
+ this.apiClient = new core.DynamicApiClient({
735
+ environmentId,
736
+ authToken,
737
+ baseApiUrl
738
+ });
739
+ this.getWallets();
740
+ }
741
+ }
742
+
743
+ Object.defineProperty(exports, "BIP340", {
744
+ enumerable: true,
745
+ get: function () { return libMpcWeb.BIP340; }
746
+ });
747
+ Object.defineProperty(exports, "BIP340InitKeygenResult", {
748
+ enumerable: true,
749
+ get: function () { return libMpcWeb.BIP340InitKeygenResult; }
750
+ });
751
+ Object.defineProperty(exports, "BIP340KeygenResult", {
752
+ enumerable: true,
753
+ get: function () { return libMpcWeb.BIP340KeygenResult; }
754
+ });
755
+ Object.defineProperty(exports, "Ecdsa", {
756
+ enumerable: true,
757
+ get: function () { return libMpcWeb.Ecdsa; }
758
+ });
759
+ Object.defineProperty(exports, "EcdsaInitKeygenResult", {
760
+ enumerable: true,
761
+ get: function () { return libMpcWeb.EcdsaInitKeygenResult; }
762
+ });
763
+ Object.defineProperty(exports, "EcdsaKeygenResult", {
764
+ enumerable: true,
765
+ get: function () { return libMpcWeb.EcdsaKeygenResult; }
766
+ });
767
+ Object.defineProperty(exports, "EcdsaPublicKey", {
768
+ enumerable: true,
769
+ get: function () { return libMpcWeb.EcdsaPublicKey; }
770
+ });
771
+ Object.defineProperty(exports, "EcdsaSignature", {
772
+ enumerable: true,
773
+ get: function () { return libMpcWeb.EcdsaSignature; }
774
+ });
775
+ Object.defineProperty(exports, "Ed25519", {
776
+ enumerable: true,
777
+ get: function () { return libMpcWeb.Ed25519; }
778
+ });
779
+ Object.defineProperty(exports, "Ed25519InitKeygenResult", {
780
+ enumerable: true,
781
+ get: function () { return libMpcWeb.Ed25519InitKeygenResult; }
782
+ });
783
+ Object.defineProperty(exports, "Ed25519KeygenResult", {
784
+ enumerable: true,
785
+ get: function () { return libMpcWeb.Ed25519KeygenResult; }
786
+ });
787
+ Object.defineProperty(exports, "MessageHash", {
788
+ enumerable: true,
789
+ get: function () { return libMpcWeb.MessageHash; }
790
+ });
791
+ exports.DynamicWalletClient = DynamicWalletClient;
792
+ exports.getMPCSignatureScheme = getMPCSignatureScheme;
793
+ exports.getMPCSigner = getMPCSigner;
794
+ Object.keys(core).forEach(function (k) {
795
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
796
+ enumerable: true,
797
+ get: function () { return core[k]; }
798
+ });
799
+ });
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@dynamic-labs-wallet/browser",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "dependencies": {
5
- "@dynamic-labs-wallet/core": "0.0.8",
6
- "@dynamic-labs-wallet/lib-mpc-web": "0.0.8"
5
+ "@dynamic-labs-wallet/core": "0.0.10",
6
+ "@dynamic-labs-wallet/lib-mpc-web": "0.0.10"
7
7
  },
8
8
  "publishConfig": {
9
9
  "access": "restricted"
@@ -13,7 +13,8 @@
13
13
  "projectType": "library",
14
14
  "name": "browser"
15
15
  },
16
- "main": "./index.esm.js",
16
+ "type": "module",
17
+ "main": "./index.cjs.js",
17
18
  "module": "./index.esm.js",
18
19
  "types": "./index.esm.d.ts",
19
20
  "exports": {
@@ -21,8 +22,8 @@
21
22
  ".": {
22
23
  "types": "./index.esm.d.ts",
23
24
  "import": "./index.esm.js",
24
- "default": "./index.esm.js"
25
+ "require": "./index.cjs.js",
26
+ "default": "./index.cjs.js"
25
27
  }
26
- },
27
- "type": "module"
28
- }
28
+ }
29
+ }