@dynamic-labs-wallet/browser 0.0.0-pr384.2 → 0.0.0-pr526.0
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 +685 -342
- package/index.esm.js +680 -345
- package/internal/core/bip340.cjs +1 -0
- package/internal/core/common.cjs +1 -0
- package/internal/core/ecdsa.cjs +1 -0
- package/internal/core/ed25519.cjs +1 -0
- package/internal/core/ed25519_exportable.cjs +1 -0
- package/internal/core/index.cjs +1 -0
- package/internal/core/sr25519.cjs +1 -0
- package/internal/core/types.cjs +1 -0
- package/internal/web/generated/libmpc_executor.js +2 -2
- package/internal/web/index.d.ts +1 -1
- package/internal/web/index.js +1 -1
- package/package.json +4 -4
- package/src/client.d.ts +97 -34
- package/src/client.d.ts.map +1 -1
- package/src/constants.d.ts +2 -0
- package/src/constants.d.ts.map +1 -1
- package/src/services/encryption.d.ts +19 -0
- package/src/services/encryption.d.ts.map +1 -0
- package/src/types.d.ts +36 -1
- package/src/types.d.ts.map +1 -1
- package/src/utils.d.ts +8 -2
- package/src/utils.d.ts.map +1 -1
- package/internal/core/bip340.js +0 -1
- package/internal/core/common.js +0 -1
- package/internal/core/ecdsa.js +0 -1
- package/internal/core/ed25519.js +0 -1
- package/internal/core/ed25519_exportable.js +0 -1
- package/internal/core/index.js +0 -1
- package/internal/core/package.json +0 -17
- package/internal/core/sr25519.js +0 -1
- package/internal/core/types.js +0 -1
- package/internal/web/package.json +0 -17
- /package/internal/core/{native.js → native.cjs} +0 -0
package/index.cjs.js
CHANGED
|
@@ -5,10 +5,10 @@ var web = require('#internal/web');
|
|
|
5
5
|
var semver = require('semver');
|
|
6
6
|
var uuid = require('uuid');
|
|
7
7
|
var logger$1 = require('@dynamic-labs/logger');
|
|
8
|
+
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
8
9
|
var loadArgon2idWasm = require('argon2id');
|
|
9
10
|
var axios = require('axios');
|
|
10
11
|
var createHttpError = require('http-errors');
|
|
11
|
-
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
12
12
|
|
|
13
13
|
function _extends() {
|
|
14
14
|
_extends = Object.assign || function assign(target) {
|
|
@@ -463,10 +463,12 @@ const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
|
|
|
463
463
|
WalletKit: '4.25.4',
|
|
464
464
|
ClientSDK: '0.1.0-alpha.0'
|
|
465
465
|
};
|
|
466
|
+
const ROOM_CACHE_COUNT = 5;
|
|
466
467
|
|
|
467
468
|
const isBrowser = ()=>typeof window !== 'undefined';
|
|
468
|
-
const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
|
|
469
|
-
|
|
469
|
+
const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress, isGoogleDrive = false })=>{
|
|
470
|
+
const suffix = isGoogleDrive ? '-google-drive' : '';
|
|
471
|
+
return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}${suffix}.json`;
|
|
470
472
|
};
|
|
471
473
|
const getClientKeyShareBackupInfo = (params)=>{
|
|
472
474
|
var _params_walletProperties, _params_walletProperties_keyShares_;
|
|
@@ -475,7 +477,8 @@ const getClientKeyShareBackupInfo = (params)=>{
|
|
|
475
477
|
[core.BackupLocation.GOOGLE_DRIVE]: [],
|
|
476
478
|
[core.BackupLocation.ICLOUD]: [],
|
|
477
479
|
[core.BackupLocation.USER]: [],
|
|
478
|
-
[core.BackupLocation.EXTERNAL]: []
|
|
480
|
+
[core.BackupLocation.EXTERNAL]: [],
|
|
481
|
+
[core.BackupLocation.DELEGATED]: []
|
|
479
482
|
};
|
|
480
483
|
if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
|
|
481
484
|
return {
|
|
@@ -605,6 +608,19 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
|
|
|
605
608
|
}
|
|
606
609
|
};
|
|
607
610
|
};
|
|
611
|
+
const downloadStringAsFile = ({ filename, content, mimeType = 'application/json' })=>{
|
|
612
|
+
const blob = new Blob([
|
|
613
|
+
content
|
|
614
|
+
], {
|
|
615
|
+
type: mimeType
|
|
616
|
+
});
|
|
617
|
+
const url = URL.createObjectURL(blob);
|
|
618
|
+
const a = document.createElement('a');
|
|
619
|
+
a.href = url;
|
|
620
|
+
a.download = filename;
|
|
621
|
+
a.click();
|
|
622
|
+
URL.revokeObjectURL(url);
|
|
623
|
+
};
|
|
608
624
|
|
|
609
625
|
/**
|
|
610
626
|
* Uploads a backup to Google Drive App
|
|
@@ -651,6 +667,84 @@ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatu
|
|
|
651
667
|
}
|
|
652
668
|
};
|
|
653
669
|
|
|
670
|
+
const ALG_LABEL_RSA = 'HYBRID-RSA-AES-256';
|
|
671
|
+
/**
|
|
672
|
+
* Convert base64 to base64url encoding
|
|
673
|
+
*/ const toBase64Url = (buffer)=>{
|
|
674
|
+
const base64 = Buffer.from(buffer).toString('base64');
|
|
675
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
676
|
+
};
|
|
677
|
+
/**
|
|
678
|
+
* Convert ArrayBuffer to base64url
|
|
679
|
+
*/ const arrayBufferToBase64Url = (buffer)=>{
|
|
680
|
+
return toBase64Url(buffer);
|
|
681
|
+
};
|
|
682
|
+
/**
|
|
683
|
+
* Import RSA public key from PEM format
|
|
684
|
+
*/ const importRSAPublicKey = async (publicKeyPem)=>{
|
|
685
|
+
// Remove PEM headers and decode base64
|
|
686
|
+
const pemHeader = '-----BEGIN PUBLIC KEY-----';
|
|
687
|
+
const pemFooter = '-----END PUBLIC KEY-----';
|
|
688
|
+
const pemContents = publicKeyPem.replace(pemHeader, '').replace(pemFooter, '').replace(/\s/g, '');
|
|
689
|
+
const binaryDer = Buffer.from(pemContents, 'base64').toString('binary');
|
|
690
|
+
const keyData = new Uint8Array(binaryDer.length);
|
|
691
|
+
for(let i = 0; i < binaryDer.length; i++){
|
|
692
|
+
keyData[i] = binaryDer.charCodeAt(i);
|
|
693
|
+
}
|
|
694
|
+
return await crypto.subtle.importKey('spki', keyData, {
|
|
695
|
+
name: 'RSA-OAEP',
|
|
696
|
+
hash: 'SHA-256'
|
|
697
|
+
}, false, [
|
|
698
|
+
'encrypt'
|
|
699
|
+
]);
|
|
700
|
+
};
|
|
701
|
+
// encodedEnvelopeBytes intentionally omitted; alg/ct/ek/iv/tag/kid is sufficient
|
|
702
|
+
/**
|
|
703
|
+
* Encrypts data using HYBRID-RSA-AES-256 encryption scheme with Web Crypto API.
|
|
704
|
+
* 1. Generate random AES-256 key
|
|
705
|
+
* 2. Encrypt AES key with RSA public key
|
|
706
|
+
* 3. Encrypt data with AES-256-GCM
|
|
707
|
+
*/ const encryptDelegatedKeyShare = async (data, publicKeyPem, keyId)=>{
|
|
708
|
+
try {
|
|
709
|
+
// Step 1: Generate a random AES-256 key and 16-byte IV
|
|
710
|
+
const aesKey = await crypto.subtle.generateKey({
|
|
711
|
+
name: 'AES-GCM',
|
|
712
|
+
length: 256
|
|
713
|
+
}, true, [
|
|
714
|
+
'encrypt'
|
|
715
|
+
]);
|
|
716
|
+
const iv = crypto.getRandomValues(new Uint8Array(16)); // 128-bit IV for GCM
|
|
717
|
+
// Step 2: Encrypt the data with AES-256-GCM
|
|
718
|
+
const plaintext = new TextEncoder().encode(data);
|
|
719
|
+
const encryptedData = await crypto.subtle.encrypt({
|
|
720
|
+
name: 'AES-GCM',
|
|
721
|
+
iv: iv
|
|
722
|
+
}, aesKey, plaintext);
|
|
723
|
+
// Extract the auth tag from the encrypted data (last 16 bytes)
|
|
724
|
+
const encryptedDataArray = new Uint8Array(encryptedData);
|
|
725
|
+
const authTag = encryptedDataArray.slice(-16);
|
|
726
|
+
const ciphertext = encryptedDataArray.slice(0, -16);
|
|
727
|
+
// Step 3: Encrypt the AES key with RSA public key
|
|
728
|
+
const rsaPublicKey = await importRSAPublicKey(publicKeyPem);
|
|
729
|
+
// Export the AES key to encrypt it
|
|
730
|
+
const aesKeyData = await crypto.subtle.exportKey('raw', aesKey);
|
|
731
|
+
const encryptedAesKey = await crypto.subtle.encrypt({
|
|
732
|
+
name: 'RSA-OAEP'
|
|
733
|
+
}, rsaPublicKey, aesKeyData);
|
|
734
|
+
return _extends({
|
|
735
|
+
alg: ALG_LABEL_RSA,
|
|
736
|
+
iv: arrayBufferToBase64Url(iv.buffer),
|
|
737
|
+
ct: arrayBufferToBase64Url(ciphertext.buffer),
|
|
738
|
+
tag: arrayBufferToBase64Url(authTag.buffer),
|
|
739
|
+
ek: arrayBufferToBase64Url(encryptedAesKey)
|
|
740
|
+
}, keyId ? {
|
|
741
|
+
kid: keyId
|
|
742
|
+
} : {});
|
|
743
|
+
} catch (error) {
|
|
744
|
+
throw new Error(`Encryption failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
|
|
654
748
|
const localStorageWriteTest = {
|
|
655
749
|
tested: false,
|
|
656
750
|
writable: false
|
|
@@ -720,7 +814,61 @@ const localStorageWriteTest = {
|
|
|
720
814
|
}
|
|
721
815
|
});
|
|
722
816
|
|
|
817
|
+
const createDelegationWithGoogleDriveDistribution = ({ existingShares, delegatedShare })=>({
|
|
818
|
+
dynamicBackendShares: existingShares,
|
|
819
|
+
googleDriveShares: existingShares,
|
|
820
|
+
delegatedShare
|
|
821
|
+
});
|
|
822
|
+
const createDelegationOnlyDistribution = ({ existingShares, delegatedShare })=>({
|
|
823
|
+
dynamicBackendShares: existingShares,
|
|
824
|
+
googleDriveShares: [],
|
|
825
|
+
delegatedShare
|
|
826
|
+
});
|
|
827
|
+
const createGoogleDriveOnlyDistribution = ({ allShares })=>({
|
|
828
|
+
dynamicBackendShares: allShares.slice(0, -1),
|
|
829
|
+
googleDriveShares: allShares.slice(-1)
|
|
830
|
+
});
|
|
831
|
+
const createDynamicOnlyDistribution = ({ allShares })=>({
|
|
832
|
+
dynamicBackendShares: allShares,
|
|
833
|
+
googleDriveShares: []
|
|
834
|
+
});
|
|
835
|
+
const hasGoogleDriveBackup = (backupInfo)=>{
|
|
836
|
+
var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE, _backupInfo_backups;
|
|
837
|
+
var _backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length;
|
|
838
|
+
return ((_backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_GOOGLE_DRIVE = _backupInfo_backups[core.BackupLocation.GOOGLE_DRIVE]) == null ? void 0 : _backupInfo_backups_BackupLocation_GOOGLE_DRIVE.length) != null ? _backupInfo_backups_BackupLocation_GOOGLE_DRIVE_length : 0) > 0;
|
|
839
|
+
};
|
|
840
|
+
const hasDelegatedBackup = (backupInfo)=>{
|
|
841
|
+
var _backupInfo_backups_BackupLocation_DELEGATED, _backupInfo_backups;
|
|
842
|
+
var _backupInfo_backups_BackupLocation_DELEGATED_length;
|
|
843
|
+
return ((_backupInfo_backups_BackupLocation_DELEGATED_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_DELEGATED = _backupInfo_backups[core.BackupLocation.DELEGATED]) == null ? void 0 : _backupInfo_backups_BackupLocation_DELEGATED.length) != null ? _backupInfo_backups_BackupLocation_DELEGATED_length : 0) > 0;
|
|
844
|
+
};
|
|
845
|
+
/**
|
|
846
|
+
* Distribution for adding Google Drive backup to an existing delegation.
|
|
847
|
+
* Client's shares go to both Dynamic and Google Drive.
|
|
848
|
+
* delegatedShare is undefined - we don't re-publish, but preserve the location.
|
|
849
|
+
*/ const createAddGoogleDriveToExistingDelegationDistribution = ({ clientShares })=>({
|
|
850
|
+
dynamicBackendShares: clientShares,
|
|
851
|
+
googleDriveShares: clientShares
|
|
852
|
+
});
|
|
853
|
+
|
|
723
854
|
class DynamicWalletClient {
|
|
855
|
+
async initializeForwardMPCClient() {
|
|
856
|
+
try {
|
|
857
|
+
await this.apiClient.forwardMPCClient.connect();
|
|
858
|
+
this.logger.info('Connected to ForwardMPC enclave websocket. Instance: ' + this.instanceId);
|
|
859
|
+
try {
|
|
860
|
+
await this.apiClient.forwardMPCClient.handshake();
|
|
861
|
+
this.logger.debug('Handshaked with ForwardMPC enclave websocket. Instance: ' + this.instanceId);
|
|
862
|
+
} catch (error) {
|
|
863
|
+
this.logger.error('Error handshaking with ForwardMPC enclave websocket. Instance: ' + this.instanceId, error);
|
|
864
|
+
}
|
|
865
|
+
} catch (error) {
|
|
866
|
+
this.logger.error('Error connecting to ForwardMPC enclave websocket. Instance: ' + this.instanceId, {
|
|
867
|
+
error,
|
|
868
|
+
environmentId: this.environmentId
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
724
872
|
getAuthMode() {
|
|
725
873
|
return this.authMode;
|
|
726
874
|
}
|
|
@@ -792,30 +940,37 @@ class DynamicWalletClient {
|
|
|
792
940
|
throw error;
|
|
793
941
|
}
|
|
794
942
|
}
|
|
795
|
-
async initialize() {
|
|
943
|
+
async initialize(traceContext) {
|
|
796
944
|
if (this.initializePromise) {
|
|
797
945
|
return await this.initializePromise;
|
|
798
946
|
}
|
|
799
947
|
this.logger.debug('[DynamicWaasWalletClient] Initializing Dynamic Waas Wallet SDK');
|
|
800
|
-
this.initializePromise = this._initialize();
|
|
948
|
+
this.initializePromise = this._initialize(traceContext);
|
|
801
949
|
const result = await this.initializePromise;
|
|
802
950
|
this.logger.debug('[DynamicWaasWalletClient] Dynamic Waas Wallet SDK initialized');
|
|
803
951
|
return result;
|
|
804
952
|
}
|
|
805
953
|
/**
|
|
806
954
|
* Client initialization logic
|
|
807
|
-
*/ async _initialize() {
|
|
955
|
+
*/ async _initialize(traceContext) {
|
|
808
956
|
try {
|
|
809
957
|
const initializePromises = [
|
|
810
|
-
this.restoreWallets()
|
|
958
|
+
this.restoreWallets(),
|
|
959
|
+
this.createRooms({
|
|
960
|
+
roomType: sdkApiCore.RoomTypeEnum.Threshold,
|
|
961
|
+
thresholdSignatureScheme: core.ThresholdSignatureScheme.TWO_OF_TWO,
|
|
962
|
+
roomCount: ROOM_CACHE_COUNT
|
|
963
|
+
})
|
|
811
964
|
];
|
|
812
965
|
await Promise.all(initializePromises);
|
|
813
966
|
return {
|
|
814
|
-
error: null
|
|
967
|
+
error: null,
|
|
968
|
+
traceContext
|
|
815
969
|
};
|
|
816
970
|
} catch (error) {
|
|
817
971
|
return {
|
|
818
|
-
error
|
|
972
|
+
error,
|
|
973
|
+
traceContext
|
|
819
974
|
};
|
|
820
975
|
}
|
|
821
976
|
}
|
|
@@ -894,7 +1049,7 @@ class DynamicWalletClient {
|
|
|
894
1049
|
clientKeygenResults
|
|
895
1050
|
};
|
|
896
1051
|
}
|
|
897
|
-
async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete }) {
|
|
1052
|
+
async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
|
|
898
1053
|
const dynamicRequestId = uuid.v4();
|
|
899
1054
|
try {
|
|
900
1055
|
const clientKeygenInitResults = await this.clientInitializeKeyGen({
|
|
@@ -902,11 +1057,12 @@ class DynamicWalletClient {
|
|
|
902
1057
|
thresholdSignatureScheme
|
|
903
1058
|
});
|
|
904
1059
|
const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
|
|
905
|
-
this.
|
|
1060
|
+
this.instrument('[DynamicWaasWalletClient] Initialized client key generation', _extends({
|
|
906
1061
|
clientKeygenIds: clientKeygenIds.join(', '),
|
|
907
1062
|
chainName,
|
|
908
|
-
thresholdSignatureScheme
|
|
909
|
-
|
|
1063
|
+
thresholdSignatureScheme,
|
|
1064
|
+
key: 'keyGen'
|
|
1065
|
+
}, this.getTraceContext(traceContext)));
|
|
910
1066
|
const { roomId, serverKeygenIds } = await this.serverInitializeKeyGen({
|
|
911
1067
|
chainName,
|
|
912
1068
|
clientKeygenIds,
|
|
@@ -914,12 +1070,14 @@ class DynamicWalletClient {
|
|
|
914
1070
|
thresholdSignatureScheme,
|
|
915
1071
|
onCeremonyComplete
|
|
916
1072
|
});
|
|
917
|
-
this.
|
|
1073
|
+
this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
|
|
918
1074
|
roomId,
|
|
919
1075
|
clientKeygenIds: clientKeygenIds.join(', '),
|
|
920
1076
|
serverKeygenIds: serverKeygenIds.join(', '),
|
|
921
|
-
chainName
|
|
922
|
-
|
|
1077
|
+
chainName,
|
|
1078
|
+
key: 'keyGen',
|
|
1079
|
+
operation: 'serverKeyGen'
|
|
1080
|
+
}, this.getTraceContext(traceContext)));
|
|
923
1081
|
const { rawPublicKey, clientKeygenResults: clientKeyShares } = await this.clientKeyGen({
|
|
924
1082
|
chainName,
|
|
925
1083
|
roomId,
|
|
@@ -927,15 +1085,17 @@ class DynamicWalletClient {
|
|
|
927
1085
|
clientKeygenInitResults,
|
|
928
1086
|
thresholdSignatureScheme
|
|
929
1087
|
});
|
|
930
|
-
this.
|
|
1088
|
+
this.instrument('[DynamicWaasWalletClient] Client key generation completed', _extends({
|
|
931
1089
|
roomId,
|
|
932
1090
|
serverKeygenIds: serverKeygenIds.join(', '),
|
|
933
1091
|
clientKeygenIds: clientKeygenIds.join(', '),
|
|
934
1092
|
chainName,
|
|
935
1093
|
thresholdSignatureScheme,
|
|
936
1094
|
rawPublicKey,
|
|
937
|
-
clientKeySharesCount: clientKeyShares.length
|
|
938
|
-
|
|
1095
|
+
clientKeySharesCount: clientKeyShares.length,
|
|
1096
|
+
key: 'keyGen',
|
|
1097
|
+
operation: 'clientKeyGen'
|
|
1098
|
+
}, this.getTraceContext(traceContext)));
|
|
939
1099
|
return {
|
|
940
1100
|
rawPublicKey,
|
|
941
1101
|
clientKeyShares
|
|
@@ -944,16 +1104,16 @@ class DynamicWalletClient {
|
|
|
944
1104
|
logError({
|
|
945
1105
|
message: 'Error in keyGen',
|
|
946
1106
|
error: error,
|
|
947
|
-
context: {
|
|
1107
|
+
context: _extends({
|
|
948
1108
|
chainName,
|
|
949
1109
|
thresholdSignatureScheme,
|
|
950
1110
|
dynamicRequestId
|
|
951
|
-
}
|
|
1111
|
+
}, this.getTraceContext(traceContext))
|
|
952
1112
|
});
|
|
953
1113
|
throw error;
|
|
954
1114
|
}
|
|
955
1115
|
}
|
|
956
|
-
async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
|
|
1116
|
+
async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete, traceContext }) {
|
|
957
1117
|
const dynamicRequestId = uuid.v4();
|
|
958
1118
|
try {
|
|
959
1119
|
const mpcSigner = getMPCSigner({
|
|
@@ -965,11 +1125,13 @@ class DynamicWalletClient {
|
|
|
965
1125
|
thresholdSignatureScheme
|
|
966
1126
|
});
|
|
967
1127
|
const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
|
|
968
|
-
this.
|
|
1128
|
+
this.instrument('[DynamicWaasWalletClient] Client key generation initialized', _extends({
|
|
969
1129
|
clientKeygenIds: clientKeygenIds.join(', '),
|
|
970
1130
|
chainName,
|
|
971
|
-
thresholdSignatureScheme
|
|
972
|
-
|
|
1131
|
+
thresholdSignatureScheme,
|
|
1132
|
+
key: 'importRawPrivateKey',
|
|
1133
|
+
operation: 'clientKeyGen'
|
|
1134
|
+
}, this.getTraceContext(traceContext)));
|
|
973
1135
|
const { roomId, serverKeygenIds } = await this.apiClient.importPrivateKey({
|
|
974
1136
|
chainName,
|
|
975
1137
|
clientKeygenIds,
|
|
@@ -978,12 +1140,14 @@ class DynamicWalletClient {
|
|
|
978
1140
|
onError,
|
|
979
1141
|
onCeremonyComplete
|
|
980
1142
|
});
|
|
981
|
-
this.
|
|
1143
|
+
this.instrument('[DynamicWaasWalletClient] Server key generation initialized', _extends({
|
|
982
1144
|
roomId,
|
|
983
1145
|
clientKeygenIds: clientKeygenIds.join(', '),
|
|
984
1146
|
serverKeygenIds: serverKeygenIds.join(', '),
|
|
985
|
-
chainName
|
|
986
|
-
|
|
1147
|
+
chainName,
|
|
1148
|
+
key: 'importRawPrivateKey',
|
|
1149
|
+
operation: 'serverImportPrivateKey'
|
|
1150
|
+
}, this.getTraceContext(traceContext)));
|
|
987
1151
|
const { threshold } = core.getTSSConfig(thresholdSignatureScheme);
|
|
988
1152
|
const clientKeygenResults = await Promise.all(clientKeygenInitResults.map(async (currentInit, index)=>{
|
|
989
1153
|
const otherClientKeygenIds = clientKeygenInitResults.filter((init)=>init.keygenId !== currentInit.keygenId).map((init)=>init.keygenId);
|
|
@@ -1008,14 +1172,16 @@ class DynamicWalletClient {
|
|
|
1008
1172
|
keyShare: clientKeygenResult,
|
|
1009
1173
|
derivationPath: undefined
|
|
1010
1174
|
});
|
|
1011
|
-
this.
|
|
1175
|
+
this.instrument('[DynamicWaasWalletClient] Completed import of raw private key', _extends({
|
|
1012
1176
|
rawPublicKey,
|
|
1013
1177
|
chainName,
|
|
1014
1178
|
thresholdSignatureScheme,
|
|
1015
1179
|
roomId,
|
|
1016
1180
|
serverKeygenIds: serverKeygenIds.join(', '),
|
|
1017
|
-
clientKeygenIds: clientKeygenIds.join(', ')
|
|
1018
|
-
|
|
1181
|
+
clientKeygenIds: clientKeygenIds.join(', '),
|
|
1182
|
+
key: 'importRawPrivateKey',
|
|
1183
|
+
operation: 'importRawPrivateKey'
|
|
1184
|
+
}, this.getTraceContext(traceContext)));
|
|
1019
1185
|
return {
|
|
1020
1186
|
rawPublicKey,
|
|
1021
1187
|
clientKeyShares: clientKeygenResults
|
|
@@ -1024,16 +1190,16 @@ class DynamicWalletClient {
|
|
|
1024
1190
|
logError({
|
|
1025
1191
|
message: 'Error in importRawPrivateKey',
|
|
1026
1192
|
error: error,
|
|
1027
|
-
context: {
|
|
1193
|
+
context: _extends({
|
|
1028
1194
|
chainName,
|
|
1029
1195
|
thresholdSignatureScheme,
|
|
1030
1196
|
dynamicRequestId
|
|
1031
|
-
}
|
|
1197
|
+
}, this.getTraceContext(traceContext))
|
|
1032
1198
|
});
|
|
1033
1199
|
throw error;
|
|
1034
1200
|
}
|
|
1035
1201
|
}
|
|
1036
|
-
async serverSign({ walletId, message, isFormatted, mfaToken, context, onError, dynamicRequestId }) {
|
|
1202
|
+
async serverSign({ walletId, message, isFormatted, mfaToken, roomId, context, onError, dynamicRequestId, traceContext }) {
|
|
1037
1203
|
// Create the room and sign the message
|
|
1038
1204
|
if (typeof message !== 'string') {
|
|
1039
1205
|
message = `0x${Buffer.from(message).toString('hex')}`;
|
|
@@ -1044,44 +1210,101 @@ class DynamicWalletClient {
|
|
|
1044
1210
|
isFormatted,
|
|
1045
1211
|
dynamicRequestId,
|
|
1046
1212
|
mfaToken,
|
|
1213
|
+
roomId,
|
|
1047
1214
|
context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined,
|
|
1048
|
-
onError
|
|
1215
|
+
onError,
|
|
1216
|
+
forwardMPCClientEnabled: this.forwardMPCEnabled,
|
|
1217
|
+
traceContext
|
|
1049
1218
|
});
|
|
1050
1219
|
return data;
|
|
1051
1220
|
}
|
|
1052
|
-
async
|
|
1221
|
+
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext }) {
|
|
1222
|
+
try {
|
|
1223
|
+
if (!this.apiClient.forwardMPCClient.connected) {
|
|
1224
|
+
await this.initializeForwardMPCClient();
|
|
1225
|
+
}
|
|
1226
|
+
const messageForForwardMPC = core.serializeMessageForForwardMPC({
|
|
1227
|
+
message,
|
|
1228
|
+
isFormatted,
|
|
1229
|
+
chainName
|
|
1230
|
+
});
|
|
1231
|
+
this.logger.info('Forward MPC enabled, signing message with forward MPC', this.getTraceContext(traceContext));
|
|
1232
|
+
const signature = await this.apiClient.forwardMPCClient.signMessage({
|
|
1233
|
+
keyshare: keyShare,
|
|
1234
|
+
message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
|
|
1235
|
+
relayDomain: this.baseMPCRelayApiUrl || '',
|
|
1236
|
+
signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
|
|
1237
|
+
hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
|
|
1238
|
+
derivationPath: derivationPath,
|
|
1239
|
+
roomUuid: roomId
|
|
1240
|
+
});
|
|
1241
|
+
const signatureBytes = signature.data.signature;
|
|
1242
|
+
if (!(signatureBytes instanceof Uint8Array)) {
|
|
1243
|
+
throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
|
|
1244
|
+
}
|
|
1245
|
+
// Convert to EcdsaSignature
|
|
1246
|
+
if (chainName === 'EVM') {
|
|
1247
|
+
const ecdsaSignature = web.EcdsaSignature.fromBuffer(signatureBytes);
|
|
1248
|
+
return ecdsaSignature;
|
|
1249
|
+
} else {
|
|
1250
|
+
return signatureBytes;
|
|
1251
|
+
}
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
this.logger.error('Error signing message with forward MPC client', {
|
|
1254
|
+
error,
|
|
1255
|
+
environmentId: this.environmentId,
|
|
1256
|
+
userId: this.userId,
|
|
1257
|
+
dynamicRequestId
|
|
1258
|
+
});
|
|
1259
|
+
throw error;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
async clientSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext }) {
|
|
1053
1263
|
try {
|
|
1054
1264
|
const mpcSigner = getMPCSigner({
|
|
1055
1265
|
chainName,
|
|
1056
1266
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
1057
1267
|
});
|
|
1058
1268
|
const formattedMessage = isFormatted ? new web.MessageHash(message) : formatMessage(chainName, message);
|
|
1059
|
-
this.logger.debug('[DynamicWaasWalletClient] Starting client sign', {
|
|
1269
|
+
this.logger.debug('[DynamicWaasWalletClient] Starting client sign', _extends({
|
|
1060
1270
|
chainName,
|
|
1061
1271
|
message,
|
|
1062
1272
|
roomId,
|
|
1063
1273
|
derivationPath,
|
|
1064
1274
|
isFormatted
|
|
1065
|
-
});
|
|
1275
|
+
}, this.getTraceContext(traceContext)));
|
|
1276
|
+
if (this.forwardMPCEnabled) {
|
|
1277
|
+
return this.forwardMPCClientSign({
|
|
1278
|
+
chainName,
|
|
1279
|
+
message,
|
|
1280
|
+
roomId,
|
|
1281
|
+
keyShare,
|
|
1282
|
+
derivationPath,
|
|
1283
|
+
formattedMessage,
|
|
1284
|
+
dynamicRequestId,
|
|
1285
|
+
isFormatted,
|
|
1286
|
+
traceContext
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1066
1289
|
const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
|
|
1067
1290
|
return signature;
|
|
1068
1291
|
} catch (error) {
|
|
1069
1292
|
logError({
|
|
1070
1293
|
message: 'Error in clientSign',
|
|
1071
1294
|
error: error,
|
|
1072
|
-
context: {
|
|
1295
|
+
context: _extends({
|
|
1073
1296
|
chainName,
|
|
1074
1297
|
roomId,
|
|
1075
1298
|
derivationPath,
|
|
1076
1299
|
isFormatted,
|
|
1077
1300
|
dynamicRequestId
|
|
1078
|
-
}
|
|
1301
|
+
}, this.getTraceContext(traceContext))
|
|
1079
1302
|
});
|
|
1080
1303
|
throw error;
|
|
1081
1304
|
}
|
|
1082
1305
|
}
|
|
1083
1306
|
//todo: need to modify with imported flag
|
|
1084
|
-
async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError }) {
|
|
1307
|
+
async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError, traceContext }) {
|
|
1085
1308
|
const dynamicRequestId = uuid.v4();
|
|
1086
1309
|
try {
|
|
1087
1310
|
await this.verifyPassword({
|
|
@@ -1096,23 +1319,41 @@ class DynamicWalletClient {
|
|
|
1096
1319
|
walletOperation: core.WalletOperation.SIGN_MESSAGE,
|
|
1097
1320
|
signedSessionId
|
|
1098
1321
|
});
|
|
1099
|
-
|
|
1100
|
-
|
|
1322
|
+
const room = await this.getRoom();
|
|
1323
|
+
let roomId = room == null ? void 0 : room.roomId;
|
|
1324
|
+
if (roomId) {
|
|
1325
|
+
this.instrument('[DynamicWaasWalletClient] Using cached room', _extends({
|
|
1326
|
+
key: 'sign',
|
|
1327
|
+
operation: 'cachedRoomFound',
|
|
1328
|
+
accountAddress,
|
|
1329
|
+
chainName,
|
|
1330
|
+
walletId: wallet.walletId,
|
|
1331
|
+
roomId
|
|
1332
|
+
}, this.getTraceContext(traceContext)));
|
|
1333
|
+
}
|
|
1334
|
+
// Perform the server sign - always call, but only await if roomId is not provided
|
|
1335
|
+
const serverSignPromise = this.serverSign({
|
|
1101
1336
|
walletId: wallet.walletId,
|
|
1102
1337
|
message,
|
|
1103
1338
|
isFormatted,
|
|
1104
1339
|
mfaToken,
|
|
1340
|
+
roomId,
|
|
1105
1341
|
context,
|
|
1106
1342
|
onError,
|
|
1107
|
-
dynamicRequestId
|
|
1343
|
+
dynamicRequestId,
|
|
1344
|
+
traceContext
|
|
1108
1345
|
});
|
|
1109
|
-
|
|
1346
|
+
// Only await if roomId is not provided
|
|
1347
|
+
roomId = roomId != null ? roomId : (await serverSignPromise).roomId;
|
|
1348
|
+
this.instrument('[DynamicWaasWalletClient] Server sign completed', _extends({
|
|
1110
1349
|
message,
|
|
1111
1350
|
accountAddress,
|
|
1112
1351
|
walletId: wallet.walletId,
|
|
1113
|
-
roomId
|
|
1114
|
-
dynamicRequestId
|
|
1115
|
-
|
|
1352
|
+
roomId,
|
|
1353
|
+
dynamicRequestId,
|
|
1354
|
+
key: 'sign',
|
|
1355
|
+
operation: 'serverSign'
|
|
1356
|
+
}, this.getTraceContext(traceContext)));
|
|
1116
1357
|
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
1117
1358
|
// Perform the client sign and return the signature
|
|
1118
1359
|
const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
|
|
@@ -1121,35 +1362,39 @@ class DynamicWalletClient {
|
|
|
1121
1362
|
const signature = await this.clientSign({
|
|
1122
1363
|
chainName,
|
|
1123
1364
|
message,
|
|
1124
|
-
roomId
|
|
1365
|
+
roomId,
|
|
1125
1366
|
keyShare: clientKeyShares[0],
|
|
1126
1367
|
derivationPath,
|
|
1127
1368
|
isFormatted,
|
|
1128
|
-
dynamicRequestId
|
|
1369
|
+
dynamicRequestId,
|
|
1370
|
+
traceContext
|
|
1129
1371
|
});
|
|
1130
|
-
this.
|
|
1372
|
+
this.instrument('[DynamicWaasWalletClient] Client sign completed', _extends({
|
|
1373
|
+
accountAddress,
|
|
1131
1374
|
chainName,
|
|
1132
1375
|
message,
|
|
1133
|
-
roomId
|
|
1376
|
+
roomId,
|
|
1134
1377
|
derivationPath,
|
|
1135
|
-
isFormatted
|
|
1136
|
-
|
|
1378
|
+
isFormatted,
|
|
1379
|
+
key: 'sign',
|
|
1380
|
+
operation: 'clientSign'
|
|
1381
|
+
}, this.getTraceContext(traceContext)));
|
|
1137
1382
|
return signature;
|
|
1138
1383
|
} catch (error) {
|
|
1139
1384
|
logError({
|
|
1140
1385
|
message: 'Error in sign',
|
|
1141
1386
|
error: error,
|
|
1142
|
-
context: {
|
|
1387
|
+
context: _extends({
|
|
1143
1388
|
accountAddress,
|
|
1144
1389
|
chainName,
|
|
1145
1390
|
isFormatted: isFormatted ? 'true' : 'false',
|
|
1146
1391
|
dynamicRequestId
|
|
1147
|
-
}
|
|
1392
|
+
}, this.getTraceContext(traceContext))
|
|
1148
1393
|
});
|
|
1149
1394
|
throw error;
|
|
1150
1395
|
}
|
|
1151
1396
|
}
|
|
1152
|
-
async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken }) {
|
|
1397
|
+
async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
|
|
1153
1398
|
const dynamicRequestId = uuid.v4();
|
|
1154
1399
|
try {
|
|
1155
1400
|
await this.verifyPassword({
|
|
@@ -1193,11 +1438,11 @@ class DynamicWalletClient {
|
|
|
1193
1438
|
logError({
|
|
1194
1439
|
message: 'Error in refreshWalletAccountShares',
|
|
1195
1440
|
error: error,
|
|
1196
|
-
context: {
|
|
1441
|
+
context: _extends({
|
|
1197
1442
|
accountAddress,
|
|
1198
1443
|
chainName,
|
|
1199
1444
|
dynamicRequestId
|
|
1200
|
-
}
|
|
1445
|
+
}, this.getTraceContext(traceContext))
|
|
1201
1446
|
});
|
|
1202
1447
|
throw error;
|
|
1203
1448
|
}
|
|
@@ -1253,7 +1498,7 @@ class DynamicWalletClient {
|
|
|
1253
1498
|
existingClientKeyShares
|
|
1254
1499
|
};
|
|
1255
1500
|
}
|
|
1256
|
-
async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken }) {
|
|
1501
|
+
async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, backupToGoogleDrive = false, delegateToProjectEnvironment = false, mfaToken, revokeDelegation = false }) {
|
|
1257
1502
|
const dynamicRequestId = uuid.v4();
|
|
1258
1503
|
try {
|
|
1259
1504
|
await this.verifyPassword({
|
|
@@ -1292,7 +1537,8 @@ class DynamicWalletClient {
|
|
|
1292
1537
|
newThresholdSignatureScheme,
|
|
1293
1538
|
dynamicRequestId,
|
|
1294
1539
|
delegateToProjectEnvironment,
|
|
1295
|
-
mfaToken
|
|
1540
|
+
mfaToken,
|
|
1541
|
+
revokeDelegation
|
|
1296
1542
|
});
|
|
1297
1543
|
const { roomId, serverKeygenIds, newServerKeygenIds = [] } = data;
|
|
1298
1544
|
// Get the MPC config for the threshold signature scheme
|
|
@@ -1307,35 +1553,68 @@ class DynamicWalletClient {
|
|
|
1307
1553
|
chainName,
|
|
1308
1554
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
1309
1555
|
});
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1556
|
+
const existingResharePromises = existingClientKeyShares.map((keyShare)=>mpcSigner.reshareRemainingParty(roomId, newMpcConfig.threshold, keyShare, allPartyKeygenIds));
|
|
1557
|
+
const newResharePromises = newClientInitKeygenResults.map((keygenResult)=>mpcSigner.reshareNewParty(roomId, oldMpcConfig.threshold, newMpcConfig.threshold, keygenResult, allPartyKeygenIds));
|
|
1558
|
+
// Run both share parties in parallel by group
|
|
1559
|
+
const [existingReshareResults, newReshareResults] = await Promise.all([
|
|
1560
|
+
Promise.all(existingResharePromises),
|
|
1561
|
+
Promise.all(newResharePromises)
|
|
1313
1562
|
]);
|
|
1563
|
+
const clientKeysharesToLocalStorage = delegateToProjectEnvironment ? [
|
|
1564
|
+
...existingReshareResults
|
|
1565
|
+
] : [
|
|
1566
|
+
...existingReshareResults,
|
|
1567
|
+
...newReshareResults
|
|
1568
|
+
];
|
|
1314
1569
|
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
1315
1570
|
thresholdSignatureScheme: newThresholdSignatureScheme
|
|
1316
1571
|
});
|
|
1572
|
+
// store client key shares to localStorage
|
|
1317
1573
|
await this.setClientKeySharesToLocalStorage({
|
|
1318
1574
|
accountAddress,
|
|
1319
|
-
clientKeyShares:
|
|
1575
|
+
clientKeyShares: clientKeysharesToLocalStorage,
|
|
1320
1576
|
overwriteOrMerge: 'overwrite'
|
|
1321
1577
|
});
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1578
|
+
const allClientShares = [
|
|
1579
|
+
...existingReshareResults,
|
|
1580
|
+
...newReshareResults
|
|
1581
|
+
];
|
|
1582
|
+
let distribution;
|
|
1583
|
+
if (delegateToProjectEnvironment && backupToGoogleDrive) {
|
|
1584
|
+
// Delegation + Google Drive: Client's existing share backs up to both Dynamic and Google Drive.
|
|
1585
|
+
// The new share goes to the webhook for delegation.
|
|
1586
|
+
distribution = createDelegationWithGoogleDriveDistribution({
|
|
1587
|
+
existingShares: existingReshareResults,
|
|
1588
|
+
delegatedShare: newReshareResults[0]
|
|
1589
|
+
});
|
|
1590
|
+
} else if (delegateToProjectEnvironment) {
|
|
1591
|
+
// Delegation only: Client's existing share backs up to Dynamic.
|
|
1592
|
+
// The new share goes to the webhook for delegation. No Google Drive backup.
|
|
1593
|
+
distribution = createDelegationOnlyDistribution({
|
|
1594
|
+
existingShares: existingReshareResults,
|
|
1595
|
+
delegatedShare: newReshareResults[0]
|
|
1596
|
+
});
|
|
1597
|
+
} else if (backupToGoogleDrive) {
|
|
1598
|
+
// Google Drive only: Split shares between Dynamic (N-1) and Google Drive (1).
|
|
1599
|
+
// The last share (new share) goes to Google Drive.
|
|
1600
|
+
distribution = createGoogleDriveOnlyDistribution({
|
|
1601
|
+
allShares: allClientShares
|
|
1327
1602
|
});
|
|
1328
1603
|
} else {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
signedSessionId,
|
|
1333
|
-
backupToGoogleDrive
|
|
1604
|
+
// No delegation, no Google Drive: All shares go to Dynamic backend only.
|
|
1605
|
+
distribution = createDynamicOnlyDistribution({
|
|
1606
|
+
allShares: allClientShares
|
|
1334
1607
|
});
|
|
1335
1608
|
}
|
|
1609
|
+
await this.backupSharesWithDistribution({
|
|
1610
|
+
accountAddress,
|
|
1611
|
+
password,
|
|
1612
|
+
signedSessionId,
|
|
1613
|
+
distribution
|
|
1614
|
+
});
|
|
1336
1615
|
} catch (error) {
|
|
1337
1616
|
logError({
|
|
1338
|
-
message: 'Error in reshare',
|
|
1617
|
+
message: 'Error in reshare, resetting wallet to previous state',
|
|
1339
1618
|
error: error,
|
|
1340
1619
|
context: {
|
|
1341
1620
|
accountAddress,
|
|
@@ -1346,52 +1625,25 @@ class DynamicWalletClient {
|
|
|
1346
1625
|
dynamicRequestId
|
|
1347
1626
|
}
|
|
1348
1627
|
});
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
async sendKeySharesToDelegatedAccess({ accountAddress, chainName, delegatedKeyShares, environmentId, userId, walletId }) {
|
|
1353
|
-
try {
|
|
1354
|
-
if (!this.delegatedAccessEndpoint) {
|
|
1355
|
-
throw new Error('Cannot send key shares to delegated access because delegated access endpoint is not set');
|
|
1356
|
-
}
|
|
1357
|
-
const delegatedAccessEndpoint = this.delegatedAccessEndpoint;
|
|
1358
|
-
const response = await fetch(delegatedAccessEndpoint, {
|
|
1359
|
-
method: 'POST',
|
|
1360
|
-
body: JSON.stringify({
|
|
1361
|
-
chainName,
|
|
1362
|
-
accountAddress,
|
|
1363
|
-
delegatedKeyShares,
|
|
1364
|
-
environmentId,
|
|
1365
|
-
userId,
|
|
1366
|
-
walletId
|
|
1367
|
-
})
|
|
1628
|
+
// reset user wallet when reshare fails, this would allow the client to recover wallets from an active state
|
|
1629
|
+
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
1630
|
+
thresholdSignatureScheme: oldThresholdSignatureScheme
|
|
1368
1631
|
});
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
message: 'Error in sendKeySharesToDelegatedAccess',
|
|
1374
|
-
error: error,
|
|
1375
|
-
context: {
|
|
1376
|
-
accountAddress,
|
|
1377
|
-
chainName
|
|
1378
|
-
}
|
|
1632
|
+
await this.setClientKeySharesToLocalStorage({
|
|
1633
|
+
accountAddress,
|
|
1634
|
+
clientKeyShares: [],
|
|
1635
|
+
overwriteOrMerge: 'overwrite'
|
|
1379
1636
|
});
|
|
1380
1637
|
throw error;
|
|
1381
1638
|
}
|
|
1382
1639
|
}
|
|
1383
|
-
async
|
|
1640
|
+
async performDelegationOperation({ accountAddress, password, signedSessionId, mfaToken, newThresholdSignatureScheme, revokeDelegation = false, operationName }) {
|
|
1384
1641
|
try {
|
|
1642
|
+
var _this_walletMap_accountAddress;
|
|
1385
1643
|
const delegateToProjectEnvironment = this.featureFlags && this.featureFlags[core.FEATURE_FLAGS.ENABLE_DELEGATED_KEY_SHARES_FLAG] === true;
|
|
1386
1644
|
if (!delegateToProjectEnvironment) {
|
|
1387
1645
|
throw new Error('Delegation is not allowed for this project environment');
|
|
1388
1646
|
}
|
|
1389
|
-
const environmentSettings = await this.apiClient.getEnvironmentSettings();
|
|
1390
|
-
const delegatedAccessEndpoint = environmentSettings.sdk.waas.delegatedAccessEndpoint;
|
|
1391
|
-
if (!delegatedAccessEndpoint) {
|
|
1392
|
-
throw new Error('Cannot delegate key shares because verified access endpoint is not set in the environment settings');
|
|
1393
|
-
}
|
|
1394
|
-
this.delegatedAccessEndpoint = delegatedAccessEndpoint;
|
|
1395
1647
|
const wallet = await this.getWallet({
|
|
1396
1648
|
accountAddress,
|
|
1397
1649
|
walletOperation: core.WalletOperation.REACH_ALL_PARTIES,
|
|
@@ -1402,28 +1654,21 @@ class DynamicWalletClient {
|
|
|
1402
1654
|
throw new Error('Delegation is not allowed for SUI');
|
|
1403
1655
|
}
|
|
1404
1656
|
const currentThresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
});
|
|
1418
|
-
const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
|
|
1419
|
-
const delegatedKeyShares = backupInfo.backups[core.BackupLocation.EXTERNAL] || [];
|
|
1420
|
-
return delegatedKeyShares;
|
|
1421
|
-
} else {
|
|
1422
|
-
throw new Error('Delegation is not allowed for this threshold signature scheme');
|
|
1423
|
-
}
|
|
1657
|
+
await this.reshare({
|
|
1658
|
+
chainName: this.walletMap[accountAddress].chainName,
|
|
1659
|
+
accountAddress,
|
|
1660
|
+
oldThresholdSignatureScheme: currentThresholdSignatureScheme,
|
|
1661
|
+
newThresholdSignatureScheme,
|
|
1662
|
+
password,
|
|
1663
|
+
signedSessionId,
|
|
1664
|
+
backupToGoogleDrive: hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo),
|
|
1665
|
+
delegateToProjectEnvironment: true,
|
|
1666
|
+
mfaToken,
|
|
1667
|
+
revokeDelegation
|
|
1668
|
+
});
|
|
1424
1669
|
} catch (error) {
|
|
1425
1670
|
logError({
|
|
1426
|
-
message:
|
|
1671
|
+
message: `Error in ${operationName}`,
|
|
1427
1672
|
error: error,
|
|
1428
1673
|
context: {
|
|
1429
1674
|
accountAddress
|
|
@@ -1432,7 +1677,31 @@ class DynamicWalletClient {
|
|
|
1432
1677
|
throw error;
|
|
1433
1678
|
}
|
|
1434
1679
|
}
|
|
1435
|
-
async
|
|
1680
|
+
async delegateKeyShares({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
|
|
1681
|
+
await this.performDelegationOperation({
|
|
1682
|
+
accountAddress,
|
|
1683
|
+
password,
|
|
1684
|
+
signedSessionId,
|
|
1685
|
+
mfaToken,
|
|
1686
|
+
newThresholdSignatureScheme: core.ThresholdSignatureScheme.TWO_OF_THREE,
|
|
1687
|
+
operationName: 'delegateKeyShares'
|
|
1688
|
+
});
|
|
1689
|
+
const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
|
|
1690
|
+
const delegatedKeyShares = backupInfo.backups[core.BackupLocation.DELEGATED] || [];
|
|
1691
|
+
return delegatedKeyShares;
|
|
1692
|
+
}
|
|
1693
|
+
async revokeDelegation({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
|
|
1694
|
+
await this.performDelegationOperation({
|
|
1695
|
+
accountAddress,
|
|
1696
|
+
password,
|
|
1697
|
+
signedSessionId,
|
|
1698
|
+
mfaToken,
|
|
1699
|
+
newThresholdSignatureScheme: core.ThresholdSignatureScheme.TWO_OF_TWO,
|
|
1700
|
+
revokeDelegation: true,
|
|
1701
|
+
operationName: 'revokeDelegation'
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
async exportKey({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
|
|
1436
1705
|
const dynamicRequestId = uuid.v4();
|
|
1437
1706
|
try {
|
|
1438
1707
|
const wallet = await this.getWallet({
|
|
@@ -1458,24 +1727,24 @@ class DynamicWalletClient {
|
|
|
1458
1727
|
dynamicRequestId,
|
|
1459
1728
|
mfaToken
|
|
1460
1729
|
});
|
|
1461
|
-
this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', {
|
|
1730
|
+
this.logger.debug('[DynamicWaasWalletClient] Starting export of private key', _extends({
|
|
1462
1731
|
accountAddress,
|
|
1463
1732
|
chainName,
|
|
1464
1733
|
walletId: wallet.walletId,
|
|
1465
1734
|
exportId,
|
|
1466
1735
|
roomId: data.roomId
|
|
1467
|
-
});
|
|
1736
|
+
}, this.getTraceContext(traceContext)));
|
|
1468
1737
|
const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, clientKeyShares[0], exportId);
|
|
1469
1738
|
if (!keyExportRaw) {
|
|
1470
1739
|
throw new Error('Error exporting private key');
|
|
1471
1740
|
}
|
|
1472
|
-
this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', {
|
|
1741
|
+
this.logger.debug('[DynamicWaasWalletClient] Completed export of private key', _extends({
|
|
1473
1742
|
accountAddress,
|
|
1474
1743
|
chainName,
|
|
1475
1744
|
walletId: wallet.walletId,
|
|
1476
1745
|
exportId,
|
|
1477
1746
|
roomId: data.roomId
|
|
1478
|
-
});
|
|
1747
|
+
}, this.getTraceContext(traceContext)));
|
|
1479
1748
|
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
1480
1749
|
let derivedPrivateKey;
|
|
1481
1750
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
@@ -1492,11 +1761,11 @@ class DynamicWalletClient {
|
|
|
1492
1761
|
logError({
|
|
1493
1762
|
message: 'Error in exportKey',
|
|
1494
1763
|
error: error,
|
|
1495
|
-
context: {
|
|
1764
|
+
context: _extends({
|
|
1496
1765
|
accountAddress,
|
|
1497
1766
|
chainName,
|
|
1498
1767
|
dynamicRequestId
|
|
1499
|
-
}
|
|
1768
|
+
}, this.getTraceContext(traceContext))
|
|
1500
1769
|
});
|
|
1501
1770
|
throw error;
|
|
1502
1771
|
}
|
|
@@ -1596,43 +1865,14 @@ class DynamicWalletClient {
|
|
|
1596
1865
|
});
|
|
1597
1866
|
await ((_this_storage = this.storage) == null ? void 0 : _this_storage.setItem(accountAddress, stringifiedClientKeyShares));
|
|
1598
1867
|
}
|
|
1599
|
-
|
|
1600
|
-
* Central backup orchestrator that encrypts and stores wallet key shares.
|
|
1601
|
-
*
|
|
1602
|
-
* This method serves as the main backup coordinator, handling the distribution of encrypted
|
|
1603
|
-
* key shares between Dynamic's backend and Google Drive based on the wallet's threshold scheme.
|
|
1604
|
-
* It is used by multiple operations including reshare, refresh, and manual backup requests.
|
|
1605
|
-
*
|
|
1606
|
-
* **Backup Distribution Strategy:**
|
|
1607
|
-
* - **Single share wallets**: All shares stored on Dynamic's backend only
|
|
1608
|
-
* - **Multi-share wallets (2+)**: When backing up to Google Drive, N-1 shares on Dynamic's backend, 1 share on Google Drive
|
|
1609
|
-
* - **Multi-share wallets (2+)**: When not backing up to Google Drive, all shares on Dynamic's backend
|
|
1610
|
-
*
|
|
1611
|
-
* **Process Flow:**
|
|
1612
|
-
* 1. Encrypts all client key shares with the provided password (or environment ID if no password)
|
|
1613
|
-
* 2. For multi-share wallets (2+): conditionally distributes N-1 to backend, 1 to Google Drive
|
|
1614
|
-
* 3. For other configurations: stores all shares on Dynamic's backend
|
|
1615
|
-
* 4. Updates backup metadata and synchronizes wallet state
|
|
1616
|
-
* 5. Persists the updated wallet map to local storage
|
|
1617
|
-
*
|
|
1618
|
-
* @param params - The backup operation parameters
|
|
1619
|
-
* @param params.accountAddress - The account address of the wallet to backup
|
|
1620
|
-
* @param params.clientKeyShares - Optional specific key shares to backup (uses localStorage if not provided)
|
|
1621
|
-
* @param params.password - Optional password for encryption (uses environment ID if not provided)
|
|
1622
|
-
* @param params.signedSessionId - Optional signed session ID for authentication
|
|
1623
|
-
* @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
|
|
1624
|
-
* @returns Promise with backup metadata including share locations and IDs
|
|
1625
|
-
*/ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, backupToGoogleDrive = false }) {
|
|
1868
|
+
async backupSharesWithDistribution({ accountAddress, password, signedSessionId, distribution, preserveDelegatedLocation = false }) {
|
|
1626
1869
|
const dynamicRequestId = uuid.v4();
|
|
1627
1870
|
try {
|
|
1628
|
-
var _this_walletMap_accountAddress
|
|
1629
|
-
const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
|
|
1630
|
-
accountAddress
|
|
1631
|
-
});
|
|
1871
|
+
var _this_walletMap_accountAddress;
|
|
1632
1872
|
if (!((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.walletId)) {
|
|
1633
1873
|
const error = new Error(`WalletId not found for accountAddress ${accountAddress}`);
|
|
1634
1874
|
logError({
|
|
1635
|
-
message: 'Error in
|
|
1875
|
+
message: 'Error in backupSharesWithDistribution, wallet or walletId not found from the wallet map',
|
|
1636
1876
|
error,
|
|
1637
1877
|
context: {
|
|
1638
1878
|
accountAddress,
|
|
@@ -1641,55 +1881,73 @@ class DynamicWalletClient {
|
|
|
1641
1881
|
});
|
|
1642
1882
|
throw error;
|
|
1643
1883
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
}
|
|
1665
|
-
const data = await this.apiClient.storeEncryptedBackupByWallet({
|
|
1666
|
-
walletId: this.walletMap[accountAddress].walletId,
|
|
1667
|
-
encryptedKeyShares: dynamicClientKeyShares,
|
|
1668
|
-
passwordEncrypted: Boolean(password) && password !== this.environmentId,
|
|
1669
|
-
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
1670
|
-
signedSessionId,
|
|
1671
|
-
authMode: this.authMode,
|
|
1672
|
-
requiresSignedSessionId: this.requiresSignedSessionId(),
|
|
1673
|
-
dynamicRequestId
|
|
1674
|
-
});
|
|
1675
|
-
if (data.keyShareIds.length === 0) {
|
|
1676
|
-
throw new Error('No key shares were backed up');
|
|
1677
|
-
}
|
|
1678
|
-
const locations = [
|
|
1679
|
-
{
|
|
1884
|
+
const locations = [];
|
|
1885
|
+
if (distribution.dynamicBackendShares.length > 0) {
|
|
1886
|
+
const encryptedDynamicShares = await Promise.all(distribution.dynamicBackendShares.map((keyShare)=>this.encryptKeyShare({
|
|
1887
|
+
keyShare,
|
|
1888
|
+
password
|
|
1889
|
+
})));
|
|
1890
|
+
const data = await this.apiClient.storeEncryptedBackupByWallet({
|
|
1891
|
+
walletId: this.walletMap[accountAddress].walletId,
|
|
1892
|
+
encryptedKeyShares: encryptedDynamicShares,
|
|
1893
|
+
passwordEncrypted: Boolean(password) && password !== this.environmentId,
|
|
1894
|
+
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
1895
|
+
signedSessionId,
|
|
1896
|
+
authMode: this.authMode,
|
|
1897
|
+
requiresSignedSessionId: this.requiresSignedSessionId(),
|
|
1898
|
+
dynamicRequestId
|
|
1899
|
+
});
|
|
1900
|
+
if (data.keyShareIds.length === 0) {
|
|
1901
|
+
throw new Error('No key shares were backed up to Dynamic backend');
|
|
1902
|
+
}
|
|
1903
|
+
locations.push({
|
|
1680
1904
|
location: core.BackupLocation.DYNAMIC,
|
|
1681
1905
|
externalKeyShareId: data.keyShareIds[0]
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
if (
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
if (distribution.googleDriveShares.length > 0) {
|
|
1909
|
+
const encryptedGoogleDriveShares = await Promise.all(distribution.googleDriveShares.map((keyShare)=>this.encryptKeyShare({
|
|
1910
|
+
keyShare,
|
|
1911
|
+
password
|
|
1912
|
+
})));
|
|
1685
1913
|
await this.uploadKeySharesToGoogleDrive({
|
|
1686
1914
|
accountAddress,
|
|
1687
|
-
encryptedKeyShares:
|
|
1915
|
+
encryptedKeyShares: encryptedGoogleDriveShares
|
|
1688
1916
|
});
|
|
1689
1917
|
locations.push({
|
|
1690
1918
|
location: core.BackupLocation.GOOGLE_DRIVE
|
|
1691
1919
|
});
|
|
1692
1920
|
}
|
|
1921
|
+
if (distribution.delegatedShare) {
|
|
1922
|
+
var _publicKey_key, _publicKey_key1, _publicKey_key2;
|
|
1923
|
+
const publicKey = await this.apiClient.getDelegatedEncryptionKey({
|
|
1924
|
+
environmentId: this.environmentId
|
|
1925
|
+
});
|
|
1926
|
+
if (!(publicKey == null ? void 0 : (_publicKey_key = publicKey.key) == null ? void 0 : _publicKey_key.publicKeyPemB64)) {
|
|
1927
|
+
throw new Error('Public key not found');
|
|
1928
|
+
}
|
|
1929
|
+
var _publicKey_key_keyId;
|
|
1930
|
+
const encryptedDelegatedKeyShareEnvelope = await encryptDelegatedKeyShare(JSON.stringify(distribution.delegatedShare), publicKey == null ? void 0 : (_publicKey_key1 = publicKey.key) == null ? void 0 : _publicKey_key1.publicKeyPemB64, (_publicKey_key_keyId = publicKey == null ? void 0 : (_publicKey_key2 = publicKey.key) == null ? void 0 : _publicKey_key2.keyId) != null ? _publicKey_key_keyId : publicKey == null ? void 0 : publicKey.keyId);
|
|
1931
|
+
const { status } = await this.apiClient.publishDelegatedKeyShare({
|
|
1932
|
+
walletId: this.walletMap[accountAddress].walletId,
|
|
1933
|
+
encryptedKeyShare: encryptedDelegatedKeyShareEnvelope,
|
|
1934
|
+
signedSessionId,
|
|
1935
|
+
requiresSignedSessionId: this.requiresSignedSessionId(),
|
|
1936
|
+
dynamicRequestId
|
|
1937
|
+
});
|
|
1938
|
+
if (status !== 200) {
|
|
1939
|
+
throw new Error('Failed to publish delegated key share');
|
|
1940
|
+
}
|
|
1941
|
+
locations.push({
|
|
1942
|
+
location: core.BackupLocation.DELEGATED
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
// Preserve existing delegated location without re-publishing
|
|
1946
|
+
if (preserveDelegatedLocation && !distribution.delegatedShare) {
|
|
1947
|
+
locations.push({
|
|
1948
|
+
location: core.BackupLocation.DELEGATED
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1693
1951
|
const backupData = await this.apiClient.markKeySharesAsBackedUp({
|
|
1694
1952
|
walletId: this.walletMap[accountAddress].walletId,
|
|
1695
1953
|
locations,
|
|
@@ -1711,10 +1969,10 @@ class DynamicWalletClient {
|
|
|
1711
1969
|
clientKeySharesBackupInfo: updatedBackupInfo
|
|
1712
1970
|
});
|
|
1713
1971
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
1714
|
-
return
|
|
1972
|
+
return backupData;
|
|
1715
1973
|
} catch (error) {
|
|
1716
1974
|
logError({
|
|
1717
|
-
message: 'Error in
|
|
1975
|
+
message: 'Error in backupSharesWithDistribution',
|
|
1718
1976
|
error: error,
|
|
1719
1977
|
context: {
|
|
1720
1978
|
accountAddress,
|
|
@@ -1724,109 +1982,87 @@ class DynamicWalletClient {
|
|
|
1724
1982
|
throw error;
|
|
1725
1983
|
}
|
|
1726
1984
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
});
|
|
1792
|
-
data.keyShares.push({
|
|
1793
|
-
backupLocation: core.BackupLocation.EXTERNAL,
|
|
1794
|
-
id: delegatedKeyShareIds
|
|
1795
|
-
});
|
|
1796
|
-
//todo: combine with user share service backup once other branch is merged
|
|
1797
|
-
await this.apiClient.markKeySharesAsBackedUp({
|
|
1798
|
-
walletId: this.walletMap[accountAddress].walletId,
|
|
1799
|
-
locations: [
|
|
1800
|
-
{
|
|
1801
|
-
location: core.BackupLocation.EXTERNAL
|
|
1802
|
-
}
|
|
1803
|
-
],
|
|
1804
|
-
dynamicRequestId
|
|
1805
|
-
});
|
|
1806
|
-
}
|
|
1807
|
-
const updatedBackupInfo = getClientKeyShareBackupInfo({
|
|
1808
|
-
walletProperties: {
|
|
1809
|
-
derivationPath: this.walletMap[accountAddress].derivationPath,
|
|
1810
|
-
keyShares: data.keyShares,
|
|
1811
|
-
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
|
|
1812
|
-
}
|
|
1813
|
-
});
|
|
1814
|
-
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
1815
|
-
clientKeySharesBackupInfo: updatedBackupInfo
|
|
1985
|
+
/**
|
|
1986
|
+
* Central backup orchestrator that encrypts and stores wallet key shares.
|
|
1987
|
+
*
|
|
1988
|
+
* This method serves as the main backup coordinator, handling the distribution of encrypted
|
|
1989
|
+
* key shares between Dynamic's backend and Google Drive based on the wallet's threshold scheme.
|
|
1990
|
+
* It is used by multiple operations including reshare, refresh, and manual backup requests.
|
|
1991
|
+
*
|
|
1992
|
+
* **Backup Distribution Strategy:**
|
|
1993
|
+
* - **Single share wallets**: All shares stored on Dynamic's backend only
|
|
1994
|
+
* - **Multi-share wallets (2+)**: When backing up to Google Drive, N-1 shares on Dynamic's backend, 1 share on Google Drive
|
|
1995
|
+
* - **Multi-share wallets (2+)**: When not backing up to Google Drive, all shares on Dynamic's backend
|
|
1996
|
+
*
|
|
1997
|
+
* **Process Flow:**
|
|
1998
|
+
* 1. Encrypts all client key shares with the provided password (or environment ID if no password)
|
|
1999
|
+
* 2. For multi-share wallets (2+): conditionally distributes N-1 to backend, 1 to Google Drive
|
|
2000
|
+
* 3. For other configurations: stores all shares on Dynamic's backend
|
|
2001
|
+
* 4. Updates backup metadata and synchronizes wallet state
|
|
2002
|
+
* 5. Persists the updated wallet map to local storage
|
|
2003
|
+
*
|
|
2004
|
+
* **Delegated Key Shares:**
|
|
2005
|
+
* - When delegatedKeyshare is provided, the method will not store the delegated key share but it will mark the delegated share as backed up on Dynamic's backend
|
|
2006
|
+
* - and encrypt the delegated key share to publish it to the webhook
|
|
2007
|
+
*
|
|
2008
|
+
* @param params - The backup operation parameters
|
|
2009
|
+
* @param params.accountAddress - The account address of the wallet to backup
|
|
2010
|
+
* @param params.clientKeyShares - Optional specific key shares to backup (uses localStorage if not provided)
|
|
2011
|
+
* @param params.password - Optional password for encryption (uses environment ID if not provided)
|
|
2012
|
+
* @param params.signedSessionId - Optional signed session ID for authentication
|
|
2013
|
+
* @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
|
|
2014
|
+
* @returns Promise with backup metadata including share locations and IDs
|
|
2015
|
+
*/ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, backupToGoogleDrive = false, delegatedKeyshare = undefined }) {
|
|
2016
|
+
var _this_walletMap_accountAddress, _this_walletMap_accountAddress1;
|
|
2017
|
+
const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromLocalStorage({
|
|
2018
|
+
accountAddress
|
|
2019
|
+
});
|
|
2020
|
+
const shouldBackupToGoogleDrive = backupToGoogleDrive || hasGoogleDriveBackup((_this_walletMap_accountAddress = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress.clientKeySharesBackupInfo);
|
|
2021
|
+
const hasExistingDelegation = hasDelegatedBackup((_this_walletMap_accountAddress1 = this.walletMap[accountAddress]) == null ? void 0 : _this_walletMap_accountAddress1.clientKeySharesBackupInfo);
|
|
2022
|
+
let distribution;
|
|
2023
|
+
let preserveDelegatedLocation = false;
|
|
2024
|
+
if (delegatedKeyshare && shouldBackupToGoogleDrive) {
|
|
2025
|
+
// NEW delegation + Google Drive: Client's shares back up to both Dynamic and Google Drive.
|
|
2026
|
+
// The delegated share goes to the webhook.
|
|
2027
|
+
distribution = createDelegationWithGoogleDriveDistribution({
|
|
2028
|
+
existingShares: keySharesToBackup,
|
|
2029
|
+
delegatedShare: delegatedKeyshare
|
|
2030
|
+
});
|
|
2031
|
+
} else if (delegatedKeyshare) {
|
|
2032
|
+
// NEW delegation only: Client's shares back up to Dynamic.
|
|
2033
|
+
// The delegated share goes to the webhook. No Google Drive backup.
|
|
2034
|
+
distribution = createDelegationOnlyDistribution({
|
|
2035
|
+
existingShares: keySharesToBackup,
|
|
2036
|
+
delegatedShare: delegatedKeyshare
|
|
2037
|
+
});
|
|
2038
|
+
} else if (hasExistingDelegation && shouldBackupToGoogleDrive) {
|
|
2039
|
+
// ADD Google Drive to EXISTING delegation: Client's share backs up to both Dynamic and GD.
|
|
2040
|
+
// Don't re-publish delegated share, just preserve the location.
|
|
2041
|
+
distribution = createAddGoogleDriveToExistingDelegationDistribution({
|
|
2042
|
+
clientShares: keySharesToBackup
|
|
2043
|
+
});
|
|
2044
|
+
preserveDelegatedLocation = true;
|
|
2045
|
+
} else if (shouldBackupToGoogleDrive && keySharesToBackup.length >= 2) {
|
|
2046
|
+
// Google Drive only (no delegation): Split shares between Dynamic (N-1) and Google Drive (1).
|
|
2047
|
+
distribution = createGoogleDriveOnlyDistribution({
|
|
2048
|
+
allShares: keySharesToBackup
|
|
1816
2049
|
});
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
message: 'Error in storeEncryptedBackupByDelegatedWallet',
|
|
1822
|
-
error: error,
|
|
1823
|
-
context: {
|
|
1824
|
-
accountAddress,
|
|
1825
|
-
dynamicRequestId
|
|
1826
|
-
}
|
|
2050
|
+
} else {
|
|
2051
|
+
// No delegation, no Google Drive: All shares go to Dynamic backend only.
|
|
2052
|
+
distribution = createDynamicOnlyDistribution({
|
|
2053
|
+
allShares: keySharesToBackup
|
|
1827
2054
|
});
|
|
1828
|
-
throw error;
|
|
1829
2055
|
}
|
|
2056
|
+
const backupData = await this.backupSharesWithDistribution({
|
|
2057
|
+
accountAddress,
|
|
2058
|
+
password,
|
|
2059
|
+
signedSessionId,
|
|
2060
|
+
distribution,
|
|
2061
|
+
preserveDelegatedLocation
|
|
2062
|
+
});
|
|
2063
|
+
return _extends({}, backupData, {
|
|
2064
|
+
keyShareIds: backupData.locationsWithKeyShares.map((ks)=>ks.keyShareId)
|
|
2065
|
+
});
|
|
1830
2066
|
}
|
|
1831
2067
|
async storeEncryptedBackupByWalletWithRetry({ accountAddress, clientKeyShares, password, signedSessionId }) {
|
|
1832
2068
|
await retryPromise(()=>this.storeEncryptedBackupByWallet({
|
|
@@ -2017,14 +2253,18 @@ class DynamicWalletClient {
|
|
|
2017
2253
|
return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
|
|
2018
2254
|
});
|
|
2019
2255
|
} else {
|
|
2020
|
-
|
|
2021
|
-
const data = await this.storeEncryptedBackupByWallet({
|
|
2256
|
+
await this.storeEncryptedBackupByWallet({
|
|
2022
2257
|
accountAddress,
|
|
2023
2258
|
password,
|
|
2024
2259
|
signedSessionId,
|
|
2025
2260
|
backupToGoogleDrive: true
|
|
2026
2261
|
});
|
|
2027
|
-
|
|
2262
|
+
const backupInfo = this.walletMap[accountAddress].clientKeySharesBackupInfo;
|
|
2263
|
+
const googleDriveShares = backupInfo.backups[core.BackupLocation.GOOGLE_DRIVE] || [];
|
|
2264
|
+
return googleDriveShares.map((ks)=>{
|
|
2265
|
+
var _ks_externalKeyShareId;
|
|
2266
|
+
return (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : '';
|
|
2267
|
+
});
|
|
2028
2268
|
}
|
|
2029
2269
|
} catch (error) {
|
|
2030
2270
|
logError({
|
|
@@ -2060,7 +2300,8 @@ class DynamicWalletClient {
|
|
|
2060
2300
|
const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
|
|
2061
2301
|
const fileName = getClientKeyShareExportFileName({
|
|
2062
2302
|
thresholdSignatureScheme,
|
|
2063
|
-
accountAddress
|
|
2303
|
+
accountAddress,
|
|
2304
|
+
isGoogleDrive: true
|
|
2064
2305
|
});
|
|
2065
2306
|
const backupData = createBackupData({
|
|
2066
2307
|
encryptedKeyShares,
|
|
@@ -2085,7 +2326,7 @@ class DynamicWalletClient {
|
|
|
2085
2326
|
throw error;
|
|
2086
2327
|
}
|
|
2087
2328
|
}
|
|
2088
|
-
async
|
|
2329
|
+
async exportClientKeysharesFromGoogleDrive({ accountAddress, password, signedSessionId }) {
|
|
2089
2330
|
try {
|
|
2090
2331
|
await this.getWallet({
|
|
2091
2332
|
accountAddress,
|
|
@@ -2096,15 +2337,16 @@ class DynamicWalletClient {
|
|
|
2096
2337
|
oauthAccountId
|
|
2097
2338
|
});
|
|
2098
2339
|
const thresholdSignatureScheme = this.walletMap[accountAddress].thresholdSignatureScheme;
|
|
2099
|
-
const
|
|
2340
|
+
const backupFileName = getClientKeyShareExportFileName({
|
|
2100
2341
|
thresholdSignatureScheme,
|
|
2101
|
-
accountAddress
|
|
2342
|
+
accountAddress,
|
|
2343
|
+
isGoogleDrive: true
|
|
2102
2344
|
});
|
|
2103
2345
|
let backupData = null;
|
|
2104
2346
|
try {
|
|
2105
2347
|
backupData = await retryPromise(()=>downloadFileFromGoogleDrive({
|
|
2106
2348
|
accessToken,
|
|
2107
|
-
fileName
|
|
2349
|
+
fileName: backupFileName
|
|
2108
2350
|
}));
|
|
2109
2351
|
} catch (error) {
|
|
2110
2352
|
logError({
|
|
@@ -2112,7 +2354,7 @@ class DynamicWalletClient {
|
|
|
2112
2354
|
error: error,
|
|
2113
2355
|
context: {
|
|
2114
2356
|
accountAddress,
|
|
2115
|
-
fileName
|
|
2357
|
+
fileName: backupFileName
|
|
2116
2358
|
}
|
|
2117
2359
|
});
|
|
2118
2360
|
throw error;
|
|
@@ -2124,7 +2366,7 @@ class DynamicWalletClient {
|
|
|
2124
2366
|
error: new Error('No backup file found'),
|
|
2125
2367
|
context: {
|
|
2126
2368
|
accountAddress,
|
|
2127
|
-
fileName
|
|
2369
|
+
fileName: backupFileName
|
|
2128
2370
|
}
|
|
2129
2371
|
});
|
|
2130
2372
|
throw error;
|
|
@@ -2137,7 +2379,7 @@ class DynamicWalletClient {
|
|
|
2137
2379
|
error,
|
|
2138
2380
|
context: {
|
|
2139
2381
|
accountAddress,
|
|
2140
|
-
fileName
|
|
2382
|
+
fileName: backupFileName
|
|
2141
2383
|
}
|
|
2142
2384
|
});
|
|
2143
2385
|
throw error;
|
|
@@ -2147,15 +2389,20 @@ class DynamicWalletClient {
|
|
|
2147
2389
|
keyShare,
|
|
2148
2390
|
password
|
|
2149
2391
|
})));
|
|
2150
|
-
|
|
2392
|
+
const text = JSON.stringify(decryptedKeyShares, null, 2);
|
|
2393
|
+
const exportFileName = getClientKeyShareExportFileName({
|
|
2394
|
+
thresholdSignatureScheme,
|
|
2151
2395
|
accountAddress,
|
|
2152
|
-
|
|
2153
|
-
|
|
2396
|
+
isGoogleDrive: true
|
|
2397
|
+
});
|
|
2398
|
+
downloadStringAsFile({
|
|
2399
|
+
filename: exportFileName,
|
|
2400
|
+
content: text,
|
|
2401
|
+
mimeType: 'application/json'
|
|
2154
2402
|
});
|
|
2155
|
-
return decryptedKeyShares;
|
|
2156
2403
|
} catch (error) {
|
|
2157
2404
|
logError({
|
|
2158
|
-
message: 'Error in
|
|
2405
|
+
message: 'Error in exportClientKeysharesFromGoogleDrive',
|
|
2159
2406
|
error: error,
|
|
2160
2407
|
context: {
|
|
2161
2408
|
accountAddress
|
|
@@ -2182,16 +2429,11 @@ class DynamicWalletClient {
|
|
|
2182
2429
|
keyShares: clientKeyShares,
|
|
2183
2430
|
derivationPath
|
|
2184
2431
|
});
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2432
|
+
downloadStringAsFile({
|
|
2433
|
+
filename: `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${accountAddress}.txt`,
|
|
2434
|
+
content: text,
|
|
2435
|
+
mimeType: 'text/plain'
|
|
2189
2436
|
});
|
|
2190
|
-
const url = URL.createObjectURL(blob);
|
|
2191
|
-
const a = document.createElement('a');
|
|
2192
|
-
a.href = url;
|
|
2193
|
-
a.download = `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${accountAddress}.txt`;
|
|
2194
|
-
a.click();
|
|
2195
2437
|
}
|
|
2196
2438
|
async getClientKeyShares({ accountAddress, password, signedSessionId }) {
|
|
2197
2439
|
await this.getWallet({
|
|
@@ -2491,29 +2733,115 @@ class DynamicWalletClient {
|
|
|
2491
2733
|
}
|
|
2492
2734
|
this.apiClient.syncAuthToken(authToken);
|
|
2493
2735
|
}
|
|
2736
|
+
async createRooms({ roomType, thresholdSignatureScheme, roomCount = 5 }) {
|
|
2737
|
+
try {
|
|
2738
|
+
var _rooms_roomIds;
|
|
2739
|
+
if (DynamicWalletClient.roomsInitializing) {
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
DynamicWalletClient.roomsInitializing = true;
|
|
2743
|
+
const restoredRooms = await this.restoreRooms();
|
|
2744
|
+
if (restoredRooms && restoredRooms.length > 0) {
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
const rooms = await this.apiClient.createRoomsWithoutWalletId({
|
|
2748
|
+
roomType,
|
|
2749
|
+
roomCount,
|
|
2750
|
+
thresholdSignatureScheme
|
|
2751
|
+
});
|
|
2752
|
+
const newRooms = rooms == null ? void 0 : (_rooms_roomIds = rooms.roomIds) == null ? void 0 : _rooms_roomIds.map((roomId)=>({
|
|
2753
|
+
roomId,
|
|
2754
|
+
createdAt: Date.now(),
|
|
2755
|
+
expiresAt: Date.now() + 1000 * 60 * 10
|
|
2756
|
+
}));
|
|
2757
|
+
DynamicWalletClient.rooms = [
|
|
2758
|
+
...newRooms,
|
|
2759
|
+
...DynamicWalletClient.rooms
|
|
2760
|
+
];
|
|
2761
|
+
DynamicWalletClient.roomsInitializing = false;
|
|
2762
|
+
await this.storage.setItem(`${this.storageKey}-rooms`, JSON.stringify(DynamicWalletClient.rooms));
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
this.logger.error('Error creating rooms', error);
|
|
2765
|
+
throw error;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
async restoreRooms() {
|
|
2769
|
+
const roomData = await this.storage.getItem(`${this.storageKey}-rooms`);
|
|
2770
|
+
if (!roomData) {
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
const parsedRoomData = JSON.parse(roomData);
|
|
2774
|
+
const now = Date.now();
|
|
2775
|
+
// Filter out expired rooms
|
|
2776
|
+
const rooms = parsedRoomData.filter((room)=>room.expiresAt > now);
|
|
2777
|
+
if (rooms.length === 0) {
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
DynamicWalletClient.rooms = rooms;
|
|
2781
|
+
return rooms;
|
|
2782
|
+
}
|
|
2783
|
+
async getRooms() {
|
|
2784
|
+
return DynamicWalletClient.rooms;
|
|
2785
|
+
}
|
|
2786
|
+
async getRoom() {
|
|
2787
|
+
const now = Date.now();
|
|
2788
|
+
// Filter out expired rooms
|
|
2789
|
+
DynamicWalletClient.rooms = DynamicWalletClient.rooms.filter((room)=>room.expiresAt > now);
|
|
2790
|
+
if (DynamicWalletClient.rooms.length === 0) {
|
|
2791
|
+
// create rooms if no valid rooms are cached,
|
|
2792
|
+
// dont await the creation of rooms, the room will be created on the server
|
|
2793
|
+
this.createRooms({
|
|
2794
|
+
roomType: sdkApiCore.RoomTypeEnum.Threshold,
|
|
2795
|
+
thresholdSignatureScheme: core.ThresholdSignatureScheme.TWO_OF_TWO,
|
|
2796
|
+
roomCount: ROOM_CACHE_COUNT
|
|
2797
|
+
});
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
// Return the first non-expired room (and remove it from the array)
|
|
2801
|
+
return DynamicWalletClient.rooms.shift();
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Helper method to instrument with automatic properties inclusion
|
|
2805
|
+
*/ instrument(message, context) {
|
|
2806
|
+
const defaultContext = {
|
|
2807
|
+
environmentId: context.environmentId || this.environmentId
|
|
2808
|
+
};
|
|
2809
|
+
this.logger.debug(message, _extends({}, defaultContext, context));
|
|
2810
|
+
this.logger.instrument(message, _extends({}, defaultContext, context));
|
|
2811
|
+
}
|
|
2812
|
+
getTraceContext(traceContext) {
|
|
2813
|
+
const now = Date.now();
|
|
2814
|
+
return _extends({
|
|
2815
|
+
now,
|
|
2816
|
+
time: (traceContext == null ? void 0 : traceContext.startTime) ? now - traceContext.startTime : 0
|
|
2817
|
+
}, traceContext);
|
|
2818
|
+
}
|
|
2494
2819
|
constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = core.AuthMode.HEADER, authToken = undefined, // Represents the version of the client SDK used by developer
|
|
2495
|
-
sdkVersion }){
|
|
2820
|
+
sdkVersion, forwardMPCClient, baseClientKeysharesRelayApiUrl }){
|
|
2496
2821
|
this.userId = undefined;
|
|
2497
2822
|
this.sessionId = undefined;
|
|
2498
|
-
this.delegatedAccessEndpoint = undefined;
|
|
2499
2823
|
this.initializePromise = null;
|
|
2500
2824
|
this.logger = logger;
|
|
2501
2825
|
this.walletMap = {} // todo: store in session storage
|
|
2502
2826
|
;
|
|
2503
2827
|
this.memoryStorage = null;
|
|
2504
2828
|
this.iframe = null;
|
|
2829
|
+
this.forwardMPCEnabled = false;
|
|
2505
2830
|
this.featureFlags = {};
|
|
2506
2831
|
this.environmentId = environmentId;
|
|
2507
2832
|
this.storageKey = `${STORAGE_KEY}-${storageKey != null ? storageKey : environmentId}`;
|
|
2508
2833
|
this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
|
|
2509
2834
|
this.authMode = authMode;
|
|
2510
2835
|
this.sdkVersion = sdkVersion;
|
|
2836
|
+
this.baseClientKeysharesRelayApiUrl = baseClientKeysharesRelayApiUrl;
|
|
2511
2837
|
this.apiClient = new core.DynamicApiClient({
|
|
2512
2838
|
environmentId,
|
|
2513
2839
|
authToken,
|
|
2514
2840
|
baseApiUrl,
|
|
2515
2841
|
authMode,
|
|
2516
|
-
sdkVersion
|
|
2842
|
+
sdkVersion,
|
|
2843
|
+
forwardMPCClient,
|
|
2844
|
+
baseClientKeysharesRelayApiUrl
|
|
2517
2845
|
});
|
|
2518
2846
|
this.debug = Boolean(debug);
|
|
2519
2847
|
this.logger.setLogLevel(this.debug ? logger$1.LogLevel.DEBUG : DEFAULT_LOG_LEVEL);
|
|
@@ -2533,8 +2861,15 @@ class DynamicWalletClient {
|
|
|
2533
2861
|
if (authMode === core.AuthMode.HEADER && authToken) {
|
|
2534
2862
|
this.initLoggerContext(authToken);
|
|
2535
2863
|
}
|
|
2864
|
+
this.forwardMPCEnabled = this.featureFlags && this.featureFlags[core.FEATURE_FLAGS.ENABLE_FORWARD_MPC_CLIENT_FLAG] === true;
|
|
2865
|
+
// if forwardMPCEnabled is true and forwardMPCClient is not provided, initialize the forwardMPCClient
|
|
2866
|
+
if (this.forwardMPCEnabled && !forwardMPCClient) {
|
|
2867
|
+
this.initializeForwardMPCClient();
|
|
2868
|
+
}
|
|
2536
2869
|
}
|
|
2537
2870
|
}
|
|
2871
|
+
DynamicWalletClient.rooms = [];
|
|
2872
|
+
DynamicWalletClient.roomsInitializing = false;
|
|
2538
2873
|
|
|
2539
2874
|
const ERROR_KEYGEN_FAILED = '[DynamicWaasWalletClient]: Error with keygen';
|
|
2540
2875
|
const ERROR_CREATE_WALLET_ACCOUNT = '[DynamicWaasWalletClient]: Error creating wallet account';
|
|
@@ -2608,7 +2943,13 @@ exports.ERROR_SIGN_MESSAGE = ERROR_SIGN_MESSAGE;
|
|
|
2608
2943
|
exports.ERROR_SIGN_TYPED_DATA = ERROR_SIGN_TYPED_DATA;
|
|
2609
2944
|
exports.ERROR_VERIFY_MESSAGE_SIGNATURE = ERROR_VERIFY_MESSAGE_SIGNATURE;
|
|
2610
2945
|
exports.ERROR_VERIFY_TRANSACTION_SIGNATURE = ERROR_VERIFY_TRANSACTION_SIGNATURE;
|
|
2946
|
+
exports.createAddGoogleDriveToExistingDelegationDistribution = createAddGoogleDriveToExistingDelegationDistribution;
|
|
2611
2947
|
exports.createBackupData = createBackupData;
|
|
2948
|
+
exports.createDelegationOnlyDistribution = createDelegationOnlyDistribution;
|
|
2949
|
+
exports.createDelegationWithGoogleDriveDistribution = createDelegationWithGoogleDriveDistribution;
|
|
2950
|
+
exports.createDynamicOnlyDistribution = createDynamicOnlyDistribution;
|
|
2951
|
+
exports.createGoogleDriveOnlyDistribution = createGoogleDriveOnlyDistribution;
|
|
2952
|
+
exports.downloadStringAsFile = downloadStringAsFile;
|
|
2612
2953
|
exports.formatEvmMessage = formatEvmMessage;
|
|
2613
2954
|
exports.formatMessage = formatMessage;
|
|
2614
2955
|
exports.getClientKeyShareBackupInfo = getClientKeyShareBackupInfo;
|
|
@@ -2616,6 +2957,8 @@ exports.getClientKeyShareExportFileName = getClientKeyShareExportFileName;
|
|
|
2616
2957
|
exports.getGoogleOAuthAccountId = getGoogleOAuthAccountId;
|
|
2617
2958
|
exports.getMPCSignatureScheme = getMPCSignatureScheme;
|
|
2618
2959
|
exports.getMPCSigner = getMPCSigner;
|
|
2960
|
+
exports.hasDelegatedBackup = hasDelegatedBackup;
|
|
2961
|
+
exports.hasGoogleDriveBackup = hasGoogleDriveBackup;
|
|
2619
2962
|
exports.isBrowser = isBrowser;
|
|
2620
2963
|
exports.isHexString = isHexString;
|
|
2621
2964
|
exports.mergeUniqueKeyShares = mergeUniqueKeyShares;
|