@dynamic-labs-wallet/browser 0.0.30 → 0.0.31
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.js +126 -53
- package/index.esm.js +126 -53
- package/package.json +2 -2
- package/src/backup/encryption.d.ts +5 -0
- package/src/backup/encryption.d.ts.map +1 -1
- package/src/backup/providers/googleDrive.d.ts +8 -3
- package/src/backup/providers/googleDrive.d.ts.map +1 -1
- package/src/client.d.ts +13 -9
- package/src/client.d.ts.map +1 -1
- package/src/types.d.ts +18 -0
- package/src/types.d.ts.map +1 -1
package/index.cjs.js
CHANGED
|
@@ -64,6 +64,11 @@ const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddr
|
|
|
64
64
|
return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const PBKDF2_ALGORITHM = 'PBKDF2';
|
|
68
|
+
const PBKDF2_ITERATIONS = 100000;
|
|
69
|
+
const PBKDF2_HASH_ALGORITHM = 'SHA-256';
|
|
70
|
+
const AES_GCM_ALGORITHM = 'AES-GCM';
|
|
71
|
+
const AES_GCM_LENGTH = 256;
|
|
67
72
|
const getKey = async ({ password, salt })=>{
|
|
68
73
|
const passwordBytes = stringToBytes(password);
|
|
69
74
|
const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
|
|
@@ -72,13 +77,13 @@ const getKey = async ({ password, salt })=>{
|
|
|
72
77
|
'deriveKey'
|
|
73
78
|
]);
|
|
74
79
|
return crypto.subtle.deriveKey({
|
|
75
|
-
name:
|
|
80
|
+
name: PBKDF2_ALGORITHM,
|
|
76
81
|
salt,
|
|
77
|
-
iterations:
|
|
78
|
-
hash:
|
|
82
|
+
iterations: PBKDF2_ITERATIONS,
|
|
83
|
+
hash: PBKDF2_HASH_ALGORITHM
|
|
79
84
|
}, initialKey, {
|
|
80
|
-
name:
|
|
81
|
-
length:
|
|
85
|
+
name: AES_GCM_ALGORITHM,
|
|
86
|
+
length: AES_GCM_LENGTH
|
|
82
87
|
}, false, [
|
|
83
88
|
'encrypt',
|
|
84
89
|
'decrypt'
|
|
@@ -97,7 +102,7 @@ const encryptData = async ({ data, password })=>{
|
|
|
97
102
|
const dataBytes = new TextEncoder().encode(data);
|
|
98
103
|
// Encrypt the data
|
|
99
104
|
const encryptedData = await crypto.subtle.encrypt({
|
|
100
|
-
name:
|
|
105
|
+
name: AES_GCM_ALGORITHM,
|
|
101
106
|
iv
|
|
102
107
|
}, key, dataBytes);
|
|
103
108
|
// Convert to base64 strings, ensure proper padding
|
|
@@ -126,24 +131,42 @@ const decryptData = async ({ data, password })=>{
|
|
|
126
131
|
salt: saltBytes
|
|
127
132
|
});
|
|
128
133
|
const decryptedData = await crypto.subtle.decrypt({
|
|
129
|
-
name:
|
|
134
|
+
name: AES_GCM_ALGORITHM,
|
|
130
135
|
iv: ivBytes
|
|
131
136
|
}, key, cipherBytes);
|
|
132
137
|
return new TextDecoder().decode(decryptedData);
|
|
133
138
|
} catch (error) {
|
|
134
139
|
console.error('Decryption error details:', error);
|
|
135
|
-
throw new Error(
|
|
140
|
+
throw new Error('Decryption failed');
|
|
136
141
|
}
|
|
137
142
|
};
|
|
138
143
|
|
|
139
144
|
const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
const uploadFileToGoogleDriveAppStorage = async ({ accessToken, fileName, jsonData })=>{
|
|
146
|
+
return uploadFileToGoogleDrive({
|
|
147
|
+
accessToken,
|
|
148
|
+
fileName,
|
|
149
|
+
jsonData,
|
|
144
150
|
parents: [
|
|
145
151
|
'appDataFolder'
|
|
146
152
|
]
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const uploadFileToGoogleDrivePersonal = async ({ accessToken, fileName, jsonData })=>{
|
|
156
|
+
return uploadFileToGoogleDrive({
|
|
157
|
+
accessToken,
|
|
158
|
+
fileName,
|
|
159
|
+
jsonData,
|
|
160
|
+
parents: [
|
|
161
|
+
'root'
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
const uploadFileToGoogleDrive = async ({ accessToken, fileName, jsonData, parents })=>{
|
|
166
|
+
const metadata = {
|
|
167
|
+
name: fileName,
|
|
168
|
+
mimeType: 'application/json',
|
|
169
|
+
parents
|
|
147
170
|
};
|
|
148
171
|
const form = new FormData();
|
|
149
172
|
form.append('metadata', new Blob([
|
|
@@ -189,6 +212,9 @@ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
|
|
|
189
212
|
accessToken,
|
|
190
213
|
name
|
|
191
214
|
});
|
|
215
|
+
if (!files || files.length === 0) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
192
218
|
// Get the most recent file
|
|
193
219
|
const fileMetadata = files[0];
|
|
194
220
|
// Fetch the file data using the file ID
|
|
@@ -202,7 +228,14 @@ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
|
|
|
202
228
|
if (fileRawData.length === 0) {
|
|
203
229
|
return null;
|
|
204
230
|
}
|
|
205
|
-
|
|
231
|
+
try {
|
|
232
|
+
// Just parse and return the data without validation
|
|
233
|
+
// The client will handle validation of the structure
|
|
234
|
+
return JSON.parse(fileRawData);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('Error parsing backup file:', error);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
206
239
|
};
|
|
207
240
|
|
|
208
241
|
const localStorageWriteTest = {
|
|
@@ -322,13 +355,11 @@ class DynamicWalletClient {
|
|
|
322
355
|
const keygenInitResults = await Promise.all(Array(clientThreshold).fill(null).map(()=>mpcSigner.initKeygen()));
|
|
323
356
|
return keygenInitResults;
|
|
324
357
|
}
|
|
325
|
-
async derivePublicKey({ chainName, keyShare,
|
|
358
|
+
async derivePublicKey({ chainName, keyShare, derivationPath }) {
|
|
326
359
|
const mpcSigner = getMPCSigner({
|
|
327
360
|
chainName,
|
|
328
361
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
329
362
|
});
|
|
330
|
-
const chainConfig = core.getMPCChainConfig(chainName);
|
|
331
|
-
const derivationPath = imported ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
332
363
|
let publicKey;
|
|
333
364
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
334
365
|
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
@@ -358,10 +389,12 @@ class DynamicWalletClient {
|
|
|
358
389
|
}));
|
|
359
390
|
// only need one client keygen result to derive the public key
|
|
360
391
|
const [clientKeygenResult] = clientKeygenResults;
|
|
392
|
+
const chainConfig = core.getMPCChainConfig(chainName);
|
|
393
|
+
const derivationPath = new Uint32Array(chainConfig.derivationPath);
|
|
361
394
|
const rawPublicKey = await this.derivePublicKey({
|
|
362
395
|
chainName,
|
|
363
396
|
keyShare: clientKeygenResult,
|
|
364
|
-
|
|
397
|
+
derivationPath
|
|
365
398
|
});
|
|
366
399
|
return {
|
|
367
400
|
rawPublicKey,
|
|
@@ -407,14 +440,12 @@ class DynamicWalletClient {
|
|
|
407
440
|
});
|
|
408
441
|
return data;
|
|
409
442
|
}
|
|
410
|
-
async clientSign({ chainName, message, roomId, keyShare,
|
|
443
|
+
async clientSign({ chainName, message, roomId, keyShare, derivationPath }) {
|
|
411
444
|
try {
|
|
412
|
-
const chainConfig = core.getMPCChainConfig(chainName);
|
|
413
445
|
const mpcSigner = getMPCSigner({
|
|
414
446
|
chainName,
|
|
415
447
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
416
448
|
});
|
|
417
|
-
const derivationPath = imported ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
418
449
|
let formattedMessage;
|
|
419
450
|
//note: Ecdsa can also be used by bitcoin, but only keccak256 is used by ethereum
|
|
420
451
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
@@ -442,23 +473,23 @@ class DynamicWalletClient {
|
|
|
442
473
|
}
|
|
443
474
|
}
|
|
444
475
|
//todo: need to modify with imported flag
|
|
445
|
-
async sign({ accountAddress, message, chainName
|
|
476
|
+
async sign({ accountAddress, message, chainName }) {
|
|
446
477
|
const wallet = await this.getWallet({
|
|
447
478
|
accountAddress
|
|
448
479
|
});
|
|
449
|
-
this.logger.debug('signing wallet', wallet);
|
|
450
480
|
// Perform the server sign
|
|
451
481
|
const data = await this.serverSign({
|
|
452
482
|
walletId: wallet.walletId,
|
|
453
483
|
message
|
|
454
484
|
});
|
|
485
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
455
486
|
// Perform the client sign and return the signature
|
|
456
487
|
const signature = await this.clientSign({
|
|
457
488
|
chainName,
|
|
458
489
|
message,
|
|
459
490
|
roomId: data.roomId,
|
|
460
491
|
keyShare: wallet.clientKeyShares[0],
|
|
461
|
-
|
|
492
|
+
derivationPath
|
|
462
493
|
});
|
|
463
494
|
return signature;
|
|
464
495
|
}
|
|
@@ -586,7 +617,6 @@ class DynamicWalletClient {
|
|
|
586
617
|
const wallet = await this.getWallet({
|
|
587
618
|
accountAddress
|
|
588
619
|
});
|
|
589
|
-
const chainConfig = core.getMPCChainConfig(chainName);
|
|
590
620
|
const mpcSigner = getMPCSigner({
|
|
591
621
|
chainName,
|
|
592
622
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
@@ -603,7 +633,7 @@ class DynamicWalletClient {
|
|
|
603
633
|
if (!keyExportRaw) {
|
|
604
634
|
throw new Error('Error exporting private key');
|
|
605
635
|
}
|
|
606
|
-
const derivationPath = new Uint32Array(
|
|
636
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
607
637
|
let derivedPrivateKey;
|
|
608
638
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
609
639
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
@@ -616,7 +646,7 @@ class DynamicWalletClient {
|
|
|
616
646
|
derivedPrivateKey
|
|
617
647
|
};
|
|
618
648
|
}
|
|
619
|
-
async offlineExportKey({ chainName, keyShares }) {
|
|
649
|
+
async offlineExportKey({ chainName, keyShares, derivationPath }) {
|
|
620
650
|
try {
|
|
621
651
|
if (!keyShares || keyShares.length < 2) {
|
|
622
652
|
throw new Error(`Must provide at least min threshold of key shares`);
|
|
@@ -633,14 +663,14 @@ class DynamicWalletClient {
|
|
|
633
663
|
throw new Error('Error exporting private key: Export returned null');
|
|
634
664
|
}
|
|
635
665
|
const chainConfig = core.getMPCChainConfig(chainName);
|
|
636
|
-
const
|
|
666
|
+
const walletDerivationPath = !derivationPath ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
637
667
|
let derivedPrivateKey;
|
|
638
668
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
639
|
-
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw,
|
|
669
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
640
670
|
} else if (mpcSigner instanceof web.Ed25519) {
|
|
641
671
|
derivedPrivateKey = keyExportRaw;
|
|
642
672
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
643
|
-
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw,
|
|
673
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
644
674
|
}
|
|
645
675
|
return {
|
|
646
676
|
derivedPrivateKey
|
|
@@ -702,7 +732,8 @@ class DynamicWalletClient {
|
|
|
702
732
|
accountAddress,
|
|
703
733
|
chainName: data.chainName,
|
|
704
734
|
keyShare,
|
|
705
|
-
thresholdSignatureScheme: wallet.thresholdSignatureScheme
|
|
735
|
+
thresholdSignatureScheme: wallet.thresholdSignatureScheme,
|
|
736
|
+
derivationPath: wallet.derivationPath
|
|
706
737
|
});
|
|
707
738
|
});
|
|
708
739
|
return decryptedKeyShares;
|
|
@@ -715,7 +746,7 @@ class DynamicWalletClient {
|
|
|
715
746
|
}
|
|
716
747
|
this.walletMap = JSON.parse(wallets);
|
|
717
748
|
}
|
|
718
|
-
async restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme }) {
|
|
749
|
+
async restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme, derivationPath }) {
|
|
719
750
|
var _this_walletMap_accountAddress;
|
|
720
751
|
this.walletMap[accountAddress] = {
|
|
721
752
|
walletId,
|
|
@@ -725,7 +756,8 @@ class DynamicWalletClient {
|
|
|
725
756
|
...((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeyShares) || [],
|
|
726
757
|
keyShare
|
|
727
758
|
],
|
|
728
|
-
thresholdSignatureScheme
|
|
759
|
+
thresholdSignatureScheme,
|
|
760
|
+
derivationPath
|
|
729
761
|
};
|
|
730
762
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
731
763
|
}
|
|
@@ -749,15 +781,44 @@ class DynamicWalletClient {
|
|
|
749
781
|
thresholdSignatureScheme,
|
|
750
782
|
accountAddress
|
|
751
783
|
});
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
784
|
+
const backupData = {
|
|
785
|
+
keyShares: encryptedKeyShares,
|
|
786
|
+
metadata: {
|
|
787
|
+
version: '1.0',
|
|
788
|
+
createdAt: new Date().toISOString(),
|
|
789
|
+
accountAddress,
|
|
790
|
+
thresholdSignatureScheme,
|
|
791
|
+
hasPassword: true,
|
|
792
|
+
encryption: {
|
|
793
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
794
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
795
|
+
iterations: PBKDF2_ITERATIONS,
|
|
796
|
+
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
797
|
+
algorithmLength: AES_GCM_LENGTH
|
|
798
|
+
},
|
|
799
|
+
shareCount: encryptedKeyShares.length
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
// TODO: handle errors
|
|
803
|
+
const [appUpload, personalUpload] = await Promise.all([
|
|
804
|
+
uploadFileToGoogleDriveAppStorage({
|
|
805
|
+
accessToken,
|
|
806
|
+
fileName: fileName != null ? fileName : suggestedFileName,
|
|
807
|
+
jsonData: backupData
|
|
808
|
+
}),
|
|
809
|
+
uploadFileToGoogleDrivePersonal({
|
|
810
|
+
accessToken,
|
|
811
|
+
fileName: fileName != null ? fileName : suggestedFileName,
|
|
812
|
+
jsonData: backupData
|
|
813
|
+
})
|
|
814
|
+
]);
|
|
757
815
|
await this.apiClient.markKeySharesAsBackedUpGoogleDrive({
|
|
758
816
|
walletId: this.walletMap[accountAddress].walletId
|
|
759
817
|
});
|
|
760
|
-
return
|
|
818
|
+
return {
|
|
819
|
+
appUpload,
|
|
820
|
+
personalUpload
|
|
821
|
+
};
|
|
761
822
|
}
|
|
762
823
|
async restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, name, password }) {
|
|
763
824
|
await this.getWallet({
|
|
@@ -771,17 +832,19 @@ class DynamicWalletClient {
|
|
|
771
832
|
thresholdSignatureScheme,
|
|
772
833
|
accountAddress
|
|
773
834
|
});
|
|
774
|
-
const
|
|
835
|
+
const backupData = await downloadFileFromGoogleDrive({
|
|
775
836
|
accessToken,
|
|
776
837
|
name: name != null ? name : suggestedFileName
|
|
777
838
|
});
|
|
778
|
-
if (!
|
|
779
|
-
throw new Error('No file found');
|
|
839
|
+
if (!backupData) {
|
|
840
|
+
throw new Error('No backup file found');
|
|
780
841
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
842
|
+
// Validate the backup data structure
|
|
843
|
+
if (!backupData.keyShares || !backupData.metadata) {
|
|
844
|
+
throw new Error('Invalid backup format: missing keyShares or metadata');
|
|
845
|
+
}
|
|
846
|
+
const { keyShares } = backupData;
|
|
847
|
+
const decryptedKeyShares = await Promise.all(keyShares.map((keyShare)=>this.decryptKeyShare({
|
|
785
848
|
keyShare,
|
|
786
849
|
password
|
|
787
850
|
})));
|
|
@@ -835,7 +898,7 @@ class DynamicWalletClient {
|
|
|
835
898
|
const rawPublicKey = await this.derivePublicKey({
|
|
836
899
|
chainName,
|
|
837
900
|
keyShare: clientKeygenResult,
|
|
838
|
-
|
|
901
|
+
derivationPath: undefined
|
|
839
902
|
});
|
|
840
903
|
return {
|
|
841
904
|
rawPublicKey,
|
|
@@ -846,7 +909,14 @@ class DynamicWalletClient {
|
|
|
846
909
|
const clientKeyShares = await this.getClientKeyShares({
|
|
847
910
|
accountAddress
|
|
848
911
|
});
|
|
849
|
-
|
|
912
|
+
if (!accountAddress) {
|
|
913
|
+
throw new Error('Must provide an account address');
|
|
914
|
+
}
|
|
915
|
+
const derivationPath = this.walletMap[accountAddress].derivationPath;
|
|
916
|
+
const text = JSON.stringify({
|
|
917
|
+
keyShares: clientKeyShares,
|
|
918
|
+
derivationPath
|
|
919
|
+
});
|
|
850
920
|
const blob = new Blob([
|
|
851
921
|
text
|
|
852
922
|
], {
|
|
@@ -866,7 +936,7 @@ class DynamicWalletClient {
|
|
|
866
936
|
}
|
|
867
937
|
async getWallet({ accountAddress }) {
|
|
868
938
|
if (accountAddress) {
|
|
869
|
-
if (this.walletMap[accountAddress] && this.walletMap[accountAddress].clientKeyShares.length > 0) {
|
|
939
|
+
if (this.walletMap[accountAddress] && this.walletMap[accountAddress].clientKeyShares.length > 0 && this.walletMap[accountAddress].thresholdSignatureScheme && this.walletMap[accountAddress].derivationPath) {
|
|
870
940
|
this.logger.debug('Wallet already exists', this.walletMap[accountAddress]);
|
|
871
941
|
return this.walletMap[accountAddress];
|
|
872
942
|
} else {
|
|
@@ -882,7 +952,8 @@ class DynamicWalletClient {
|
|
|
882
952
|
walletId: wallet.id,
|
|
883
953
|
chainName: wallet.chainName,
|
|
884
954
|
accountAddress,
|
|
885
|
-
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme
|
|
955
|
+
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme,
|
|
956
|
+
derivationPath: walletProperties.derivationPath
|
|
886
957
|
});
|
|
887
958
|
// restore backup
|
|
888
959
|
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
@@ -917,13 +988,15 @@ class DynamicWalletClient {
|
|
|
917
988
|
accountAddress: vc.address
|
|
918
989
|
}));
|
|
919
990
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
920
|
-
var
|
|
991
|
+
var _acc_accountAddress, _acc_accountAddress1, _acc_accountAddress2;
|
|
992
|
+
const accountAddress = wallet.accountAddress;
|
|
921
993
|
acc[wallet.accountAddress] = {
|
|
922
994
|
walletId: wallet.walletId,
|
|
923
995
|
chainName: wallet.chainName,
|
|
924
|
-
accountAddress:
|
|
925
|
-
clientKeyShares: ((
|
|
926
|
-
thresholdSignatureScheme: ((
|
|
996
|
+
accountAddress: accountAddress,
|
|
997
|
+
clientKeyShares: ((_acc_accountAddress = acc[accountAddress]) == null ? void 0 : _acc_accountAddress.clientKeyShares) || [],
|
|
998
|
+
thresholdSignatureScheme: ((_acc_accountAddress1 = acc[accountAddress]) == null ? void 0 : _acc_accountAddress1.thresholdSignatureScheme) || undefined,
|
|
999
|
+
derivationPath: ((_acc_accountAddress2 = acc[accountAddress]) == null ? void 0 : _acc_accountAddress2.derivationPath) || undefined
|
|
927
1000
|
};
|
|
928
1001
|
return acc;
|
|
929
1002
|
}, {});
|
package/index.esm.js
CHANGED
|
@@ -64,6 +64,11 @@ const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddr
|
|
|
64
64
|
return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const PBKDF2_ALGORITHM = 'PBKDF2';
|
|
68
|
+
const PBKDF2_ITERATIONS = 100000;
|
|
69
|
+
const PBKDF2_HASH_ALGORITHM = 'SHA-256';
|
|
70
|
+
const AES_GCM_ALGORITHM = 'AES-GCM';
|
|
71
|
+
const AES_GCM_LENGTH = 256;
|
|
67
72
|
const getKey = async ({ password, salt })=>{
|
|
68
73
|
const passwordBytes = stringToBytes(password);
|
|
69
74
|
const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
|
|
@@ -72,13 +77,13 @@ const getKey = async ({ password, salt })=>{
|
|
|
72
77
|
'deriveKey'
|
|
73
78
|
]);
|
|
74
79
|
return crypto.subtle.deriveKey({
|
|
75
|
-
name:
|
|
80
|
+
name: PBKDF2_ALGORITHM,
|
|
76
81
|
salt,
|
|
77
|
-
iterations:
|
|
78
|
-
hash:
|
|
82
|
+
iterations: PBKDF2_ITERATIONS,
|
|
83
|
+
hash: PBKDF2_HASH_ALGORITHM
|
|
79
84
|
}, initialKey, {
|
|
80
|
-
name:
|
|
81
|
-
length:
|
|
85
|
+
name: AES_GCM_ALGORITHM,
|
|
86
|
+
length: AES_GCM_LENGTH
|
|
82
87
|
}, false, [
|
|
83
88
|
'encrypt',
|
|
84
89
|
'decrypt'
|
|
@@ -97,7 +102,7 @@ const encryptData = async ({ data, password })=>{
|
|
|
97
102
|
const dataBytes = new TextEncoder().encode(data);
|
|
98
103
|
// Encrypt the data
|
|
99
104
|
const encryptedData = await crypto.subtle.encrypt({
|
|
100
|
-
name:
|
|
105
|
+
name: AES_GCM_ALGORITHM,
|
|
101
106
|
iv
|
|
102
107
|
}, key, dataBytes);
|
|
103
108
|
// Convert to base64 strings, ensure proper padding
|
|
@@ -126,24 +131,42 @@ const decryptData = async ({ data, password })=>{
|
|
|
126
131
|
salt: saltBytes
|
|
127
132
|
});
|
|
128
133
|
const decryptedData = await crypto.subtle.decrypt({
|
|
129
|
-
name:
|
|
134
|
+
name: AES_GCM_ALGORITHM,
|
|
130
135
|
iv: ivBytes
|
|
131
136
|
}, key, cipherBytes);
|
|
132
137
|
return new TextDecoder().decode(decryptedData);
|
|
133
138
|
} catch (error) {
|
|
134
139
|
console.error('Decryption error details:', error);
|
|
135
|
-
throw new Error(
|
|
140
|
+
throw new Error('Decryption failed');
|
|
136
141
|
}
|
|
137
142
|
};
|
|
138
143
|
|
|
139
144
|
const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
const uploadFileToGoogleDriveAppStorage = async ({ accessToken, fileName, jsonData })=>{
|
|
146
|
+
return uploadFileToGoogleDrive({
|
|
147
|
+
accessToken,
|
|
148
|
+
fileName,
|
|
149
|
+
jsonData,
|
|
144
150
|
parents: [
|
|
145
151
|
'appDataFolder'
|
|
146
152
|
]
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const uploadFileToGoogleDrivePersonal = async ({ accessToken, fileName, jsonData })=>{
|
|
156
|
+
return uploadFileToGoogleDrive({
|
|
157
|
+
accessToken,
|
|
158
|
+
fileName,
|
|
159
|
+
jsonData,
|
|
160
|
+
parents: [
|
|
161
|
+
'root'
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
const uploadFileToGoogleDrive = async ({ accessToken, fileName, jsonData, parents })=>{
|
|
166
|
+
const metadata = {
|
|
167
|
+
name: fileName,
|
|
168
|
+
mimeType: 'application/json',
|
|
169
|
+
parents
|
|
147
170
|
};
|
|
148
171
|
const form = new FormData();
|
|
149
172
|
form.append('metadata', new Blob([
|
|
@@ -189,6 +212,9 @@ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
|
|
|
189
212
|
accessToken,
|
|
190
213
|
name
|
|
191
214
|
});
|
|
215
|
+
if (!files || files.length === 0) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
192
218
|
// Get the most recent file
|
|
193
219
|
const fileMetadata = files[0];
|
|
194
220
|
// Fetch the file data using the file ID
|
|
@@ -202,7 +228,14 @@ const downloadFileFromGoogleDrive = async ({ accessToken, name })=>{
|
|
|
202
228
|
if (fileRawData.length === 0) {
|
|
203
229
|
return null;
|
|
204
230
|
}
|
|
205
|
-
|
|
231
|
+
try {
|
|
232
|
+
// Just parse and return the data without validation
|
|
233
|
+
// The client will handle validation of the structure
|
|
234
|
+
return JSON.parse(fileRawData);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('Error parsing backup file:', error);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
206
239
|
};
|
|
207
240
|
|
|
208
241
|
const localStorageWriteTest = {
|
|
@@ -322,13 +355,11 @@ class DynamicWalletClient {
|
|
|
322
355
|
const keygenInitResults = await Promise.all(Array(clientThreshold).fill(null).map(()=>mpcSigner.initKeygen()));
|
|
323
356
|
return keygenInitResults;
|
|
324
357
|
}
|
|
325
|
-
async derivePublicKey({ chainName, keyShare,
|
|
358
|
+
async derivePublicKey({ chainName, keyShare, derivationPath }) {
|
|
326
359
|
const mpcSigner = getMPCSigner({
|
|
327
360
|
chainName,
|
|
328
361
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
329
362
|
});
|
|
330
|
-
const chainConfig = getMPCChainConfig(chainName);
|
|
331
|
-
const derivationPath = imported ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
332
363
|
let publicKey;
|
|
333
364
|
if (mpcSigner instanceof Ecdsa) {
|
|
334
365
|
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
@@ -358,10 +389,12 @@ class DynamicWalletClient {
|
|
|
358
389
|
}));
|
|
359
390
|
// only need one client keygen result to derive the public key
|
|
360
391
|
const [clientKeygenResult] = clientKeygenResults;
|
|
392
|
+
const chainConfig = getMPCChainConfig(chainName);
|
|
393
|
+
const derivationPath = new Uint32Array(chainConfig.derivationPath);
|
|
361
394
|
const rawPublicKey = await this.derivePublicKey({
|
|
362
395
|
chainName,
|
|
363
396
|
keyShare: clientKeygenResult,
|
|
364
|
-
|
|
397
|
+
derivationPath
|
|
365
398
|
});
|
|
366
399
|
return {
|
|
367
400
|
rawPublicKey,
|
|
@@ -407,14 +440,12 @@ class DynamicWalletClient {
|
|
|
407
440
|
});
|
|
408
441
|
return data;
|
|
409
442
|
}
|
|
410
|
-
async clientSign({ chainName, message, roomId, keyShare,
|
|
443
|
+
async clientSign({ chainName, message, roomId, keyShare, derivationPath }) {
|
|
411
444
|
try {
|
|
412
|
-
const chainConfig = getMPCChainConfig(chainName);
|
|
413
445
|
const mpcSigner = getMPCSigner({
|
|
414
446
|
chainName,
|
|
415
447
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
416
448
|
});
|
|
417
|
-
const derivationPath = imported ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
418
449
|
let formattedMessage;
|
|
419
450
|
//note: Ecdsa can also be used by bitcoin, but only keccak256 is used by ethereum
|
|
420
451
|
if (mpcSigner instanceof Ecdsa) {
|
|
@@ -442,23 +473,23 @@ class DynamicWalletClient {
|
|
|
442
473
|
}
|
|
443
474
|
}
|
|
444
475
|
//todo: need to modify with imported flag
|
|
445
|
-
async sign({ accountAddress, message, chainName
|
|
476
|
+
async sign({ accountAddress, message, chainName }) {
|
|
446
477
|
const wallet = await this.getWallet({
|
|
447
478
|
accountAddress
|
|
448
479
|
});
|
|
449
|
-
this.logger.debug('signing wallet', wallet);
|
|
450
480
|
// Perform the server sign
|
|
451
481
|
const data = await this.serverSign({
|
|
452
482
|
walletId: wallet.walletId,
|
|
453
483
|
message
|
|
454
484
|
});
|
|
485
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
455
486
|
// Perform the client sign and return the signature
|
|
456
487
|
const signature = await this.clientSign({
|
|
457
488
|
chainName,
|
|
458
489
|
message,
|
|
459
490
|
roomId: data.roomId,
|
|
460
491
|
keyShare: wallet.clientKeyShares[0],
|
|
461
|
-
|
|
492
|
+
derivationPath
|
|
462
493
|
});
|
|
463
494
|
return signature;
|
|
464
495
|
}
|
|
@@ -586,7 +617,6 @@ class DynamicWalletClient {
|
|
|
586
617
|
const wallet = await this.getWallet({
|
|
587
618
|
accountAddress
|
|
588
619
|
});
|
|
589
|
-
const chainConfig = getMPCChainConfig(chainName);
|
|
590
620
|
const mpcSigner = getMPCSigner({
|
|
591
621
|
chainName,
|
|
592
622
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
@@ -603,7 +633,7 @@ class DynamicWalletClient {
|
|
|
603
633
|
if (!keyExportRaw) {
|
|
604
634
|
throw new Error('Error exporting private key');
|
|
605
635
|
}
|
|
606
|
-
const derivationPath = new Uint32Array(
|
|
636
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
607
637
|
let derivedPrivateKey;
|
|
608
638
|
if (mpcSigner instanceof Ecdsa) {
|
|
609
639
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
@@ -616,7 +646,7 @@ class DynamicWalletClient {
|
|
|
616
646
|
derivedPrivateKey
|
|
617
647
|
};
|
|
618
648
|
}
|
|
619
|
-
async offlineExportKey({ chainName, keyShares }) {
|
|
649
|
+
async offlineExportKey({ chainName, keyShares, derivationPath }) {
|
|
620
650
|
try {
|
|
621
651
|
if (!keyShares || keyShares.length < 2) {
|
|
622
652
|
throw new Error(`Must provide at least min threshold of key shares`);
|
|
@@ -633,14 +663,14 @@ class DynamicWalletClient {
|
|
|
633
663
|
throw new Error('Error exporting private key: Export returned null');
|
|
634
664
|
}
|
|
635
665
|
const chainConfig = getMPCChainConfig(chainName);
|
|
636
|
-
const
|
|
666
|
+
const walletDerivationPath = !derivationPath ? undefined : new Uint32Array(chainConfig.derivationPath);
|
|
637
667
|
let derivedPrivateKey;
|
|
638
668
|
if (mpcSigner instanceof Ecdsa) {
|
|
639
|
-
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw,
|
|
669
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
640
670
|
} else if (mpcSigner instanceof Ed25519) {
|
|
641
671
|
derivedPrivateKey = keyExportRaw;
|
|
642
672
|
} else if (mpcSigner instanceof BIP340) {
|
|
643
|
-
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw,
|
|
673
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
644
674
|
}
|
|
645
675
|
return {
|
|
646
676
|
derivedPrivateKey
|
|
@@ -702,7 +732,8 @@ class DynamicWalletClient {
|
|
|
702
732
|
accountAddress,
|
|
703
733
|
chainName: data.chainName,
|
|
704
734
|
keyShare,
|
|
705
|
-
thresholdSignatureScheme: wallet.thresholdSignatureScheme
|
|
735
|
+
thresholdSignatureScheme: wallet.thresholdSignatureScheme,
|
|
736
|
+
derivationPath: wallet.derivationPath
|
|
706
737
|
});
|
|
707
738
|
});
|
|
708
739
|
return decryptedKeyShares;
|
|
@@ -715,7 +746,7 @@ class DynamicWalletClient {
|
|
|
715
746
|
}
|
|
716
747
|
this.walletMap = JSON.parse(wallets);
|
|
717
748
|
}
|
|
718
|
-
async restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme }) {
|
|
749
|
+
async restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme, derivationPath }) {
|
|
719
750
|
var _this_walletMap_accountAddress;
|
|
720
751
|
this.walletMap[accountAddress] = {
|
|
721
752
|
walletId,
|
|
@@ -725,7 +756,8 @@ class DynamicWalletClient {
|
|
|
725
756
|
...((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeyShares) || [],
|
|
726
757
|
keyShare
|
|
727
758
|
],
|
|
728
|
-
thresholdSignatureScheme
|
|
759
|
+
thresholdSignatureScheme,
|
|
760
|
+
derivationPath
|
|
729
761
|
};
|
|
730
762
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
731
763
|
}
|
|
@@ -749,15 +781,44 @@ class DynamicWalletClient {
|
|
|
749
781
|
thresholdSignatureScheme,
|
|
750
782
|
accountAddress
|
|
751
783
|
});
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
784
|
+
const backupData = {
|
|
785
|
+
keyShares: encryptedKeyShares,
|
|
786
|
+
metadata: {
|
|
787
|
+
version: '1.0',
|
|
788
|
+
createdAt: new Date().toISOString(),
|
|
789
|
+
accountAddress,
|
|
790
|
+
thresholdSignatureScheme,
|
|
791
|
+
hasPassword: true,
|
|
792
|
+
encryption: {
|
|
793
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
794
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
795
|
+
iterations: PBKDF2_ITERATIONS,
|
|
796
|
+
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
797
|
+
algorithmLength: AES_GCM_LENGTH
|
|
798
|
+
},
|
|
799
|
+
shareCount: encryptedKeyShares.length
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
// TODO: handle errors
|
|
803
|
+
const [appUpload, personalUpload] = await Promise.all([
|
|
804
|
+
uploadFileToGoogleDriveAppStorage({
|
|
805
|
+
accessToken,
|
|
806
|
+
fileName: fileName != null ? fileName : suggestedFileName,
|
|
807
|
+
jsonData: backupData
|
|
808
|
+
}),
|
|
809
|
+
uploadFileToGoogleDrivePersonal({
|
|
810
|
+
accessToken,
|
|
811
|
+
fileName: fileName != null ? fileName : suggestedFileName,
|
|
812
|
+
jsonData: backupData
|
|
813
|
+
})
|
|
814
|
+
]);
|
|
757
815
|
await this.apiClient.markKeySharesAsBackedUpGoogleDrive({
|
|
758
816
|
walletId: this.walletMap[accountAddress].walletId
|
|
759
817
|
});
|
|
760
|
-
return
|
|
818
|
+
return {
|
|
819
|
+
appUpload,
|
|
820
|
+
personalUpload
|
|
821
|
+
};
|
|
761
822
|
}
|
|
762
823
|
async restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, name, password }) {
|
|
763
824
|
await this.getWallet({
|
|
@@ -771,17 +832,19 @@ class DynamicWalletClient {
|
|
|
771
832
|
thresholdSignatureScheme,
|
|
772
833
|
accountAddress
|
|
773
834
|
});
|
|
774
|
-
const
|
|
835
|
+
const backupData = await downloadFileFromGoogleDrive({
|
|
775
836
|
accessToken,
|
|
776
837
|
name: name != null ? name : suggestedFileName
|
|
777
838
|
});
|
|
778
|
-
if (!
|
|
779
|
-
throw new Error('No file found');
|
|
839
|
+
if (!backupData) {
|
|
840
|
+
throw new Error('No backup file found');
|
|
780
841
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
842
|
+
// Validate the backup data structure
|
|
843
|
+
if (!backupData.keyShares || !backupData.metadata) {
|
|
844
|
+
throw new Error('Invalid backup format: missing keyShares or metadata');
|
|
845
|
+
}
|
|
846
|
+
const { keyShares } = backupData;
|
|
847
|
+
const decryptedKeyShares = await Promise.all(keyShares.map((keyShare)=>this.decryptKeyShare({
|
|
785
848
|
keyShare,
|
|
786
849
|
password
|
|
787
850
|
})));
|
|
@@ -835,7 +898,7 @@ class DynamicWalletClient {
|
|
|
835
898
|
const rawPublicKey = await this.derivePublicKey({
|
|
836
899
|
chainName,
|
|
837
900
|
keyShare: clientKeygenResult,
|
|
838
|
-
|
|
901
|
+
derivationPath: undefined
|
|
839
902
|
});
|
|
840
903
|
return {
|
|
841
904
|
rawPublicKey,
|
|
@@ -846,7 +909,14 @@ class DynamicWalletClient {
|
|
|
846
909
|
const clientKeyShares = await this.getClientKeyShares({
|
|
847
910
|
accountAddress
|
|
848
911
|
});
|
|
849
|
-
|
|
912
|
+
if (!accountAddress) {
|
|
913
|
+
throw new Error('Must provide an account address');
|
|
914
|
+
}
|
|
915
|
+
const derivationPath = this.walletMap[accountAddress].derivationPath;
|
|
916
|
+
const text = JSON.stringify({
|
|
917
|
+
keyShares: clientKeyShares,
|
|
918
|
+
derivationPath
|
|
919
|
+
});
|
|
850
920
|
const blob = new Blob([
|
|
851
921
|
text
|
|
852
922
|
], {
|
|
@@ -866,7 +936,7 @@ class DynamicWalletClient {
|
|
|
866
936
|
}
|
|
867
937
|
async getWallet({ accountAddress }) {
|
|
868
938
|
if (accountAddress) {
|
|
869
|
-
if (this.walletMap[accountAddress] && this.walletMap[accountAddress].clientKeyShares.length > 0) {
|
|
939
|
+
if (this.walletMap[accountAddress] && this.walletMap[accountAddress].clientKeyShares.length > 0 && this.walletMap[accountAddress].thresholdSignatureScheme && this.walletMap[accountAddress].derivationPath) {
|
|
870
940
|
this.logger.debug('Wallet already exists', this.walletMap[accountAddress]);
|
|
871
941
|
return this.walletMap[accountAddress];
|
|
872
942
|
} else {
|
|
@@ -882,7 +952,8 @@ class DynamicWalletClient {
|
|
|
882
952
|
walletId: wallet.id,
|
|
883
953
|
chainName: wallet.chainName,
|
|
884
954
|
accountAddress,
|
|
885
|
-
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme
|
|
955
|
+
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme,
|
|
956
|
+
derivationPath: walletProperties.derivationPath
|
|
886
957
|
});
|
|
887
958
|
// restore backup
|
|
888
959
|
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
@@ -917,13 +988,15 @@ class DynamicWalletClient {
|
|
|
917
988
|
accountAddress: vc.address
|
|
918
989
|
}));
|
|
919
990
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
920
|
-
var
|
|
991
|
+
var _acc_accountAddress, _acc_accountAddress1, _acc_accountAddress2;
|
|
992
|
+
const accountAddress = wallet.accountAddress;
|
|
921
993
|
acc[wallet.accountAddress] = {
|
|
922
994
|
walletId: wallet.walletId,
|
|
923
995
|
chainName: wallet.chainName,
|
|
924
|
-
accountAddress:
|
|
925
|
-
clientKeyShares: ((
|
|
926
|
-
thresholdSignatureScheme: ((
|
|
996
|
+
accountAddress: accountAddress,
|
|
997
|
+
clientKeyShares: ((_acc_accountAddress = acc[accountAddress]) == null ? void 0 : _acc_accountAddress.clientKeyShares) || [],
|
|
998
|
+
thresholdSignatureScheme: ((_acc_accountAddress1 = acc[accountAddress]) == null ? void 0 : _acc_accountAddress1.thresholdSignatureScheme) || undefined,
|
|
999
|
+
derivationPath: ((_acc_accountAddress2 = acc[accountAddress]) == null ? void 0 : _acc_accountAddress2.derivationPath) || undefined
|
|
927
1000
|
};
|
|
928
1001
|
return acc;
|
|
929
1002
|
}, {});
|
package/package.json
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export declare const PBKDF2_ALGORITHM = "PBKDF2";
|
|
2
|
+
export declare const PBKDF2_ITERATIONS = 100000;
|
|
3
|
+
export declare const PBKDF2_HASH_ALGORITHM = "SHA-256";
|
|
4
|
+
export declare const AES_GCM_ALGORITHM = "AES-GCM";
|
|
5
|
+
export declare const AES_GCM_LENGTH = 256;
|
|
1
6
|
export declare const encryptData: ({ data, password, }: {
|
|
2
7
|
data: string;
|
|
3
8
|
password: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/backup/encryption.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/backup/encryption.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,gBAAgB,WAAW,CAAC;AACzC,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAC3C,eAAO,MAAM,cAAc,MAAM,CAAC;AA4BlC,eAAO,MAAM,WAAW,wBAGrB;IACD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;;;;EA4BA,CAAC;AAEF,eAAO,MAAM,WAAW,wBAGrB;IACD,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,QAAQ,EAAE,MAAM,CAAC;CAClB,oBA0BA,CAAC"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const uploadFileToGoogleDriveAppStorage: ({ accessToken, fileName, jsonData, }: {
|
|
2
2
|
accessToken: string;
|
|
3
3
|
fileName: string;
|
|
4
|
-
jsonData:
|
|
4
|
+
jsonData: unknown;
|
|
5
|
+
}) => Promise<any>;
|
|
6
|
+
export declare const uploadFileToGoogleDrivePersonal: ({ accessToken, fileName, jsonData, }: {
|
|
7
|
+
accessToken: string;
|
|
8
|
+
fileName: string;
|
|
9
|
+
jsonData: unknown;
|
|
5
10
|
}) => Promise<any>;
|
|
6
11
|
export declare const listFilesFromGoogleDrive: ({ accessToken, name, }: {
|
|
7
12
|
accessToken: string;
|
|
@@ -10,5 +15,5 @@ export declare const listFilesFromGoogleDrive: ({ accessToken, name, }: {
|
|
|
10
15
|
export declare const downloadFileFromGoogleDrive: ({ accessToken, name, }: {
|
|
11
16
|
accessToken: string;
|
|
12
17
|
name: string;
|
|
13
|
-
}) => Promise<
|
|
18
|
+
}) => Promise<unknown | null>;
|
|
14
19
|
//# sourceMappingURL=googleDrive.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"googleDrive.d.ts","sourceRoot":"","sources":["../../../src/backup/providers/googleDrive.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"googleDrive.d.ts","sourceRoot":"","sources":["../../../src/backup/providers/googleDrive.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iCAAiC,yCAI3C;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,iBAOA,CAAC;AAEF,eAAO,MAAM,+BAA+B,yCAIzC;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB,iBAOA,CAAC;AAiDF,eAAO,MAAM,wBAAwB,2BAGlC;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,iBAsBA,CAAC;AAEF,eAAO,MAAM,2BAA2B,2BAGrC;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,KAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAiCzB,CAAC"}
|
package/src/client.d.ts
CHANGED
|
@@ -31,10 +31,10 @@ export declare class DynamicWalletClient {
|
|
|
31
31
|
chainName: string;
|
|
32
32
|
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
33
33
|
}): Promise<ClientInitKeygenResult[]>;
|
|
34
|
-
derivePublicKey({ chainName, keyShare,
|
|
34
|
+
derivePublicKey({ chainName, keyShare, derivationPath, }: {
|
|
35
35
|
chainName: string;
|
|
36
36
|
keyShare: ClientKeyShare;
|
|
37
|
-
|
|
37
|
+
derivationPath: Uint32Array | undefined;
|
|
38
38
|
}): Promise<EcdsaPublicKey | Uint8Array | undefined>;
|
|
39
39
|
clientKeyGen({ chainName, roomId, serverKeygenIds, clientKeygenInitResults, thresholdSignatureScheme, }: {
|
|
40
40
|
chainName: string;
|
|
@@ -57,18 +57,17 @@ export declare class DynamicWalletClient {
|
|
|
57
57
|
walletId: string;
|
|
58
58
|
message: string | Uint8Array;
|
|
59
59
|
}): Promise<any>;
|
|
60
|
-
clientSign({ chainName, message, roomId, keyShare,
|
|
60
|
+
clientSign({ chainName, message, roomId, keyShare, derivationPath, }: {
|
|
61
61
|
chainName: string;
|
|
62
62
|
message: string | Uint8Array;
|
|
63
63
|
roomId: string;
|
|
64
64
|
keyShare: ClientKeyShare;
|
|
65
|
-
|
|
65
|
+
derivationPath: Uint32Array | undefined;
|
|
66
66
|
}): Promise<Uint8Array | EcdsaSignature>;
|
|
67
|
-
sign({ accountAddress, message, chainName,
|
|
67
|
+
sign({ accountAddress, message, chainName, }: {
|
|
68
68
|
accountAddress?: string;
|
|
69
69
|
message: string | Uint8Array;
|
|
70
70
|
chainName: string;
|
|
71
|
-
imported?: boolean;
|
|
72
71
|
}): Promise<Uint8Array | EcdsaSignature>;
|
|
73
72
|
refreshWalletAccountShares({ accountAddress, chainName, }: {
|
|
74
73
|
accountAddress: string;
|
|
@@ -115,9 +114,10 @@ export declare class DynamicWalletClient {
|
|
|
115
114
|
}): Promise<{
|
|
116
115
|
derivedPrivateKey: string | undefined;
|
|
117
116
|
}>;
|
|
118
|
-
offlineExportKey({ chainName, keyShares, }: {
|
|
117
|
+
offlineExportKey({ chainName, keyShares, derivationPath, }: {
|
|
119
118
|
chainName: string;
|
|
120
119
|
keyShares: ClientKeyShare[];
|
|
120
|
+
derivationPath?: string;
|
|
121
121
|
}): Promise<{
|
|
122
122
|
derivedPrivateKey: string | undefined;
|
|
123
123
|
}>;
|
|
@@ -138,19 +138,23 @@ export declare class DynamicWalletClient {
|
|
|
138
138
|
password?: string;
|
|
139
139
|
}): Promise<any[]>;
|
|
140
140
|
restoreWallets(): Promise<void>;
|
|
141
|
-
restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme, }: {
|
|
141
|
+
restoreBackupShare({ walletId, accountAddress, chainName, keyShare, thresholdSignatureScheme, derivationPath, }: {
|
|
142
142
|
walletId: string;
|
|
143
143
|
accountAddress: string;
|
|
144
144
|
chainName: string;
|
|
145
145
|
keyShare: ClientKeyShare;
|
|
146
146
|
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
147
|
+
derivationPath?: string;
|
|
147
148
|
}): Promise<void>;
|
|
148
149
|
backupKeySharesToGoogleDrive({ accountAddress, fileName, oauthAccountId, password, }: {
|
|
149
150
|
accountAddress: string;
|
|
150
151
|
fileName?: string;
|
|
151
152
|
oauthAccountId: string;
|
|
152
153
|
password?: string;
|
|
153
|
-
}): Promise<
|
|
154
|
+
}): Promise<{
|
|
155
|
+
appUpload: any;
|
|
156
|
+
personalUpload: any;
|
|
157
|
+
}>;
|
|
154
158
|
restoreBackupFromGoogleDrive({ accountAddress, oauthAccountId, name, password, }: {
|
|
155
159
|
accountAddress: string;
|
|
156
160
|
oauthAccountId: string;
|
package/src/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../packages/src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,wBAAwB,EACxB,gBAAgB,EAIjB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAIL,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAElB,cAAc,EACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../packages/src/client.ts"],"names":[],"mappings":"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"}
|
package/src/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface WalletProperties {
|
|
|
9
9
|
accountAddress: string;
|
|
10
10
|
clientKeyShares: ClientKeyShare[];
|
|
11
11
|
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
12
|
+
derivationPath?: string;
|
|
12
13
|
}
|
|
13
14
|
export interface DynamicWalletClientProps {
|
|
14
15
|
environmentId: string;
|
|
@@ -18,4 +19,21 @@ export interface DynamicWalletClientProps {
|
|
|
18
19
|
debug?: boolean;
|
|
19
20
|
baseMPCRelayApiUrl?: string;
|
|
20
21
|
}
|
|
22
|
+
export type BackupData = {
|
|
23
|
+
keyShares: string[];
|
|
24
|
+
metadata: {
|
|
25
|
+
version: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
accountAddress: string;
|
|
28
|
+
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
29
|
+
hasPassword: boolean;
|
|
30
|
+
encryption?: {
|
|
31
|
+
algorithm: string;
|
|
32
|
+
keyDerivation: string;
|
|
33
|
+
iterations: number;
|
|
34
|
+
hashAlgorithm: string;
|
|
35
|
+
};
|
|
36
|
+
shareCount: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
21
39
|
//# sourceMappingURL=types.d.ts.map
|
package/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../packages/src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,GAAG,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,wBAAwB,EAAE,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../packages/src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,GAAG,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,wBAAwB,EAAE,wBAAwB,CAAC;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,wBAAwB,EAAE,wBAAwB,CAAC;QACnD,WAAW,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE;YACX,SAAS,EAAE,MAAM,CAAC;YAClB,aAAa,EAAE,MAAM,CAAC;YACtB,UAAU,EAAE,MAAM,CAAC;YACnB,aAAa,EAAE,MAAM,CAAC;SACvB,CAAC;QACF,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC"}
|