@dynamic-labs-wallet/node 0.0.0-preview.114.4 → 0.0.1-paolo-1
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 +790 -233
- package/index.esm.js +778 -195
- package/internal/core/bip340.cjs +1 -0
- package/internal/core/common.cjs +1 -0
- package/internal/core/ecdsa.cjs +1 -0
- package/internal/core/ecdsa.d.ts +1 -1
- package/internal/core/ed25519.cjs +1 -0
- package/internal/core/ed25519_exportable.cjs +1 -0
- package/internal/core/ed25519_exportable.d.ts +21 -0
- package/internal/core/index.cjs +1 -0
- package/internal/core/index.d.ts +4 -2
- package/internal/core/native.d.ts +65 -0
- package/internal/core/sr25519.cjs +1 -0
- package/internal/core/sr25519.d.ts +22 -0
- package/internal/core/types.cjs +1 -0
- package/internal/core/types.d.ts +20 -0
- package/internal/node/index.cjs +1 -0
- package/internal/node/index.d.ts +8 -2
- package/internal/node/native/libmpc_executor_linux_arm64_nodejs.node +0 -0
- package/internal/node/native/libmpc_executor_linux_x86_64_nodejs.node +0 -0
- package/internal/node/native/libmpc_executor_macos_arm64_nodejs.node +0 -0
- package/internal/node/native/libmpc_executor_macos_x86_64_nodejs.node +0 -0
- package/internal/node/native.cjs +1 -0
- package/internal/node/native.d.ts +1 -1
- package/package.json +13 -8
- package/src/backup/encryption.d.ts +42 -2
- package/src/backup/encryption.d.ts.map +1 -1
- package/src/backup/utils.d.ts +4 -0
- package/src/backup/utils.d.ts.map +1 -0
- package/src/client.d.ts +85 -30
- package/src/client.d.ts.map +1 -1
- package/src/core/createCore.d.ts +25 -0
- package/src/core/createCore.d.ts.map +1 -0
- package/src/core/types.d.ts +7 -0
- package/src/core/types.d.ts.map +1 -0
- package/src/delegatedClient/createDelegatedClient.d.ts +23 -0
- package/src/delegatedClient/createDelegatedClient.d.ts.map +1 -0
- package/src/delegatedClient/index.d.ts +7 -0
- package/src/delegatedClient/index.d.ts.map +1 -0
- package/src/delegatedClient/modules/decryptWebhookData.d.ts +18 -0
- package/src/delegatedClient/modules/decryptWebhookData.d.ts.map +1 -0
- package/src/delegatedClient/modules/revokeDelegation.d.ts +3 -0
- package/src/delegatedClient/modules/revokeDelegation.d.ts.map +1 -0
- package/src/delegatedClient/modules/sign.d.ts +31 -0
- package/src/delegatedClient/modules/sign.d.ts.map +1 -0
- package/src/index.d.ts +11 -5
- package/src/index.d.ts.map +1 -1
- package/src/mpc/index.d.ts +2 -2
- package/src/mpc/index.d.ts.map +1 -1
- package/src/mpc/mpc.d.ts +3 -4
- package/src/mpc/mpc.d.ts.map +1 -1
- package/src/mpc/types.d.ts +1 -2
- package/src/mpc/types.d.ts.map +1 -1
- package/src/types.d.ts +11 -2
- package/src/types.d.ts.map +1 -1
- package/src/utils.d.ts +5 -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/index.js +0 -1
- package/internal/core/types.js +0 -1
- package/internal/node/index.js +0 -1
- package/internal/node/native.js +0 -1
- /package/internal/core/{native.js → native.cjs} +0 -0
package/index.cjs.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var core$1 = require('#internal/core');
|
|
3
4
|
var core = require('@dynamic-labs-wallet/core');
|
|
4
|
-
var node = require('
|
|
5
|
-
var
|
|
5
|
+
var node = require('#internal/node');
|
|
6
|
+
var uuid = require('uuid');
|
|
6
7
|
var crypto = require('crypto');
|
|
8
|
+
var logger$1 = require('@dynamic-labs/logger');
|
|
9
|
+
require('node:crypto');
|
|
7
10
|
|
|
11
|
+
// Removed duplicate exports - these are already exported from #internal/core
|
|
8
12
|
const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = core.MPC_RELAY_PROD_API_URL })=>{
|
|
9
13
|
switch(signingAlgorithm){
|
|
10
14
|
case core.SigningAlgorithm.ECDSA:
|
|
11
15
|
return new node.Ecdsa(baseRelayUrl);
|
|
12
16
|
case core.SigningAlgorithm.ED25519:
|
|
13
|
-
return new node.
|
|
17
|
+
return new node.ExportableEd25519(baseRelayUrl);
|
|
14
18
|
case core.SigningAlgorithm.BIP340:
|
|
15
19
|
return new node.BIP340(baseRelayUrl);
|
|
16
20
|
default:
|
|
@@ -65,7 +69,8 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
|
|
|
65
69
|
[core.BackupLocation.GOOGLE_DRIVE]: [],
|
|
66
70
|
[core.BackupLocation.ICLOUD]: [],
|
|
67
71
|
[core.BackupLocation.USER]: [],
|
|
68
|
-
[core.BackupLocation.EXTERNAL]: []
|
|
72
|
+
[core.BackupLocation.EXTERNAL]: [],
|
|
73
|
+
[core.BackupLocation.DELEGATED]: []
|
|
69
74
|
};
|
|
70
75
|
if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
|
|
71
76
|
return {
|
|
@@ -75,7 +80,11 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
|
|
|
75
80
|
}
|
|
76
81
|
params.walletProperties.keyShares.forEach((keyShare)=>{
|
|
77
82
|
if (backups[keyShare.backupLocation]) {
|
|
78
|
-
backups[keyShare.backupLocation].push(
|
|
83
|
+
backups[keyShare.backupLocation].push({
|
|
84
|
+
location: keyShare.backupLocation,
|
|
85
|
+
keyShareId: keyShare.id,
|
|
86
|
+
passwordEncrypted: keyShare.passwordEncrypted
|
|
87
|
+
});
|
|
79
88
|
}
|
|
80
89
|
});
|
|
81
90
|
const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
|
|
@@ -125,13 +134,64 @@ const getExternalServerKeyShareBackupInfo = (params)=>{
|
|
|
125
134
|
// TypeScript needs this even though it's unreachable
|
|
126
135
|
throw new Error('Unreachable code');
|
|
127
136
|
}
|
|
137
|
+
const formatEvmMessage = (message)=>{
|
|
138
|
+
if (typeof message === 'string' && message.startsWith('0x')) {
|
|
139
|
+
const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
|
|
140
|
+
return node.MessageHash.keccak256(serializedTxBytes);
|
|
141
|
+
}
|
|
142
|
+
return node.MessageHash.keccak256(message);
|
|
143
|
+
};
|
|
144
|
+
const formatSolanaMessage = (message)=>{
|
|
145
|
+
if (typeof message === 'string') {
|
|
146
|
+
if (!isHexString(message)) {
|
|
147
|
+
return Buffer.from(message).toString('hex');
|
|
148
|
+
} else {
|
|
149
|
+
return new Uint8Array(Buffer.from(message, 'hex'));
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
return message;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const formatMessage = (chainName, message)=>{
|
|
156
|
+
switch(chainName){
|
|
157
|
+
case 'EVM':
|
|
158
|
+
return formatEvmMessage(message);
|
|
159
|
+
case 'SVM':
|
|
160
|
+
return formatSolanaMessage(message);
|
|
161
|
+
case 'SUI':
|
|
162
|
+
return message;
|
|
163
|
+
default:
|
|
164
|
+
throw new Error('Unsupported chain name');
|
|
165
|
+
}
|
|
166
|
+
};
|
|
128
167
|
|
|
168
|
+
const ENCRYPTION_VERSION_LEGACY = 'v1';
|
|
169
|
+
const ENCRYPTION_VERSION_CURRENT = 'v2';
|
|
129
170
|
const PBKDF2_ALGORITHM = 'PBKDF2';
|
|
130
|
-
const PBKDF2_ITERATIONS = 100000;
|
|
131
171
|
const PBKDF2_HASH_ALGORITHM = 'SHA-256';
|
|
132
172
|
const AES_GCM_ALGORITHM = 'AES-GCM';
|
|
133
173
|
const AES_GCM_LENGTH = 256;
|
|
134
|
-
const
|
|
174
|
+
const ENCRYPTION_VERSIONS = {
|
|
175
|
+
[ENCRYPTION_VERSION_LEGACY]: {
|
|
176
|
+
version: ENCRYPTION_VERSION_LEGACY,
|
|
177
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
178
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
179
|
+
iterations: 100000,
|
|
180
|
+
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
181
|
+
algorithmLength: AES_GCM_LENGTH
|
|
182
|
+
},
|
|
183
|
+
[ENCRYPTION_VERSION_CURRENT]: {
|
|
184
|
+
version: ENCRYPTION_VERSION_CURRENT,
|
|
185
|
+
algorithm: AES_GCM_ALGORITHM,
|
|
186
|
+
keyDerivation: PBKDF2_ALGORITHM,
|
|
187
|
+
iterations: 1000000,
|
|
188
|
+
hashAlgorithm: PBKDF2_HASH_ALGORITHM,
|
|
189
|
+
algorithmLength: AES_GCM_LENGTH
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_CURRENT].iterations;
|
|
193
|
+
ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY].iterations;
|
|
194
|
+
const getKey = async ({ password, salt, encryptionConfig })=>{
|
|
135
195
|
const passwordBytes = stringToBytes(password);
|
|
136
196
|
const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
|
|
137
197
|
name: 'PBKDF2'
|
|
@@ -139,26 +199,34 @@ const getKey = async ({ password, salt })=>{
|
|
|
139
199
|
'deriveKey'
|
|
140
200
|
]);
|
|
141
201
|
return crypto.subtle.deriveKey({
|
|
142
|
-
name:
|
|
143
|
-
salt,
|
|
144
|
-
iterations:
|
|
145
|
-
hash:
|
|
202
|
+
name: encryptionConfig.keyDerivation,
|
|
203
|
+
salt: salt,
|
|
204
|
+
iterations: encryptionConfig.iterations,
|
|
205
|
+
hash: encryptionConfig.hashAlgorithm
|
|
146
206
|
}, initialKey, {
|
|
147
|
-
name:
|
|
148
|
-
length:
|
|
207
|
+
name: encryptionConfig.algorithm,
|
|
208
|
+
length: encryptionConfig.algorithmLength
|
|
149
209
|
}, false, [
|
|
150
210
|
'encrypt',
|
|
151
211
|
'decrypt'
|
|
152
212
|
]);
|
|
153
213
|
};
|
|
154
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Encrypts data using the specified encryption version.
|
|
216
|
+
* Always uses the latest encryption configuration for new encryptions by default.
|
|
217
|
+
*/ const encryptData = async ({ data, password, version = ENCRYPTION_VERSION_CURRENT })=>{
|
|
218
|
+
const encryptionConfig = ENCRYPTION_VERSIONS[version];
|
|
219
|
+
if (!encryptionConfig) {
|
|
220
|
+
throw new Error(`Unsupported encryption version: ${version}`);
|
|
221
|
+
}
|
|
155
222
|
try {
|
|
156
223
|
// Generate a random salt and IV
|
|
157
224
|
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
158
225
|
const iv = crypto.getRandomValues(new Uint8Array(12)); // AES-GCM requires 12 bytes
|
|
159
226
|
const key = await getKey({
|
|
160
227
|
password,
|
|
161
|
-
salt
|
|
228
|
+
salt,
|
|
229
|
+
encryptionConfig
|
|
162
230
|
});
|
|
163
231
|
// Convert the input string to bytes
|
|
164
232
|
const dataBytes = new TextEncoder().encode(data);
|
|
@@ -171,25 +239,39 @@ const encryptData = async ({ data, password })=>{
|
|
|
171
239
|
return {
|
|
172
240
|
salt: bytesToBase64(salt),
|
|
173
241
|
iv: bytesToBase64(iv),
|
|
174
|
-
cipher: bytesToBase64(new Uint8Array(encryptedData))
|
|
242
|
+
cipher: bytesToBase64(new Uint8Array(encryptedData)),
|
|
243
|
+
version
|
|
175
244
|
};
|
|
176
|
-
} catch (
|
|
245
|
+
} catch (e) {
|
|
177
246
|
throw new Error('Error encrypting data');
|
|
178
247
|
}
|
|
179
248
|
};
|
|
180
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Decrypts data with version-based configuration.
|
|
251
|
+
* Uses the version field from the data to determine encryption parameters.
|
|
252
|
+
* Falls back to legacy version for backward compatibility if no version is specified.
|
|
253
|
+
*/ const decryptData = async ({ data, password })=>{
|
|
254
|
+
const { salt, iv, cipher, version } = data;
|
|
255
|
+
// Ensure proper base64 padding for all values
|
|
256
|
+
const paddedSalt = ensureBase64Padding(salt);
|
|
257
|
+
const paddedIv = ensureBase64Padding(iv);
|
|
258
|
+
const paddedCipher = ensureBase64Padding(cipher);
|
|
259
|
+
const saltBytes = base64ToBytes(paddedSalt);
|
|
260
|
+
const ivBytes = base64ToBytes(paddedIv);
|
|
261
|
+
const cipherBytes = base64ToBytes(paddedCipher);
|
|
262
|
+
let encryptionConfig;
|
|
263
|
+
// Use version-based configuration if available, otherwise fallback to legacy
|
|
264
|
+
if (version && ENCRYPTION_VERSIONS[version]) {
|
|
265
|
+
encryptionConfig = ENCRYPTION_VERSIONS[version];
|
|
266
|
+
} else {
|
|
267
|
+
// Fallback to legacy version for backward compatibility
|
|
268
|
+
encryptionConfig = ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY];
|
|
269
|
+
}
|
|
181
270
|
try {
|
|
182
|
-
const { salt, iv, cipher } = data;
|
|
183
|
-
// Ensure proper base64 padding for all values
|
|
184
|
-
const paddedSalt = ensureBase64Padding(salt);
|
|
185
|
-
const paddedIv = ensureBase64Padding(iv);
|
|
186
|
-
const paddedCipher = ensureBase64Padding(cipher);
|
|
187
|
-
const saltBytes = base64ToBytes(paddedSalt);
|
|
188
|
-
const ivBytes = base64ToBytes(paddedIv);
|
|
189
|
-
const cipherBytes = base64ToBytes(paddedCipher);
|
|
190
271
|
const key = await getKey({
|
|
191
272
|
password,
|
|
192
|
-
salt: saltBytes
|
|
273
|
+
salt: saltBytes,
|
|
274
|
+
encryptionConfig
|
|
193
275
|
});
|
|
194
276
|
const decryptedData = await crypto.subtle.decrypt({
|
|
195
277
|
name: AES_GCM_ALGORITHM,
|
|
@@ -197,13 +279,23 @@ const decryptData = async ({ data, password })=>{
|
|
|
197
279
|
}, key, cipherBytes);
|
|
198
280
|
return new TextDecoder().decode(decryptedData);
|
|
199
281
|
} catch (error) {
|
|
200
|
-
throw new Error('Decryption failed');
|
|
282
|
+
throw new Error('Decryption failed: ' + error);
|
|
201
283
|
}
|
|
202
284
|
};
|
|
203
285
|
|
|
204
286
|
const DEFAULT_LOG_LEVEL = 'INFO';
|
|
205
287
|
|
|
206
288
|
class DynamicWalletClient {
|
|
289
|
+
async initializeForwardMPCClient() {
|
|
290
|
+
try {
|
|
291
|
+
await this.apiClient.forwardMPCClient.ensureWsConnection();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
this.logger.error('Error connecting to ForwardMPC enclave websocket. Environment: ' + this.environmentId, {
|
|
294
|
+
error,
|
|
295
|
+
environmentId: this.environmentId
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
207
299
|
ensureApiClientAuthenticated() {
|
|
208
300
|
if (!this.isApiClientAuthenticated) {
|
|
209
301
|
throw new Error('Client must be authenticated before making API calls. Call authenticateApiToken first.');
|
|
@@ -227,13 +319,15 @@ class DynamicWalletClient {
|
|
|
227
319
|
});
|
|
228
320
|
this.isApiClientAuthenticated = true;
|
|
229
321
|
}
|
|
230
|
-
async dynamicServerInitializeKeyGen({ chainName, externalServerKeygenIds, thresholdSignatureScheme, onError, onCeremonyComplete }) {
|
|
322
|
+
async dynamicServerInitializeKeyGen({ chainName, externalServerKeygenIds, thresholdSignatureScheme, dynamicRequestId, skipLock, onError, onCeremonyComplete }) {
|
|
231
323
|
this.ensureApiClientAuthenticated();
|
|
232
324
|
try {
|
|
233
325
|
const data = await this.apiClient.createWalletAccount({
|
|
234
326
|
chainName,
|
|
235
327
|
clientKeygenIds: externalServerKeygenIds,
|
|
236
328
|
thresholdSignatureScheme,
|
|
329
|
+
dynamicRequestId,
|
|
330
|
+
skipLock,
|
|
237
331
|
onError,
|
|
238
332
|
onCeremonyComplete
|
|
239
333
|
});
|
|
@@ -267,8 +361,8 @@ class DynamicWalletClient {
|
|
|
267
361
|
let publicKey;
|
|
268
362
|
if (mpcSigner instanceof node.Ecdsa) {
|
|
269
363
|
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
270
|
-
} else if (mpcSigner instanceof node.
|
|
271
|
-
publicKey = await mpcSigner.
|
|
364
|
+
} else if (mpcSigner instanceof node.ExportableEd25519) {
|
|
365
|
+
publicKey = await mpcSigner.getPubkey(keyShare);
|
|
272
366
|
}
|
|
273
367
|
return publicKey;
|
|
274
368
|
} catch (error) {
|
|
@@ -294,7 +388,13 @@ class DynamicWalletClient {
|
|
|
294
388
|
...dynamicServerKeygenIds,
|
|
295
389
|
...otherExternalServerKeygenIds
|
|
296
390
|
];
|
|
297
|
-
|
|
391
|
+
if (!(mpcSigner instanceof node.ExportableEd25519)) {
|
|
392
|
+
return mpcSigner.keygen(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, currentInit, allOtherKeygenIds);
|
|
393
|
+
} else {
|
|
394
|
+
// One party joins the keygen room using acting as the sampler: (wallet-service)
|
|
395
|
+
// The remaining parties join the key sampling ceremony using: (browser)
|
|
396
|
+
return mpcSigner.receiveKey(roomId, mpcConfig.numberOfParties, mpcConfig.threshold, currentInit, allOtherKeygenIds);
|
|
397
|
+
}
|
|
298
398
|
}));
|
|
299
399
|
// only need one client keygen result to derive the public key
|
|
300
400
|
const [serverKeygenResult] = serverKeygenResults;
|
|
@@ -314,7 +414,8 @@ class DynamicWalletClient {
|
|
|
314
414
|
throw new Error('Error deriving public key in externalServerKeyGen');
|
|
315
415
|
}
|
|
316
416
|
}
|
|
317
|
-
async keyGen({ chainName, thresholdSignatureScheme, onError, onCeremonyComplete }) {
|
|
417
|
+
async keyGen({ chainName, thresholdSignatureScheme, skipLock, onError, onCeremonyComplete }) {
|
|
418
|
+
const dynamicRequestId = uuid.v4();
|
|
318
419
|
try {
|
|
319
420
|
const externalServerInitKeygenResults = await this.externalServerInitializeKeyGen({
|
|
320
421
|
chainName,
|
|
@@ -324,7 +425,9 @@ class DynamicWalletClient {
|
|
|
324
425
|
const { roomId, serverKeygenIds: dynamicServerKeygenIds } = await this.dynamicServerInitializeKeyGen({
|
|
325
426
|
chainName,
|
|
326
427
|
externalServerKeygenIds,
|
|
428
|
+
dynamicRequestId,
|
|
327
429
|
thresholdSignatureScheme,
|
|
430
|
+
skipLock,
|
|
328
431
|
onCeremonyComplete
|
|
329
432
|
});
|
|
330
433
|
const { rawPublicKey, externalServerKeyGenResults } = await this.externalServerKeyGen({
|
|
@@ -345,6 +448,7 @@ class DynamicWalletClient {
|
|
|
345
448
|
}
|
|
346
449
|
async importRawPrivateKey({ chainName, privateKey, thresholdSignatureScheme, onError, onCeremonyComplete }) {
|
|
347
450
|
this.ensureApiClientAuthenticated();
|
|
451
|
+
const dynamicRequestId = uuid.v4();
|
|
348
452
|
const mpcSigner = getMPCSigner({
|
|
349
453
|
chainName,
|
|
350
454
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
@@ -358,6 +462,7 @@ class DynamicWalletClient {
|
|
|
358
462
|
chainName,
|
|
359
463
|
clientKeygenIds: externalServerKeygenIds,
|
|
360
464
|
thresholdSignatureScheme,
|
|
465
|
+
dynamicRequestId,
|
|
361
466
|
onError,
|
|
362
467
|
onCeremonyComplete
|
|
363
468
|
});
|
|
@@ -390,7 +495,8 @@ class DynamicWalletClient {
|
|
|
390
495
|
externalServerKeyShares: externalServerKeygenResults
|
|
391
496
|
};
|
|
392
497
|
}
|
|
393
|
-
async dynamicServerSign({ walletId, message }) {
|
|
498
|
+
async dynamicServerSign({ walletId, message, isFormatted, context, onError }) {
|
|
499
|
+
const dynamicRequestId = uuid.v4();
|
|
394
500
|
this.ensureApiClientAuthenticated();
|
|
395
501
|
// Create the room and sign the message
|
|
396
502
|
if (typeof message !== 'string') {
|
|
@@ -398,35 +504,22 @@ class DynamicWalletClient {
|
|
|
398
504
|
}
|
|
399
505
|
const data = await this.apiClient.signMessage({
|
|
400
506
|
walletId,
|
|
401
|
-
message
|
|
507
|
+
message,
|
|
508
|
+
isFormatted,
|
|
509
|
+
dynamicRequestId,
|
|
510
|
+
context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined,
|
|
511
|
+
onError,
|
|
512
|
+
forwardMPCClientEnabled: this.forwardMPCEnabled
|
|
402
513
|
});
|
|
403
514
|
return data;
|
|
404
515
|
}
|
|
405
|
-
async externalServerSign({ chainName, message, roomId, keyShare, derivationPath }) {
|
|
516
|
+
async externalServerSign({ chainName, message, roomId, keyShare, derivationPath, isFormatted }) {
|
|
406
517
|
try {
|
|
407
518
|
const mpcSigner = getMPCSigner({
|
|
408
519
|
chainName,
|
|
409
520
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
410
521
|
});
|
|
411
|
-
|
|
412
|
-
//note: Ecdsa can also be used by bitcoin, but only keccak256 is used by ethereum
|
|
413
|
-
if (mpcSigner instanceof node.Ecdsa) {
|
|
414
|
-
formattedMessage = node.MessageHash.keccak256(message);
|
|
415
|
-
} else if (mpcSigner instanceof node.Ed25519) {
|
|
416
|
-
if (typeof message === 'string') {
|
|
417
|
-
if (!isHexString(message)) {
|
|
418
|
-
formattedMessage = Buffer.from(message).toString('hex');
|
|
419
|
-
} else {
|
|
420
|
-
formattedMessage = Buffer.from(message, 'hex');
|
|
421
|
-
}
|
|
422
|
-
} else {
|
|
423
|
-
formattedMessage = message;
|
|
424
|
-
}
|
|
425
|
-
} else if (mpcSigner instanceof node.BIP340 && typeof message === 'string') {
|
|
426
|
-
formattedMessage = new TextEncoder().encode(message);
|
|
427
|
-
} else {
|
|
428
|
-
throw new Error('Unsupported signer type');
|
|
429
|
-
}
|
|
522
|
+
const formattedMessage = isFormatted ? new node.MessageHash(message) : formatMessage(chainName, message);
|
|
430
523
|
const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
|
|
431
524
|
return signature;
|
|
432
525
|
} catch (error) {
|
|
@@ -434,35 +527,104 @@ class DynamicWalletClient {
|
|
|
434
527
|
throw error;
|
|
435
528
|
}
|
|
436
529
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
530
|
+
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted }) {
|
|
531
|
+
try {
|
|
532
|
+
if (!this.apiClient.forwardMPCClient.connected) {
|
|
533
|
+
await this.initializeForwardMPCClient();
|
|
534
|
+
}
|
|
535
|
+
const messageForForwardMPC = core.serializeMessageForForwardMPC({
|
|
536
|
+
message,
|
|
537
|
+
isFormatted,
|
|
538
|
+
chainName
|
|
539
|
+
});
|
|
540
|
+
this.logger.info('Forward MPC enabled, signing message with forward MPC');
|
|
541
|
+
const environment = core.getEnvironmentFromUrl(this.baseApiUrl);
|
|
542
|
+
const defaultRelayUrl = core.MPC_RELAY_URL_MAP[environment];
|
|
543
|
+
const signature = await this.apiClient.forwardMPCClient.signMessage({
|
|
544
|
+
keyshare: keyShare,
|
|
545
|
+
message: chainName === 'SVM' ? formattedMessage : messageForForwardMPC,
|
|
546
|
+
relayDomain: this.baseMPCRelayApiUrl || defaultRelayUrl,
|
|
547
|
+
signingAlgo: chainName === 'EVM' ? 'ECDSA' : 'ED25519',
|
|
548
|
+
hashAlgo: chainName === 'EVM' && !isFormatted ? 'keccak256' : undefined,
|
|
549
|
+
derivationPath: derivationPath,
|
|
550
|
+
roomUuid: roomId
|
|
551
|
+
});
|
|
552
|
+
const signatureBytes = signature.data.signature;
|
|
553
|
+
if (!(signatureBytes instanceof Uint8Array)) {
|
|
554
|
+
throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
|
|
555
|
+
}
|
|
556
|
+
// Convert to EcdsaSignature
|
|
557
|
+
if (chainName === 'EVM') {
|
|
558
|
+
const ecdsaSignature = node.EcdsaSignature.fromBuffer(signatureBytes);
|
|
559
|
+
return ecdsaSignature;
|
|
560
|
+
} else {
|
|
561
|
+
return signatureBytes;
|
|
562
|
+
}
|
|
563
|
+
} catch (error) {
|
|
564
|
+
this.logger.error('Error signing message with forward MPC client', {
|
|
565
|
+
error,
|
|
566
|
+
environmentId: this.environmentId,
|
|
567
|
+
dynamicRequestId
|
|
568
|
+
});
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async sign({ accountAddress, externalServerKeyShares, message, chainName, password = undefined, isFormatted = false, context, onError }) {
|
|
573
|
+
try {
|
|
574
|
+
await this.verifyPassword({
|
|
575
|
+
accountAddress,
|
|
576
|
+
password,
|
|
577
|
+
walletOperation: core.WalletOperation.SIGN_MESSAGE
|
|
578
|
+
});
|
|
579
|
+
const wallet = await this.getWallet({
|
|
580
|
+
accountAddress,
|
|
581
|
+
walletOperation: core.WalletOperation.SIGN_MESSAGE,
|
|
582
|
+
password
|
|
583
|
+
});
|
|
584
|
+
externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
|
|
585
|
+
if (!externalServerKeyShares) {
|
|
586
|
+
throw new Error('External server key shares are required to sign a message');
|
|
587
|
+
}
|
|
588
|
+
// Perform the dynamic server sign
|
|
589
|
+
const data = await this.dynamicServerSign({
|
|
590
|
+
walletId: wallet.walletId,
|
|
591
|
+
message,
|
|
592
|
+
isFormatted,
|
|
593
|
+
context,
|
|
594
|
+
onError
|
|
595
|
+
});
|
|
596
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath != '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
597
|
+
// Perform the external server sign and return the signature
|
|
598
|
+
if (this.forwardMPCEnabled) {
|
|
599
|
+
const formattedMessage = isFormatted ? new node.MessageHash(message) : formatMessage(chainName, message);
|
|
600
|
+
const signature = await this.forwardMPCClientSign({
|
|
601
|
+
chainName,
|
|
602
|
+
message,
|
|
603
|
+
roomId: data.roomId,
|
|
604
|
+
keyShare: externalServerKeyShares[0],
|
|
605
|
+
derivationPath,
|
|
606
|
+
formattedMessage,
|
|
607
|
+
dynamicRequestId: uuid.v4(),
|
|
608
|
+
isFormatted
|
|
609
|
+
});
|
|
610
|
+
return signature;
|
|
611
|
+
} else {
|
|
612
|
+
const signature = await this.externalServerSign({
|
|
613
|
+
chainName,
|
|
614
|
+
message,
|
|
615
|
+
roomId: data.roomId,
|
|
616
|
+
keyShare: externalServerKeyShares[0],
|
|
617
|
+
derivationPath,
|
|
618
|
+
isFormatted
|
|
619
|
+
});
|
|
620
|
+
return signature;
|
|
621
|
+
}
|
|
622
|
+
} catch (error) {
|
|
623
|
+
this.logger.error('Error in sign', error);
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
464
626
|
}
|
|
465
|
-
async refreshWalletAccountShares({ accountAddress, chainName, password = undefined }) {
|
|
627
|
+
async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
|
|
466
628
|
this.ensureApiClientAuthenticated();
|
|
467
629
|
await this.verifyPassword({
|
|
468
630
|
accountAddress,
|
|
@@ -483,14 +645,19 @@ class DynamicWalletClient {
|
|
|
483
645
|
walletId: wallet.walletId
|
|
484
646
|
});
|
|
485
647
|
const roomId = data.roomId;
|
|
486
|
-
|
|
648
|
+
externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
|
|
649
|
+
if (!externalServerKeyShares) {
|
|
650
|
+
throw new Error('External server key shares are required to refresh');
|
|
651
|
+
}
|
|
652
|
+
const refreshResults = await Promise.all(externalServerKeyShares.map((serverKeyShare)=>mpcSigner.refresh(roomId, serverKeyShare)));
|
|
487
653
|
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
488
654
|
externalServerKeyShares: refreshResults,
|
|
489
655
|
externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo()
|
|
490
656
|
});
|
|
491
657
|
await this.storeEncryptedBackupByWallet({
|
|
492
658
|
accountAddress,
|
|
493
|
-
password: password != null ? password : this.environmentId
|
|
659
|
+
password: password != null ? password : this.environmentId,
|
|
660
|
+
backUpToClientShareService
|
|
494
661
|
});
|
|
495
662
|
return refreshResults;
|
|
496
663
|
}
|
|
@@ -543,7 +710,7 @@ class DynamicWalletClient {
|
|
|
543
710
|
existingExternalServerKeyShares
|
|
544
711
|
};
|
|
545
712
|
}
|
|
546
|
-
async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined }) {
|
|
713
|
+
async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, externalServerKeyShares, backUpToClientShareService = false }) {
|
|
547
714
|
this.ensureApiClientAuthenticated();
|
|
548
715
|
await this.verifyPassword({
|
|
549
716
|
accountAddress,
|
|
@@ -560,9 +727,15 @@ class DynamicWalletClient {
|
|
|
560
727
|
shareCount: existingExternalServerShareCount,
|
|
561
728
|
password
|
|
562
729
|
});
|
|
730
|
+
externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
|
|
731
|
+
if (!externalServerKeyShares) {
|
|
732
|
+
throw new Error('External server key shares are required to reshare');
|
|
733
|
+
}
|
|
563
734
|
const { newExternalServerInitKeygenResults, newExternalServerKeygenIds, existingExternalServerKeygenIds, existingExternalServerKeyShares } = await this.reshareStrategy({
|
|
564
735
|
chainName,
|
|
565
|
-
wallet,
|
|
736
|
+
wallet: _extends({}, wallet, {
|
|
737
|
+
externalServerKeyShares
|
|
738
|
+
}),
|
|
566
739
|
oldThresholdSignatureScheme,
|
|
567
740
|
newThresholdSignatureScheme
|
|
568
741
|
});
|
|
@@ -600,11 +773,12 @@ class DynamicWalletClient {
|
|
|
600
773
|
});
|
|
601
774
|
await this.storeEncryptedBackupByWallet({
|
|
602
775
|
accountAddress,
|
|
603
|
-
password
|
|
776
|
+
password,
|
|
777
|
+
backUpToClientShareService
|
|
604
778
|
});
|
|
605
779
|
return reshareResults;
|
|
606
780
|
}
|
|
607
|
-
async exportKey({ accountAddress, chainName, password = undefined }) {
|
|
781
|
+
async exportKey({ accountAddress, chainName, password = undefined, externalServerKeyShares }) {
|
|
608
782
|
this.ensureApiClientAuthenticated();
|
|
609
783
|
await this.verifyPassword({
|
|
610
784
|
accountAddress,
|
|
@@ -616,19 +790,23 @@ class DynamicWalletClient {
|
|
|
616
790
|
password,
|
|
617
791
|
walletOperation: core.WalletOperation.EXPORT_PRIVATE_KEY
|
|
618
792
|
});
|
|
793
|
+
externalServerKeyShares = externalServerKeyShares != null ? externalServerKeyShares : wallet.externalServerKeyShares;
|
|
794
|
+
if (!externalServerKeyShares) {
|
|
795
|
+
throw new Error('External server key shares are required to export a private key');
|
|
796
|
+
}
|
|
619
797
|
const mpcSigner = getMPCSigner({
|
|
620
798
|
chainName,
|
|
621
799
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
622
800
|
});
|
|
623
801
|
const exportId = await this.getExportId({
|
|
624
802
|
chainName,
|
|
625
|
-
serverKeyShare:
|
|
803
|
+
serverKeyShare: externalServerKeyShares[0]
|
|
626
804
|
});
|
|
627
805
|
const data = await this.apiClient.exportKey({
|
|
628
806
|
walletId: wallet.walletId,
|
|
629
807
|
exportId
|
|
630
808
|
});
|
|
631
|
-
const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId,
|
|
809
|
+
const keyExportRaw = await mpcSigner.exportFullPrivateKey(data.roomId, externalServerKeyShares[0], exportId);
|
|
632
810
|
if (!keyExportRaw) {
|
|
633
811
|
throw new Error('Error exporting private key');
|
|
634
812
|
}
|
|
@@ -636,7 +814,7 @@ class DynamicWalletClient {
|
|
|
636
814
|
let derivedPrivateKey;
|
|
637
815
|
if (mpcSigner instanceof node.Ecdsa) {
|
|
638
816
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
639
|
-
} else if (mpcSigner instanceof node.
|
|
817
|
+
} else if (mpcSigner instanceof node.ExportableEd25519) {
|
|
640
818
|
derivedPrivateKey = keyExportRaw;
|
|
641
819
|
} else if (mpcSigner instanceof node.BIP340) {
|
|
642
820
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
@@ -655,7 +833,7 @@ class DynamicWalletClient {
|
|
|
655
833
|
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
656
834
|
});
|
|
657
835
|
const walletKeyShares = keyShares.map((keyShare)=>{
|
|
658
|
-
return mpcSigner instanceof node.Ecdsa ? new node.EcdsaKeygenResult(keyShare.pubkey, keyShare.secretShare) : mpcSigner instanceof node.
|
|
836
|
+
return mpcSigner instanceof node.Ecdsa ? new node.EcdsaKeygenResult(keyShare.pubkey, keyShare.secretShare) : mpcSigner instanceof node.ExportableEd25519 ? new node.ExportableEd25519KeygenResult(keyShare.pubkey, keyShare.secretShare) : new node.BIP340KeygenResult(keyShare.pubkey, keyShare.secretShare);
|
|
659
837
|
});
|
|
660
838
|
const keyExportRaw = await mpcSigner.offlineExportFullPrivateKey(walletKeyShares);
|
|
661
839
|
if (!keyExportRaw) {
|
|
@@ -666,7 +844,7 @@ class DynamicWalletClient {
|
|
|
666
844
|
let derivedPrivateKey;
|
|
667
845
|
if (mpcSigner instanceof node.Ecdsa) {
|
|
668
846
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
669
|
-
} else if (mpcSigner instanceof node.
|
|
847
|
+
} else if (mpcSigner instanceof node.ExportableEd25519) {
|
|
670
848
|
derivedPrivateKey = keyExportRaw;
|
|
671
849
|
} else if (mpcSigner instanceof node.BIP340) {
|
|
672
850
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
@@ -689,11 +867,28 @@ class DynamicWalletClient {
|
|
|
689
867
|
const serializedEncryptedKeyShare = Buffer.from(JSON.stringify(encryptedKeyShare)).toString('base64');
|
|
690
868
|
return serializedEncryptedKeyShare;
|
|
691
869
|
}
|
|
692
|
-
async
|
|
870
|
+
async ensureCeremonyCompletionBeforeBackup({ accountAddress }) {
|
|
871
|
+
let retries = 0;
|
|
872
|
+
const maxRetries = 3;
|
|
873
|
+
while((!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) && retries < maxRetries){
|
|
874
|
+
await new Promise((resolve)=>setTimeout(resolve, 1000)); // Wait 1 second
|
|
875
|
+
retries++;
|
|
876
|
+
}
|
|
877
|
+
if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
|
|
878
|
+
throw new Error('Ceremony completion timeout');
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined, backUpToClientShareService }) {
|
|
693
882
|
this.ensureApiClientAuthenticated();
|
|
883
|
+
//add retry logic for ceremony completion to prevent race condition
|
|
884
|
+
await this.ensureCeremonyCompletionBeforeBackup({
|
|
885
|
+
accountAddress
|
|
886
|
+
});
|
|
887
|
+
const dynamicRequestId = uuid.v4();
|
|
694
888
|
try {
|
|
695
889
|
const keySharesToBackup = externalServerKeyShares != null ? externalServerKeyShares : await this.getExternalServerKeyShares({
|
|
696
|
-
accountAddress
|
|
890
|
+
accountAddress,
|
|
891
|
+
password
|
|
697
892
|
});
|
|
698
893
|
if (!keySharesToBackup || keySharesToBackup.length === 0) {
|
|
699
894
|
throw new Error(`Key shares not found for accountAddress: ${accountAddress}`);
|
|
@@ -705,51 +900,92 @@ class DynamicWalletClient {
|
|
|
705
900
|
if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
|
|
706
901
|
throw new Error(`WalletId not found for accountAddress: ${accountAddress}`);
|
|
707
902
|
}
|
|
708
|
-
const
|
|
903
|
+
const passwordEncryptedFlag = Boolean(password) && password !== this.environmentId;
|
|
904
|
+
const locations = [];
|
|
905
|
+
if (backUpToClientShareService) {
|
|
906
|
+
const data = await this.apiClient.storeEncryptedBackupByWallet({
|
|
907
|
+
walletId: this.walletMap[accountAddress].walletId,
|
|
908
|
+
encryptedKeyShares: encryptedKeyShares,
|
|
909
|
+
passwordEncrypted: passwordEncryptedFlag,
|
|
910
|
+
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
911
|
+
requiresSignedSessionId: false,
|
|
912
|
+
dynamicRequestId
|
|
913
|
+
});
|
|
914
|
+
locations.push({
|
|
915
|
+
location: core.BackupLocation.DYNAMIC,
|
|
916
|
+
externalKeyShareId: data.keyShareIds[0],
|
|
917
|
+
passwordEncrypted: passwordEncryptedFlag
|
|
918
|
+
});
|
|
919
|
+
} else {
|
|
920
|
+
locations.push({
|
|
921
|
+
location: core.BackupLocation.EXTERNAL
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
const backupData = await this.apiClient.markKeySharesAsBackedUp({
|
|
709
925
|
walletId: this.walletMap[accountAddress].walletId,
|
|
710
|
-
|
|
711
|
-
|
|
926
|
+
locations,
|
|
927
|
+
dynamicRequestId
|
|
928
|
+
});
|
|
929
|
+
const updatedBackupInfo = getExternalServerKeyShareBackupInfo({
|
|
930
|
+
walletProperties: {
|
|
931
|
+
derivationPath: this.walletMap[accountAddress].derivationPath,
|
|
932
|
+
keyShares: backupData.locationsWithKeyShares.map((ks)=>({
|
|
933
|
+
id: ks.keyShareId,
|
|
934
|
+
backupLocation: ks.location,
|
|
935
|
+
externalKeyShareId: ks.externalKeyShareId,
|
|
936
|
+
passwordEncrypted: passwordEncryptedFlag
|
|
937
|
+
})),
|
|
938
|
+
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
|
|
939
|
+
}
|
|
712
940
|
});
|
|
713
941
|
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
714
|
-
externalServerKeySharesBackupInfo:
|
|
715
|
-
walletProperties: {
|
|
716
|
-
derivationPath: this.walletMap[accountAddress].derivationPath,
|
|
717
|
-
keyShares: data.keyShares,
|
|
718
|
-
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
|
|
719
|
-
}
|
|
720
|
-
})
|
|
942
|
+
externalServerKeySharesBackupInfo: updatedBackupInfo
|
|
721
943
|
});
|
|
722
|
-
return
|
|
944
|
+
return encryptedKeyShares;
|
|
723
945
|
} catch (error) {
|
|
724
|
-
this.logger.error('Error in storeEncryptedBackupByWallet:',
|
|
946
|
+
this.logger.error('Error in storeEncryptedBackupByWallet:', {
|
|
947
|
+
accountAddress,
|
|
948
|
+
error,
|
|
949
|
+
dynamicRequestId
|
|
950
|
+
});
|
|
725
951
|
throw error;
|
|
726
952
|
}
|
|
727
953
|
}
|
|
728
|
-
async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password }) {
|
|
954
|
+
async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password, backUpToClientShareService }) {
|
|
729
955
|
await retryPromise(()=>this.storeEncryptedBackupByWallet({
|
|
730
956
|
accountAddress,
|
|
731
957
|
externalServerKeyShares,
|
|
732
|
-
password
|
|
958
|
+
password,
|
|
959
|
+
backUpToClientShareService
|
|
733
960
|
}), {
|
|
734
961
|
operationName: 'store encrypted backup',
|
|
735
962
|
logContext: {
|
|
736
|
-
walletAddress: accountAddress
|
|
737
|
-
keyShares: externalServerKeyShares == null ? void 0 : externalServerKeyShares.map((keyShare)=>this.encryptKeyShare({
|
|
738
|
-
keyShare,
|
|
739
|
-
password
|
|
740
|
-
}))
|
|
963
|
+
walletAddress: accountAddress
|
|
741
964
|
}
|
|
742
965
|
});
|
|
743
966
|
}
|
|
744
967
|
async getExternalServerKeyShares({ accountAddress, password }) {
|
|
968
|
+
var _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo;
|
|
745
969
|
const wallet = await this.getWallet({
|
|
746
970
|
accountAddress,
|
|
747
971
|
password,
|
|
748
972
|
walletOperation: core.WalletOperation.REACH_THRESHOLD
|
|
749
973
|
});
|
|
750
|
-
|
|
974
|
+
// Check if the wallet has a dynamic backup and derive password encryption status from the first dynamic share
|
|
975
|
+
const dynamicBackups = (wallet == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups = _wallet_externalServerKeySharesBackupInfo.backups) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo_backups.dynamic) || [];
|
|
976
|
+
const passwordEncrypted = dynamicBackups.length > 0 ? Boolean(dynamicBackups[0].passwordEncrypted) : false;
|
|
977
|
+
if (passwordEncrypted && !password) {
|
|
978
|
+
throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
|
|
979
|
+
}
|
|
980
|
+
// recover the shares
|
|
981
|
+
const recoveredShares = await this.recoverEncryptedBackupByWallet({
|
|
982
|
+
accountAddress,
|
|
983
|
+
password,
|
|
984
|
+
walletOperation: core.WalletOperation.REACH_THRESHOLD
|
|
985
|
+
});
|
|
986
|
+
return recoveredShares;
|
|
751
987
|
}
|
|
752
|
-
async updatePassword({ accountAddress, existingPassword, newPassword }) {
|
|
988
|
+
async updatePassword({ accountAddress, existingPassword, newPassword, backUpToClientShareService }) {
|
|
753
989
|
await this.getWallet({
|
|
754
990
|
accountAddress,
|
|
755
991
|
password: existingPassword,
|
|
@@ -757,7 +993,8 @@ class DynamicWalletClient {
|
|
|
757
993
|
});
|
|
758
994
|
await this.storeEncryptedBackupByWallet({
|
|
759
995
|
accountAddress,
|
|
760
|
-
password: newPassword
|
|
996
|
+
password: newPassword,
|
|
997
|
+
backUpToClientShareService
|
|
761
998
|
});
|
|
762
999
|
}
|
|
763
1000
|
async decryptKeyShare({ keyShare, password }) {
|
|
@@ -788,17 +1025,57 @@ class DynamicWalletClient {
|
|
|
788
1025
|
if (shareCount !== undefined) {
|
|
789
1026
|
requiredShareCount = shareCount;
|
|
790
1027
|
}
|
|
791
|
-
const dynamicShares = backups[core.BackupLocation.DYNAMIC].slice(0, requiredShareCount);
|
|
1028
|
+
const dynamicShares = (backups[core.BackupLocation.DYNAMIC] || []).slice(0, requiredShareCount);
|
|
1029
|
+
const externalShares = (backups[core.BackupLocation.EXTERNAL] || []).slice(0, requiredShareCount);
|
|
1030
|
+
// If we have DYNAMIC shares, use those; otherwise use EXTERNAL shares
|
|
1031
|
+
const sharesToUse = dynamicShares.length > 0 ? dynamicShares : externalShares;
|
|
1032
|
+
const backupLocation = dynamicShares.length > 0 ? core.BackupLocation.DYNAMIC : core.BackupLocation.EXTERNAL;
|
|
792
1033
|
return {
|
|
793
1034
|
shares: {
|
|
794
|
-
[
|
|
1035
|
+
[backupLocation]: sharesToUse.map((ks)=>{
|
|
1036
|
+
var _ks_externalKeyShareId, _ref;
|
|
1037
|
+
return (_ref = (_ks_externalKeyShareId = ks.externalKeyShareId) != null ? _ks_externalKeyShareId : ks.keyShareId) != null ? _ref : '';
|
|
1038
|
+
})
|
|
795
1039
|
},
|
|
796
1040
|
requiredShareCount
|
|
797
1041
|
};
|
|
798
1042
|
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Attempts to recover key shares from backup if they are not provided.
|
|
1045
|
+
* The recovered shares will be stored in the wallet map for use in signing operations.
|
|
1046
|
+
* @param accountAddress - The account address to recover shares for
|
|
1047
|
+
* @param password - The password to decrypt the shares
|
|
1048
|
+
* @param walletOperation - The wallet operation being performed
|
|
1049
|
+
* @param externalServerKeyShares - The provided key shares (if any)
|
|
1050
|
+
* @param errorMessage - The error message to throw if recovery fails
|
|
1051
|
+
* @throws Error if recovery is needed but fails
|
|
1052
|
+
*/ async ensureKeySharesRecovered({ accountAddress, password, walletOperation, externalServerKeyShares, errorMessage }) {
|
|
1053
|
+
if (!externalServerKeyShares || externalServerKeyShares.length === 0) {
|
|
1054
|
+
// attempt to recover dynamic keyshares if no key shares are provided
|
|
1055
|
+
// the shares will be used for signing in the node SDK if successful
|
|
1056
|
+
const recoveredShares = await this.recoverEncryptedBackupByWallet({
|
|
1057
|
+
accountAddress,
|
|
1058
|
+
password,
|
|
1059
|
+
walletOperation,
|
|
1060
|
+
storeRecoveredShares: true
|
|
1061
|
+
});
|
|
1062
|
+
// If recovery returned empty and no shares were provided, throw error
|
|
1063
|
+
if (!recoveredShares || recoveredShares.length === 0) {
|
|
1064
|
+
throw new Error(errorMessage);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
799
1068
|
async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, shareCount = undefined, storeRecoveredShares = true }) {
|
|
1069
|
+
var _wallet_externalServerKeySharesBackupInfo;
|
|
800
1070
|
this.ensureApiClientAuthenticated();
|
|
801
|
-
|
|
1071
|
+
let wallet = this.walletMap[accountAddress];
|
|
1072
|
+
if (!wallet) {
|
|
1073
|
+
const fetchedWallet = await this.getWalletByAddress(accountAddress);
|
|
1074
|
+
if (!fetchedWallet) {
|
|
1075
|
+
throw new Error(`Wallet not found for address 1: ${accountAddress}`);
|
|
1076
|
+
}
|
|
1077
|
+
wallet = fetchedWallet;
|
|
1078
|
+
}
|
|
802
1079
|
this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
|
|
803
1080
|
const { shares } = this.recoverStrategy({
|
|
804
1081
|
externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
|
|
@@ -806,12 +1083,30 @@ class DynamicWalletClient {
|
|
|
806
1083
|
walletOperation,
|
|
807
1084
|
shareCount
|
|
808
1085
|
});
|
|
809
|
-
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1086
|
+
// Get the key share IDs from whichever backup location has shares
|
|
1087
|
+
const dynamicKeyShareIds = shares[core.BackupLocation.DYNAMIC] || [];
|
|
1088
|
+
const externalKeyShareIds = shares[core.BackupLocation.EXTERNAL] || [];
|
|
1089
|
+
// Only attempt recovery if we have DYNAMIC shares (the API doesn't support EXTERNAL shares)
|
|
1090
|
+
let data;
|
|
1091
|
+
if (dynamicKeyShareIds.length > 0) {
|
|
1092
|
+
data = await this.apiClient.recoverEncryptedBackupByWallet({
|
|
1093
|
+
walletId: wallet.walletId,
|
|
1094
|
+
keyShareIds: dynamicKeyShareIds,
|
|
1095
|
+
requiresSignedSessionId: false
|
|
1096
|
+
});
|
|
1097
|
+
} else if (externalKeyShareIds.length > 0) {
|
|
1098
|
+
this.logger.debug('Skipping recovery - only EXTERNAL shares available (backUpToClientShareService: false)');
|
|
1099
|
+
return []; // Return empty array since we can't recover EXTERNAL shares via API
|
|
1100
|
+
} else {
|
|
1101
|
+
this.logger.debug('No shares available for recovery');
|
|
1102
|
+
return []; // Return empty array if no shares are available
|
|
1103
|
+
}
|
|
814
1104
|
const dynamicKeyShares = data.keyShares.filter((keyShare)=>keyShare.encryptedAccountCredential !== null && keyShare.backupLocation === core.BackupLocation.DYNAMIC);
|
|
1105
|
+
var _wallet_externalServerKeySharesBackupInfo_passwordEncrypted;
|
|
1106
|
+
const isPasswordEncrypted = (_wallet_externalServerKeySharesBackupInfo_passwordEncrypted = (_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo.passwordEncrypted) != null ? _wallet_externalServerKeySharesBackupInfo_passwordEncrypted : false;
|
|
1107
|
+
if (isPasswordEncrypted && !password) {
|
|
1108
|
+
throw new Error('Password is required for decryption but not provided. This backup was encrypted with a password.');
|
|
1109
|
+
}
|
|
815
1110
|
const decryptedKeyShares = await Promise.all(dynamicKeyShares.map((keyShare)=>this.decryptKeyShare({
|
|
816
1111
|
keyShare: keyShare.encryptedAccountCredential,
|
|
817
1112
|
password: password != null ? password : this.environmentId
|
|
@@ -841,6 +1136,7 @@ class DynamicWalletClient {
|
|
|
841
1136
|
* @param walletOperation - The wallet operation that determines required fields
|
|
842
1137
|
* @returns boolean indicating if wallet needs to be re-fetched and restored from server
|
|
843
1138
|
*/ async checkWalletFields({ accountAddress, walletOperation = core.WalletOperation.REACH_THRESHOLD, shareCount }) {
|
|
1139
|
+
var _existingWallet_externalServerKeyShares;
|
|
844
1140
|
let keyshareCheck = false;
|
|
845
1141
|
let walletCheck = false;
|
|
846
1142
|
let thresholdSignatureSchemeCheck = false;
|
|
@@ -860,7 +1156,7 @@ class DynamicWalletClient {
|
|
|
860
1156
|
}
|
|
861
1157
|
// check if wallet already exists with sufficient keyshares
|
|
862
1158
|
if (existingWallet) {
|
|
863
|
-
var
|
|
1159
|
+
var _existingWallet_externalServerKeyShares1;
|
|
864
1160
|
const { shares } = this.recoverStrategy({
|
|
865
1161
|
externalServerKeySharesBackupInfo: existingWallet.externalServerKeySharesBackupInfo || {
|
|
866
1162
|
backups: getExternalServerKeyShareBackupInfo()
|
|
@@ -870,11 +1166,35 @@ class DynamicWalletClient {
|
|
|
870
1166
|
shareCount
|
|
871
1167
|
});
|
|
872
1168
|
const { dynamic: requiredDynamicKeyShareIds = [] } = shares;
|
|
873
|
-
|
|
1169
|
+
const { external: requiredExternalKeyShareIds = [] } = shares;
|
|
1170
|
+
// Check if we have enough shares from any backup location
|
|
1171
|
+
const totalRequiredShares = requiredDynamicKeyShareIds.length + requiredExternalKeyShareIds.length;
|
|
1172
|
+
// Check if we have the required shares either loaded OR available in backup
|
|
1173
|
+
const hasLoadedShares = totalRequiredShares <= (((_existingWallet_externalServerKeyShares1 = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares1.length) || 0);
|
|
1174
|
+
// Check if backup contains the specific required share IDs
|
|
1175
|
+
const hasBackupShares = existingWallet.externalServerKeySharesBackupInfo && (()=>{
|
|
1176
|
+
const backupShares = existingWallet.externalServerKeySharesBackupInfo.backups.dynamic;
|
|
1177
|
+
const allRequiredIds = [
|
|
1178
|
+
...requiredDynamicKeyShareIds,
|
|
1179
|
+
...requiredExternalKeyShareIds
|
|
1180
|
+
];
|
|
1181
|
+
return allRequiredIds.every((requiredId)=>backupShares.some((backupShare)=>backupShare.externalKeyShareId === requiredId || backupShare.keyShareId === requiredId));
|
|
1182
|
+
})();
|
|
1183
|
+
if (hasLoadedShares || hasBackupShares) {
|
|
874
1184
|
keyshareCheck = true;
|
|
875
1185
|
}
|
|
876
1186
|
}
|
|
877
|
-
|
|
1187
|
+
const result = walletCheck && thresholdSignatureSchemeCheck && keyshareCheck && derivationPathCheck;
|
|
1188
|
+
this.logger.debug('Wallet checks:', {
|
|
1189
|
+
walletCheck,
|
|
1190
|
+
thresholdSignatureSchemeCheck,
|
|
1191
|
+
keyshareCheck,
|
|
1192
|
+
derivationPathCheck,
|
|
1193
|
+
existingWallet: !!existingWallet,
|
|
1194
|
+
keySharesLength: existingWallet == null ? void 0 : (_existingWallet_externalServerKeyShares = existingWallet.externalServerKeyShares) == null ? void 0 : _existingWallet_externalServerKeyShares.length,
|
|
1195
|
+
result
|
|
1196
|
+
});
|
|
1197
|
+
return result;
|
|
878
1198
|
}
|
|
879
1199
|
/**
|
|
880
1200
|
* verifyPassword attempts to recover and decrypt a single client key share using the provided password.
|
|
@@ -900,8 +1220,10 @@ class DynamicWalletClient {
|
|
|
900
1220
|
accountAddress
|
|
901
1221
|
});
|
|
902
1222
|
const { dynamic: dynamicKeyShareIds = [] } = backups;
|
|
903
|
-
|
|
904
|
-
|
|
1223
|
+
const { external: externalKeyShareIds = [] } = backups;
|
|
1224
|
+
// Check if we have any shares available (DYNAMIC or EXTERNAL)
|
|
1225
|
+
if (dynamicKeyShareIds.length === 0 && externalKeyShareIds.length === 0) {
|
|
1226
|
+
throw new Error('No key shares found');
|
|
905
1227
|
}
|
|
906
1228
|
try {
|
|
907
1229
|
await this.recoverEncryptedBackupByWallet({
|
|
@@ -946,88 +1268,164 @@ class DynamicWalletClient {
|
|
|
946
1268
|
if (walletOperation === core.WalletOperation.REACH_ALL_PARTIES || walletOperation === core.WalletOperation.REFRESH || walletOperation === core.WalletOperation.RESHARE) {
|
|
947
1269
|
return true;
|
|
948
1270
|
}
|
|
949
|
-
const { requiredShareCount } = this.recoverStrategy({
|
|
1271
|
+
const { shares, requiredShareCount } = this.recoverStrategy({
|
|
950
1272
|
externalServerKeySharesBackupInfo,
|
|
951
1273
|
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme,
|
|
952
1274
|
walletOperation
|
|
953
1275
|
});
|
|
954
|
-
|
|
1276
|
+
const dynamicKeyShareIds = shares[core.BackupLocation.DYNAMIC] || [];
|
|
1277
|
+
const externalKeyShareIds = shares[core.BackupLocation.EXTERNAL] || [];
|
|
1278
|
+
// Check if we have enough shares from either location
|
|
1279
|
+
const totalAvailableShares = externalServerKeyShares.length + dynamicKeyShareIds.length + externalKeyShareIds.length;
|
|
1280
|
+
if (totalAvailableShares >= requiredShareCount) {
|
|
955
1281
|
return false;
|
|
956
1282
|
}
|
|
957
1283
|
return true;
|
|
958
1284
|
}
|
|
959
1285
|
async getWalletExternalServerKeyShareBackupInfo({ accountAddress }) {
|
|
960
|
-
var
|
|
1286
|
+
var _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC, _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo, _user_verifiedCredentials;
|
|
961
1287
|
this.ensureApiClientAuthenticated();
|
|
1288
|
+
let wallet = this.walletMap[accountAddress];
|
|
1289
|
+
if (!wallet) {
|
|
1290
|
+
const fetchedWallet = await this.getWalletByAddress(accountAddress);
|
|
1291
|
+
if (!fetchedWallet) {
|
|
1292
|
+
throw new Error(`Wallet not found for address 2: ${accountAddress}`);
|
|
1293
|
+
}
|
|
1294
|
+
wallet = fetchedWallet;
|
|
1295
|
+
}
|
|
962
1296
|
// Return existing backup info if it exists
|
|
963
|
-
if (((
|
|
964
|
-
return
|
|
1297
|
+
if (((_wallet_externalServerKeySharesBackupInfo = wallet.externalServerKeySharesBackupInfo) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups = _wallet_externalServerKeySharesBackupInfo.backups) == null ? void 0 : (_wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC = _wallet_externalServerKeySharesBackupInfo_backups[core.BackupLocation.DYNAMIC]) == null ? void 0 : _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC.length) > 0) {
|
|
1298
|
+
return wallet.externalServerKeySharesBackupInfo;
|
|
965
1299
|
}
|
|
966
1300
|
// Get backup info from server
|
|
967
|
-
const user = await this.apiClient.getUser();
|
|
968
|
-
const
|
|
1301
|
+
const user = await this.apiClient.getUser(uuid.v4());
|
|
1302
|
+
const walletData = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
|
|
969
1303
|
return getExternalServerKeyShareBackupInfo({
|
|
970
|
-
walletProperties:
|
|
1304
|
+
walletProperties: walletData == null ? void 0 : walletData.walletProperties
|
|
971
1305
|
});
|
|
972
1306
|
}
|
|
973
1307
|
async getWallet({ accountAddress, walletOperation = core.WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
accountAddress,
|
|
978
|
-
walletOperation,
|
|
979
|
-
shareCount
|
|
980
|
-
});
|
|
981
|
-
if (existingWalletCheck) {
|
|
982
|
-
this.logger.debug(`Wallet ${accountAddress} already exists`);
|
|
983
|
-
return this.walletMap[accountAddress];
|
|
984
|
-
}
|
|
985
|
-
//todo: Question - why don't we just call getWallets here? so then all are preloaded
|
|
986
|
-
// Fetch and restore wallet from server
|
|
987
|
-
const user = await this.apiClient.getUser();
|
|
988
|
-
const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
|
|
989
|
-
this.logger.debug('Restoring wallet', wallet);
|
|
990
|
-
const walletProperties = wallet.walletProperties;
|
|
991
|
-
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
992
|
-
walletId: wallet.id,
|
|
993
|
-
chainName: wallet.chainName,
|
|
994
|
-
accountAddress,
|
|
995
|
-
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme,
|
|
996
|
-
derivationPath: walletProperties.derivationPath,
|
|
997
|
-
externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
|
|
998
|
-
walletProperties
|
|
999
|
-
})
|
|
1000
|
-
});
|
|
1001
|
-
if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
1002
|
-
accountAddress,
|
|
1003
|
-
walletOperation
|
|
1004
|
-
})) {
|
|
1005
|
-
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
1308
|
+
try {
|
|
1309
|
+
this.ensureApiClientAuthenticated();
|
|
1310
|
+
const existingWalletCheck = await this.checkWalletFields({
|
|
1006
1311
|
accountAddress,
|
|
1007
|
-
|
|
1008
|
-
walletOperation: walletOperation,
|
|
1312
|
+
walletOperation,
|
|
1009
1313
|
shareCount
|
|
1010
1314
|
});
|
|
1011
|
-
|
|
1315
|
+
if (existingWalletCheck) {
|
|
1316
|
+
this.logger.debug(`Wallet ${accountAddress} already exists`);
|
|
1317
|
+
return this.walletMap[accountAddress];
|
|
1318
|
+
}
|
|
1319
|
+
const wallet = await this.getWalletByAddress(accountAddress);
|
|
1320
|
+
if (!wallet) {
|
|
1321
|
+
throw new Error(`Wallet not found for address 3: ${accountAddress}`);
|
|
1322
|
+
}
|
|
1323
|
+
this.logger.debug('Restoring wallet', wallet);
|
|
1324
|
+
// The wallet is already processed, so we can use it directly
|
|
1325
|
+
this.walletMap[accountAddress] = wallet;
|
|
1326
|
+
if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
1327
|
+
accountAddress,
|
|
1328
|
+
walletOperation
|
|
1329
|
+
})) {
|
|
1330
|
+
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
1331
|
+
accountAddress,
|
|
1332
|
+
password: password != null ? password : this.environmentId,
|
|
1333
|
+
walletOperation: walletOperation,
|
|
1334
|
+
shareCount
|
|
1335
|
+
});
|
|
1336
|
+
this.logger.debug('[DynamicWaasWalletClient] Recovered backup', decryptedKeyShares);
|
|
1337
|
+
}
|
|
1338
|
+
// externalServerKeyShares
|
|
1339
|
+
const walletCount = Object.keys(this.walletMap).length;
|
|
1340
|
+
if (walletCount === 0) {
|
|
1341
|
+
throw new Error('No wallets found');
|
|
1342
|
+
}
|
|
1343
|
+
// Return the only wallet if there's just one
|
|
1344
|
+
if (walletCount === 1) {
|
|
1345
|
+
return Object.values(this.walletMap)[0];
|
|
1346
|
+
}
|
|
1347
|
+
return this.walletMap[accountAddress];
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
this.logger.error('Error in getWallet', error);
|
|
1350
|
+
throw error;
|
|
1012
1351
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Get a single wallet by address
|
|
1355
|
+
* First tries the efficient getWaasWalletByAddress endpoint, falls back to getUser() if not available
|
|
1356
|
+
*/ async getWalletByAddress(accountAddress) {
|
|
1357
|
+
// Return cached wallet if available
|
|
1358
|
+
if (this.walletMap[accountAddress]) {
|
|
1359
|
+
return this.walletMap[accountAddress];
|
|
1016
1360
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1361
|
+
this.ensureApiClientAuthenticated();
|
|
1362
|
+
// Try getting single wallet by address first
|
|
1363
|
+
try {
|
|
1364
|
+
var _walletResponse_wallet;
|
|
1365
|
+
const walletResponse = await this.apiClient.getWaasWalletByAddress({
|
|
1366
|
+
walletAddress: accountAddress
|
|
1367
|
+
});
|
|
1368
|
+
const walletProperties = {
|
|
1369
|
+
walletId: walletResponse.wallet.walletId,
|
|
1370
|
+
chainName: walletResponse.wallet.chainName,
|
|
1371
|
+
accountAddress: walletResponse.wallet.accountAddress,
|
|
1372
|
+
externalServerKeyShares: [],
|
|
1373
|
+
derivationPath: walletResponse.wallet.derivationPath,
|
|
1374
|
+
thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
|
|
1375
|
+
externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
|
|
1376
|
+
walletProperties: {
|
|
1377
|
+
// @ts-expect-error TODO: update response to get key shares
|
|
1378
|
+
keyShares: ((_walletResponse_wallet = walletResponse.wallet) == null ? void 0 : _walletResponse_wallet.keyShares) || [],
|
|
1379
|
+
thresholdSignatureScheme: walletResponse.wallet.thresholdSignatureScheme,
|
|
1380
|
+
derivationPath: walletResponse.wallet.derivationPath
|
|
1381
|
+
}
|
|
1382
|
+
})
|
|
1383
|
+
};
|
|
1384
|
+
// Cache the wallet
|
|
1385
|
+
this.walletMap[accountAddress] = walletProperties;
|
|
1386
|
+
return walletProperties;
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
var _error_response;
|
|
1389
|
+
// If the new endpoint doesn't exist (404 or not implemented), fall back to getUser()
|
|
1390
|
+
if ((error == null ? void 0 : (_error_response = error.response) == null ? void 0 : _error_response.status) === 404 || (error == null ? void 0 : error.code) === 'ERR_BAD_REQUEST') {
|
|
1391
|
+
var _user_verifiedCredentials, _wallet_walletProperties, _wallet_walletProperties1;
|
|
1392
|
+
this.logger.debug('getWaasWalletByAddress endpoint not available, falling back to getUser()');
|
|
1393
|
+
// Fallback to getUser() approach
|
|
1394
|
+
const user = await this.apiClient.getUser(uuid.v4());
|
|
1395
|
+
const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.walletName === 'dynamicwaas' && vc.address.toLowerCase() === accountAddress.toLowerCase());
|
|
1396
|
+
if (!wallet) {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
var _wallet_walletProperties_derivationPath;
|
|
1400
|
+
const walletProperties = {
|
|
1401
|
+
walletId: wallet.id,
|
|
1402
|
+
chainName: wallet.chain,
|
|
1403
|
+
accountAddress: wallet.address,
|
|
1404
|
+
externalServerKeyShares: [],
|
|
1405
|
+
derivationPath: (_wallet_walletProperties_derivationPath = (_wallet_walletProperties = wallet.walletProperties) == null ? void 0 : _wallet_walletProperties.derivationPath) != null ? _wallet_walletProperties_derivationPath : undefined,
|
|
1406
|
+
thresholdSignatureScheme: (_wallet_walletProperties1 = wallet.walletProperties) == null ? void 0 : _wallet_walletProperties1.thresholdSignatureScheme,
|
|
1407
|
+
externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
|
|
1408
|
+
walletProperties: wallet.walletProperties || {}
|
|
1409
|
+
})
|
|
1410
|
+
};
|
|
1411
|
+
// Cache the wallet
|
|
1412
|
+
this.walletMap[accountAddress] = walletProperties;
|
|
1413
|
+
return walletProperties;
|
|
1414
|
+
}
|
|
1415
|
+
throw error;
|
|
1020
1416
|
}
|
|
1021
|
-
return this.walletMap[accountAddress];
|
|
1022
1417
|
}
|
|
1023
|
-
|
|
1418
|
+
/**
|
|
1419
|
+
* Get all wallets (kept for backward compatibility but now uses lazy loading)
|
|
1420
|
+
* @deprecated Consider using getWalletByAddress for better performance with large wallet counts
|
|
1421
|
+
*/ async getWallets() {
|
|
1024
1422
|
var _user_verifiedCredentials;
|
|
1025
1423
|
this.ensureApiClientAuthenticated();
|
|
1026
|
-
const user = await this.apiClient.getUser();
|
|
1424
|
+
const user = await this.apiClient.getUser(uuid.v4());
|
|
1027
1425
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
1028
1426
|
const wallets = waasWallets.map((vc)=>{
|
|
1029
|
-
var _this_walletMap_vc_address,
|
|
1030
|
-
var
|
|
1427
|
+
var _this_walletMap_vc_address, _vc_walletProperties, _vc_walletProperties1;
|
|
1428
|
+
var _vc_walletProperties_derivationPath;
|
|
1031
1429
|
return {
|
|
1032
1430
|
walletId: vc.id,
|
|
1033
1431
|
chainName: vc.chain,
|
|
@@ -1036,101 +1434,260 @@ class DynamicWalletClient {
|
|
|
1036
1434
|
walletProperties: vc.walletProperties || {}
|
|
1037
1435
|
}),
|
|
1038
1436
|
externalServerKeyShares: ((_this_walletMap_vc_address = this.walletMap[vc.address]) == null ? void 0 : _this_walletMap_vc_address.externalServerKeyShares) || [],
|
|
1039
|
-
derivationPath: (
|
|
1040
|
-
thresholdSignatureScheme: (
|
|
1437
|
+
derivationPath: (_vc_walletProperties_derivationPath = (_vc_walletProperties = vc.walletProperties) == null ? void 0 : _vc_walletProperties.derivationPath) != null ? _vc_walletProperties_derivationPath : undefined,
|
|
1438
|
+
thresholdSignatureScheme: (_vc_walletProperties1 = vc.walletProperties) == null ? void 0 : _vc_walletProperties1.thresholdSignatureScheme
|
|
1041
1439
|
};
|
|
1042
1440
|
});
|
|
1043
1441
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
1044
|
-
var _acc_accountAddress;
|
|
1045
|
-
const accountAddress = wallet.accountAddress;
|
|
1046
1442
|
acc[wallet.accountAddress] = {
|
|
1047
1443
|
walletId: wallet.walletId,
|
|
1048
1444
|
chainName: wallet.chainName,
|
|
1049
1445
|
accountAddress: wallet.accountAddress,
|
|
1050
1446
|
externalServerKeyShares: wallet.externalServerKeyShares || [],
|
|
1051
1447
|
externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
|
|
1052
|
-
derivationPath:
|
|
1448
|
+
derivationPath: wallet.derivationPath,
|
|
1053
1449
|
thresholdSignatureScheme: wallet.thresholdSignatureScheme
|
|
1054
1450
|
};
|
|
1055
1451
|
return acc;
|
|
1056
1452
|
}, {});
|
|
1057
1453
|
return wallets;
|
|
1058
1454
|
}
|
|
1059
|
-
constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug }){
|
|
1455
|
+
constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug, forwardMPCClient, enableMPCAccelerator = false }){
|
|
1060
1456
|
this.logger = logger;
|
|
1061
1457
|
this.walletMap = {} // todo: store in session storage
|
|
1062
1458
|
;
|
|
1063
1459
|
this.isApiClientAuthenticated = false;
|
|
1460
|
+
this.forwardMPCEnabled = false;
|
|
1064
1461
|
this.environmentId = environmentId;
|
|
1065
1462
|
this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
|
|
1066
1463
|
this.baseApiUrl = baseApiUrl;
|
|
1067
1464
|
this.debug = Boolean(debug);
|
|
1068
1465
|
this.logger.setLogLevel(this.debug ? 'DEBUG' : DEFAULT_LOG_LEVEL);
|
|
1466
|
+
this.forwardMPCEnabled = enableMPCAccelerator || Boolean(forwardMPCClient);
|
|
1467
|
+
// Initialize API client with forwardMPCClient
|
|
1468
|
+
this.apiClient = new core.DynamicApiClient({
|
|
1469
|
+
environmentId,
|
|
1470
|
+
baseApiUrl,
|
|
1471
|
+
forwardMPCClient
|
|
1472
|
+
});
|
|
1473
|
+
if (this.forwardMPCEnabled) {
|
|
1474
|
+
this.logger.info('Initializing ForwardMPC enclave websocket in node client. Environment: ' + this.environmentId);
|
|
1475
|
+
this.initializeForwardMPCClient();
|
|
1476
|
+
}
|
|
1069
1477
|
}
|
|
1070
1478
|
}
|
|
1071
1479
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
})
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1480
|
+
const createCore = ({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug = false })=>{
|
|
1481
|
+
const coreLogger = logger;
|
|
1482
|
+
coreLogger.setLogLevel(debug ? 'DEBUG' : DEFAULT_LOG_LEVEL);
|
|
1483
|
+
const createApiClient = (options = {})=>{
|
|
1484
|
+
return new core.DynamicApiClient(_extends({
|
|
1485
|
+
environmentId,
|
|
1486
|
+
baseApiUrl
|
|
1487
|
+
}, options));
|
|
1488
|
+
};
|
|
1489
|
+
return {
|
|
1490
|
+
environmentId,
|
|
1491
|
+
createApiClient,
|
|
1492
|
+
logger: coreLogger,
|
|
1493
|
+
baseMPCRelayApiUrl,
|
|
1494
|
+
baseApiUrl,
|
|
1495
|
+
debug
|
|
1496
|
+
};
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
// Helper function to create API client for delegated operations
|
|
1500
|
+
const createDelegatedApiClient = (client, options = {})=>{
|
|
1501
|
+
return client.createApiClient(options);
|
|
1502
|
+
};
|
|
1503
|
+
// Helper function to create API client with wallet-specific headers
|
|
1504
|
+
const createDelegatedApiClientWithWalletKey = (client, walletApiKey)=>{
|
|
1505
|
+
const apiClient = client.createApiClient();
|
|
1506
|
+
// Add the wallet-specific API key header to all requests from this client
|
|
1507
|
+
apiClient.apiClient.defaults.headers['x-dyn-wallet-api-key'] = walletApiKey;
|
|
1508
|
+
return apiClient;
|
|
1509
|
+
};
|
|
1510
|
+
const createDelegatedWalletClient = ({ environmentId, baseApiUrl, baseMPCRelayApiUrl, apiKey, debug = false })=>{
|
|
1511
|
+
const core = createCore({
|
|
1512
|
+
environmentId,
|
|
1513
|
+
baseApiUrl,
|
|
1514
|
+
baseMPCRelayApiUrl,
|
|
1515
|
+
debug
|
|
1516
|
+
});
|
|
1517
|
+
// Store the API key for use in delegated operations
|
|
1518
|
+
core.apiKey = apiKey;
|
|
1519
|
+
const walletMap = {};
|
|
1520
|
+
const client = {
|
|
1521
|
+
get environmentId () {
|
|
1522
|
+
return core.environmentId;
|
|
1523
|
+
},
|
|
1524
|
+
get debug () {
|
|
1525
|
+
return core.debug;
|
|
1526
|
+
},
|
|
1527
|
+
get apiUrl () {
|
|
1528
|
+
var _core_baseApiUrl;
|
|
1529
|
+
return (_core_baseApiUrl = core.baseApiUrl) != null ? _core_baseApiUrl : 'https://app.dynamicauth.com';
|
|
1530
|
+
},
|
|
1531
|
+
get wallets () {
|
|
1532
|
+
return walletMap;
|
|
1533
|
+
},
|
|
1534
|
+
createApiClient: (options = {})=>{
|
|
1535
|
+
return core.createApiClient(_extends({
|
|
1536
|
+
authToken: apiKey
|
|
1537
|
+
}, options));
|
|
1538
|
+
},
|
|
1539
|
+
logger: core.logger,
|
|
1540
|
+
apiKey,
|
|
1541
|
+
baseMPCRelayApiUrl: core.baseMPCRelayApiUrl
|
|
1542
|
+
};
|
|
1543
|
+
return client;
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
const dynamicDelegatedSign = async (client, { walletId, message, isFormatted, walletApiKey, onError, context })=>{
|
|
1547
|
+
const dynamicRequestId = uuid.v4();
|
|
1548
|
+
// Convert message to hex if it's a Uint8Array
|
|
1549
|
+
if (typeof message !== 'string') {
|
|
1550
|
+
message = '0x' + Buffer.from(message).toString('hex');
|
|
1551
|
+
}
|
|
1552
|
+
const apiClient = createDelegatedApiClientWithWalletKey(client, walletApiKey);
|
|
1553
|
+
const data = await apiClient.delegatedSignMessage({
|
|
1554
|
+
walletId,
|
|
1555
|
+
message,
|
|
1556
|
+
isFormatted,
|
|
1557
|
+
dynamicRequestId,
|
|
1558
|
+
onError,
|
|
1559
|
+
context: context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined
|
|
1560
|
+
});
|
|
1561
|
+
return data;
|
|
1562
|
+
};
|
|
1563
|
+
const delegatedSign = async (client, { chainName, message, roomId, keyShare, derivationPath, isFormatted })=>{
|
|
1564
|
+
try {
|
|
1565
|
+
const mpcSigner = getMPCSigner({
|
|
1566
|
+
chainName,
|
|
1567
|
+
baseRelayUrl: client.baseMPCRelayApiUrl
|
|
1568
|
+
});
|
|
1569
|
+
const formattedMessage = isFormatted ? new node.MessageHash(message) : formatMessage(chainName, message);
|
|
1570
|
+
const signature = await mpcSigner.sign(roomId, keyShare, formattedMessage, derivationPath);
|
|
1571
|
+
return signature;
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
client.logger.error('Error in delegatedSign', error);
|
|
1574
|
+
throw error;
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
const delegatedSignMessage = async (client, { walletId, walletApiKey, keyShare, message, chainName, isFormatted = false, onError, context })=>{
|
|
1578
|
+
// Validate required parameters
|
|
1579
|
+
if (!keyShare) {
|
|
1580
|
+
throw new Error('Delegated key share is required to sign a message');
|
|
1581
|
+
}
|
|
1582
|
+
const wallet = await getWallet(client, {
|
|
1583
|
+
walletId
|
|
1584
|
+
});
|
|
1585
|
+
// Perform the dynamic server sign
|
|
1586
|
+
const data = await dynamicDelegatedSign(client, {
|
|
1587
|
+
walletId,
|
|
1588
|
+
walletApiKey,
|
|
1589
|
+
message,
|
|
1590
|
+
isFormatted,
|
|
1591
|
+
onError,
|
|
1592
|
+
context
|
|
1593
|
+
});
|
|
1594
|
+
const derivationPath = wallet.derivationPath && wallet.derivationPath !== '' ? new Uint32Array(Object.values(JSON.parse(wallet.derivationPath))) : undefined;
|
|
1595
|
+
// Perform the external server sign and return the signature
|
|
1596
|
+
const signature = await delegatedSign(client, {
|
|
1597
|
+
chainName,
|
|
1598
|
+
message,
|
|
1599
|
+
roomId: data.roomId,
|
|
1600
|
+
keyShare,
|
|
1601
|
+
derivationPath,
|
|
1602
|
+
isFormatted
|
|
1603
|
+
});
|
|
1604
|
+
return signature;
|
|
1605
|
+
};
|
|
1606
|
+
// Helper function to get wallet (to be implemented in wallet module)
|
|
1607
|
+
const getWallet = async (client, { walletId })=>{
|
|
1608
|
+
const wallets = client.wallets;
|
|
1609
|
+
// Check if wallet is already cached
|
|
1610
|
+
if (wallets[walletId]) {
|
|
1611
|
+
return wallets[walletId];
|
|
1612
|
+
}
|
|
1613
|
+
// Fetch wallet from API
|
|
1614
|
+
const apiClient = createDelegatedApiClient(client);
|
|
1615
|
+
const { wallet } = await apiClient.getWaasWalletById({
|
|
1616
|
+
walletId
|
|
1617
|
+
});
|
|
1618
|
+
const waasWallet = {
|
|
1619
|
+
walletId,
|
|
1620
|
+
chainName: wallet.chainName,
|
|
1621
|
+
accountAddress: wallet.accountAddress,
|
|
1622
|
+
externalServerKeyShares: [],
|
|
1623
|
+
derivationPath: wallet.derivationPath,
|
|
1624
|
+
thresholdSignatureScheme: wallet.thresholdSignatureScheme,
|
|
1625
|
+
externalServerKeySharesBackupInfo: {
|
|
1626
|
+
passwordEncrypted: false,
|
|
1627
|
+
backups: {
|
|
1628
|
+
dynamic: [],
|
|
1629
|
+
googleDrive: [],
|
|
1630
|
+
iCloud: [],
|
|
1631
|
+
user: [],
|
|
1632
|
+
external: [],
|
|
1633
|
+
delegated: []
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
// Cache the wallet
|
|
1638
|
+
wallets[walletId] = waasWallet;
|
|
1639
|
+
return waasWallet;
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
const revokeDelegation = async (client, walletId)=>{
|
|
1643
|
+
try {
|
|
1644
|
+
// TODO: Uncomment when API method is implemented
|
|
1645
|
+
// const apiClient = createDelegatedApiClient(client);
|
|
1646
|
+
// await apiClient.revokeDelegation({
|
|
1647
|
+
// walletId,
|
|
1648
|
+
// });
|
|
1649
|
+
client.logger.info(`Delegation revoked for wallet: ${walletId}`);
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
client.logger.error('Error revoking delegation', error);
|
|
1652
|
+
throw new Error('Failed to revoke delegation');
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
|
|
1656
|
+
Object.defineProperty(exports, "SOLANA_RPC_URL", {
|
|
1105
1657
|
enumerable: true,
|
|
1106
|
-
get: function () { return
|
|
1658
|
+
get: function () { return core.SOLANA_RPC_URL; }
|
|
1107
1659
|
});
|
|
1108
|
-
Object.defineProperty(exports, "
|
|
1660
|
+
Object.defineProperty(exports, "ThresholdSignatureScheme", {
|
|
1109
1661
|
enumerable: true,
|
|
1110
|
-
get: function () { return
|
|
1662
|
+
get: function () { return core.ThresholdSignatureScheme; }
|
|
1111
1663
|
});
|
|
1112
|
-
Object.defineProperty(exports, "
|
|
1664
|
+
Object.defineProperty(exports, "WalletOperation", {
|
|
1113
1665
|
enumerable: true,
|
|
1114
|
-
get: function () { return
|
|
1666
|
+
get: function () { return core.WalletOperation; }
|
|
1115
1667
|
});
|
|
1116
|
-
Object.defineProperty(exports, "
|
|
1668
|
+
Object.defineProperty(exports, "getMPCChainConfig", {
|
|
1117
1669
|
enumerable: true,
|
|
1118
|
-
get: function () { return
|
|
1670
|
+
get: function () { return core.getMPCChainConfig; }
|
|
1119
1671
|
});
|
|
1120
1672
|
exports.DynamicWalletClient = DynamicWalletClient;
|
|
1121
1673
|
exports.base64ToBytes = base64ToBytes;
|
|
1122
1674
|
exports.bytesToBase64 = bytesToBase64;
|
|
1675
|
+
exports.createDelegatedWalletClient = createDelegatedWalletClient;
|
|
1676
|
+
exports.delegatedSignMessage = delegatedSignMessage;
|
|
1123
1677
|
exports.ensureBase64Padding = ensureBase64Padding;
|
|
1678
|
+
exports.formatEvmMessage = formatEvmMessage;
|
|
1679
|
+
exports.formatMessage = formatMessage;
|
|
1124
1680
|
exports.getExternalServerKeyShareBackupInfo = getExternalServerKeyShareBackupInfo;
|
|
1125
1681
|
exports.getMPCSignatureScheme = getMPCSignatureScheme;
|
|
1126
1682
|
exports.getMPCSigner = getMPCSigner;
|
|
1127
1683
|
exports.isHexString = isHexString;
|
|
1128
1684
|
exports.mergeUniqueKeyShares = mergeUniqueKeyShares;
|
|
1129
1685
|
exports.retryPromise = retryPromise;
|
|
1686
|
+
exports.revokeDelegation = revokeDelegation;
|
|
1130
1687
|
exports.stringToBytes = stringToBytes;
|
|
1131
|
-
Object.keys(core).forEach(function (k) {
|
|
1688
|
+
Object.keys(core$1).forEach(function (k) {
|
|
1132
1689
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
1133
1690
|
enumerable: true,
|
|
1134
|
-
get: function () { return core[k]; }
|
|
1691
|
+
get: function () { return core$1[k]; }
|
|
1135
1692
|
});
|
|
1136
1693
|
});
|