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