@dynamic-labs-wallet/node 0.0.0-preview.160.0 → 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 +775 -233
- package/index.esm.js +763 -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/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/node/index.cjs +1 -0
- package/internal/node/index.d.ts +1 -1
- package/internal/node/native.cjs +1 -0
- package/internal/node/native.d.ts +1 -1
- package/package.json +12 -4
- 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 +82 -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/ed25519_exportable.js +0 -1
- package/internal/core/index.js +0 -1
- package/internal/core/sr25519.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);
|
|
@@ -700,15 +878,17 @@ class DynamicWalletClient {
|
|
|
700
878
|
throw new Error('Ceremony completion timeout');
|
|
701
879
|
}
|
|
702
880
|
}
|
|
703
|
-
async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined }) {
|
|
881
|
+
async storeEncryptedBackupByWallet({ accountAddress, externalServerKeyShares = undefined, password = undefined, backUpToClientShareService }) {
|
|
704
882
|
this.ensureApiClientAuthenticated();
|
|
705
883
|
//add retry logic for ceremony completion to prevent race condition
|
|
706
884
|
await this.ensureCeremonyCompletionBeforeBackup({
|
|
707
885
|
accountAddress
|
|
708
886
|
});
|
|
887
|
+
const dynamicRequestId = uuid.v4();
|
|
709
888
|
try {
|
|
710
889
|
const keySharesToBackup = externalServerKeyShares != null ? externalServerKeyShares : await this.getExternalServerKeyShares({
|
|
711
|
-
accountAddress
|
|
890
|
+
accountAddress,
|
|
891
|
+
password
|
|
712
892
|
});
|
|
713
893
|
if (!keySharesToBackup || keySharesToBackup.length === 0) {
|
|
714
894
|
throw new Error(`Key shares not found for accountAddress: ${accountAddress}`);
|
|
@@ -720,51 +900,92 @@ class DynamicWalletClient {
|
|
|
720
900
|
if (!this.walletMap[accountAddress] || !this.walletMap[accountAddress].walletId) {
|
|
721
901
|
throw new Error(`WalletId not found for accountAddress: ${accountAddress}`);
|
|
722
902
|
}
|
|
723
|
-
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({
|
|
724
925
|
walletId: this.walletMap[accountAddress].walletId,
|
|
725
|
-
|
|
726
|
-
|
|
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
|
+
}
|
|
727
940
|
});
|
|
728
941
|
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
729
|
-
externalServerKeySharesBackupInfo:
|
|
730
|
-
walletProperties: {
|
|
731
|
-
derivationPath: this.walletMap[accountAddress].derivationPath,
|
|
732
|
-
keyShares: data.keyShares,
|
|
733
|
-
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme
|
|
734
|
-
}
|
|
735
|
-
})
|
|
942
|
+
externalServerKeySharesBackupInfo: updatedBackupInfo
|
|
736
943
|
});
|
|
737
|
-
return
|
|
944
|
+
return encryptedKeyShares;
|
|
738
945
|
} catch (error) {
|
|
739
|
-
this.logger.error('Error in storeEncryptedBackupByWallet:',
|
|
946
|
+
this.logger.error('Error in storeEncryptedBackupByWallet:', {
|
|
947
|
+
accountAddress,
|
|
948
|
+
error,
|
|
949
|
+
dynamicRequestId
|
|
950
|
+
});
|
|
740
951
|
throw error;
|
|
741
952
|
}
|
|
742
953
|
}
|
|
743
|
-
async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password }) {
|
|
954
|
+
async storeEncryptedBackupByWalletWithRetry({ accountAddress, externalServerKeyShares, password, backUpToClientShareService }) {
|
|
744
955
|
await retryPromise(()=>this.storeEncryptedBackupByWallet({
|
|
745
956
|
accountAddress,
|
|
746
957
|
externalServerKeyShares,
|
|
747
|
-
password
|
|
958
|
+
password,
|
|
959
|
+
backUpToClientShareService
|
|
748
960
|
}), {
|
|
749
961
|
operationName: 'store encrypted backup',
|
|
750
962
|
logContext: {
|
|
751
|
-
walletAddress: accountAddress
|
|
752
|
-
keyShares: externalServerKeyShares == null ? void 0 : externalServerKeyShares.map((keyShare)=>this.encryptKeyShare({
|
|
753
|
-
keyShare,
|
|
754
|
-
password
|
|
755
|
-
}))
|
|
963
|
+
walletAddress: accountAddress
|
|
756
964
|
}
|
|
757
965
|
});
|
|
758
966
|
}
|
|
759
967
|
async getExternalServerKeyShares({ accountAddress, password }) {
|
|
968
|
+
var _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo;
|
|
760
969
|
const wallet = await this.getWallet({
|
|
761
970
|
accountAddress,
|
|
762
971
|
password,
|
|
763
972
|
walletOperation: core.WalletOperation.REACH_THRESHOLD
|
|
764
973
|
});
|
|
765
|
-
|
|
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;
|
|
766
987
|
}
|
|
767
|
-
async updatePassword({ accountAddress, existingPassword, newPassword }) {
|
|
988
|
+
async updatePassword({ accountAddress, existingPassword, newPassword, backUpToClientShareService }) {
|
|
768
989
|
await this.getWallet({
|
|
769
990
|
accountAddress,
|
|
770
991
|
password: existingPassword,
|
|
@@ -772,7 +993,8 @@ class DynamicWalletClient {
|
|
|
772
993
|
});
|
|
773
994
|
await this.storeEncryptedBackupByWallet({
|
|
774
995
|
accountAddress,
|
|
775
|
-
password: newPassword
|
|
996
|
+
password: newPassword,
|
|
997
|
+
backUpToClientShareService
|
|
776
998
|
});
|
|
777
999
|
}
|
|
778
1000
|
async decryptKeyShare({ keyShare, password }) {
|
|
@@ -803,17 +1025,57 @@ class DynamicWalletClient {
|
|
|
803
1025
|
if (shareCount !== undefined) {
|
|
804
1026
|
requiredShareCount = shareCount;
|
|
805
1027
|
}
|
|
806
|
-
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;
|
|
807
1033
|
return {
|
|
808
1034
|
shares: {
|
|
809
|
-
[
|
|
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
|
+
})
|
|
810
1039
|
},
|
|
811
1040
|
requiredShareCount
|
|
812
1041
|
};
|
|
813
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
|
+
}
|
|
814
1068
|
async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, shareCount = undefined, storeRecoveredShares = true }) {
|
|
1069
|
+
var _wallet_externalServerKeySharesBackupInfo;
|
|
815
1070
|
this.ensureApiClientAuthenticated();
|
|
816
|
-
|
|
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
|
+
}
|
|
817
1079
|
this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
|
|
818
1080
|
const { shares } = this.recoverStrategy({
|
|
819
1081
|
externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
|
|
@@ -821,12 +1083,30 @@ class DynamicWalletClient {
|
|
|
821
1083
|
walletOperation,
|
|
822
1084
|
shareCount
|
|
823
1085
|
});
|
|
824
|
-
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
+
}
|
|
829
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
|
+
}
|
|
830
1110
|
const decryptedKeyShares = await Promise.all(dynamicKeyShares.map((keyShare)=>this.decryptKeyShare({
|
|
831
1111
|
keyShare: keyShare.encryptedAccountCredential,
|
|
832
1112
|
password: password != null ? password : this.environmentId
|
|
@@ -856,6 +1136,7 @@ class DynamicWalletClient {
|
|
|
856
1136
|
* @param walletOperation - The wallet operation that determines required fields
|
|
857
1137
|
* @returns boolean indicating if wallet needs to be re-fetched and restored from server
|
|
858
1138
|
*/ async checkWalletFields({ accountAddress, walletOperation = core.WalletOperation.REACH_THRESHOLD, shareCount }) {
|
|
1139
|
+
var _existingWallet_externalServerKeyShares;
|
|
859
1140
|
let keyshareCheck = false;
|
|
860
1141
|
let walletCheck = false;
|
|
861
1142
|
let thresholdSignatureSchemeCheck = false;
|
|
@@ -875,7 +1156,7 @@ class DynamicWalletClient {
|
|
|
875
1156
|
}
|
|
876
1157
|
// check if wallet already exists with sufficient keyshares
|
|
877
1158
|
if (existingWallet) {
|
|
878
|
-
var
|
|
1159
|
+
var _existingWallet_externalServerKeyShares1;
|
|
879
1160
|
const { shares } = this.recoverStrategy({
|
|
880
1161
|
externalServerKeySharesBackupInfo: existingWallet.externalServerKeySharesBackupInfo || {
|
|
881
1162
|
backups: getExternalServerKeyShareBackupInfo()
|
|
@@ -885,11 +1166,35 @@ class DynamicWalletClient {
|
|
|
885
1166
|
shareCount
|
|
886
1167
|
});
|
|
887
1168
|
const { dynamic: requiredDynamicKeyShareIds = [] } = shares;
|
|
888
|
-
|
|
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) {
|
|
889
1184
|
keyshareCheck = true;
|
|
890
1185
|
}
|
|
891
1186
|
}
|
|
892
|
-
|
|
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;
|
|
893
1198
|
}
|
|
894
1199
|
/**
|
|
895
1200
|
* verifyPassword attempts to recover and decrypt a single client key share using the provided password.
|
|
@@ -915,8 +1220,10 @@ class DynamicWalletClient {
|
|
|
915
1220
|
accountAddress
|
|
916
1221
|
});
|
|
917
1222
|
const { dynamic: dynamicKeyShareIds = [] } = backups;
|
|
918
|
-
|
|
919
|
-
|
|
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');
|
|
920
1227
|
}
|
|
921
1228
|
try {
|
|
922
1229
|
await this.recoverEncryptedBackupByWallet({
|
|
@@ -961,88 +1268,164 @@ class DynamicWalletClient {
|
|
|
961
1268
|
if (walletOperation === core.WalletOperation.REACH_ALL_PARTIES || walletOperation === core.WalletOperation.REFRESH || walletOperation === core.WalletOperation.RESHARE) {
|
|
962
1269
|
return true;
|
|
963
1270
|
}
|
|
964
|
-
const { requiredShareCount } = this.recoverStrategy({
|
|
1271
|
+
const { shares, requiredShareCount } = this.recoverStrategy({
|
|
965
1272
|
externalServerKeySharesBackupInfo,
|
|
966
1273
|
thresholdSignatureScheme: this.walletMap[accountAddress].thresholdSignatureScheme,
|
|
967
1274
|
walletOperation
|
|
968
1275
|
});
|
|
969
|
-
|
|
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) {
|
|
970
1281
|
return false;
|
|
971
1282
|
}
|
|
972
1283
|
return true;
|
|
973
1284
|
}
|
|
974
1285
|
async getWalletExternalServerKeyShareBackupInfo({ accountAddress }) {
|
|
975
|
-
var
|
|
1286
|
+
var _wallet_externalServerKeySharesBackupInfo_backups_BackupLocation_DYNAMIC, _wallet_externalServerKeySharesBackupInfo_backups, _wallet_externalServerKeySharesBackupInfo, _user_verifiedCredentials;
|
|
976
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
|
+
}
|
|
977
1296
|
// Return existing backup info if it exists
|
|
978
|
-
if (((
|
|
979
|
-
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;
|
|
980
1299
|
}
|
|
981
1300
|
// Get backup info from server
|
|
982
|
-
const user = await this.apiClient.getUser();
|
|
983
|
-
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());
|
|
984
1303
|
return getExternalServerKeyShareBackupInfo({
|
|
985
|
-
walletProperties:
|
|
1304
|
+
walletProperties: walletData == null ? void 0 : walletData.walletProperties
|
|
986
1305
|
});
|
|
987
1306
|
}
|
|
988
1307
|
async getWallet({ accountAddress, walletOperation = core.WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined }) {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
accountAddress,
|
|
993
|
-
walletOperation,
|
|
994
|
-
shareCount
|
|
995
|
-
});
|
|
996
|
-
if (existingWalletCheck) {
|
|
997
|
-
this.logger.debug(`Wallet ${accountAddress} already exists`);
|
|
998
|
-
return this.walletMap[accountAddress];
|
|
999
|
-
}
|
|
1000
|
-
//todo: Question - why don't we just call getWallets here? so then all are preloaded
|
|
1001
|
-
// Fetch and restore wallet from server
|
|
1002
|
-
const user = await this.apiClient.getUser();
|
|
1003
|
-
const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>vc.address.toLowerCase() === accountAddress.toLowerCase());
|
|
1004
|
-
this.logger.debug('Restoring wallet', wallet);
|
|
1005
|
-
const walletProperties = wallet.walletProperties;
|
|
1006
|
-
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
1007
|
-
walletId: wallet.id,
|
|
1008
|
-
chainName: wallet.chainName,
|
|
1009
|
-
accountAddress,
|
|
1010
|
-
thresholdSignatureScheme: walletProperties.thresholdSignatureScheme,
|
|
1011
|
-
derivationPath: walletProperties.derivationPath,
|
|
1012
|
-
externalServerKeySharesBackupInfo: getExternalServerKeyShareBackupInfo({
|
|
1013
|
-
walletProperties
|
|
1014
|
-
})
|
|
1015
|
-
});
|
|
1016
|
-
if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
1017
|
-
accountAddress,
|
|
1018
|
-
walletOperation
|
|
1019
|
-
})) {
|
|
1020
|
-
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
1308
|
+
try {
|
|
1309
|
+
this.ensureApiClientAuthenticated();
|
|
1310
|
+
const existingWalletCheck = await this.checkWalletFields({
|
|
1021
1311
|
accountAddress,
|
|
1022
|
-
|
|
1023
|
-
walletOperation: walletOperation,
|
|
1312
|
+
walletOperation,
|
|
1024
1313
|
shareCount
|
|
1025
1314
|
});
|
|
1026
|
-
|
|
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;
|
|
1027
1351
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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];
|
|
1031
1360
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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;
|
|
1035
1416
|
}
|
|
1036
|
-
return this.walletMap[accountAddress];
|
|
1037
1417
|
}
|
|
1038
|
-
|
|
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() {
|
|
1039
1422
|
var _user_verifiedCredentials;
|
|
1040
1423
|
this.ensureApiClientAuthenticated();
|
|
1041
|
-
const user = await this.apiClient.getUser();
|
|
1424
|
+
const user = await this.apiClient.getUser(uuid.v4());
|
|
1042
1425
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
1043
1426
|
const wallets = waasWallets.map((vc)=>{
|
|
1044
|
-
var _this_walletMap_vc_address,
|
|
1045
|
-
var
|
|
1427
|
+
var _this_walletMap_vc_address, _vc_walletProperties, _vc_walletProperties1;
|
|
1428
|
+
var _vc_walletProperties_derivationPath;
|
|
1046
1429
|
return {
|
|
1047
1430
|
walletId: vc.id,
|
|
1048
1431
|
chainName: vc.chain,
|
|
@@ -1051,101 +1434,260 @@ class DynamicWalletClient {
|
|
|
1051
1434
|
walletProperties: vc.walletProperties || {}
|
|
1052
1435
|
}),
|
|
1053
1436
|
externalServerKeyShares: ((_this_walletMap_vc_address = this.walletMap[vc.address]) == null ? void 0 : _this_walletMap_vc_address.externalServerKeyShares) || [],
|
|
1054
|
-
derivationPath: (
|
|
1055
|
-
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
|
|
1056
1439
|
};
|
|
1057
1440
|
});
|
|
1058
1441
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
1059
|
-
var _acc_accountAddress;
|
|
1060
|
-
const accountAddress = wallet.accountAddress;
|
|
1061
1442
|
acc[wallet.accountAddress] = {
|
|
1062
1443
|
walletId: wallet.walletId,
|
|
1063
1444
|
chainName: wallet.chainName,
|
|
1064
1445
|
accountAddress: wallet.accountAddress,
|
|
1065
1446
|
externalServerKeyShares: wallet.externalServerKeyShares || [],
|
|
1066
1447
|
externalServerKeySharesBackupInfo: wallet.externalServerKeySharesBackupInfo,
|
|
1067
|
-
derivationPath:
|
|
1448
|
+
derivationPath: wallet.derivationPath,
|
|
1068
1449
|
thresholdSignatureScheme: wallet.thresholdSignatureScheme
|
|
1069
1450
|
};
|
|
1070
1451
|
return acc;
|
|
1071
1452
|
}, {});
|
|
1072
1453
|
return wallets;
|
|
1073
1454
|
}
|
|
1074
|
-
constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug }){
|
|
1455
|
+
constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, debug, forwardMPCClient, enableMPCAccelerator = false }){
|
|
1075
1456
|
this.logger = logger;
|
|
1076
1457
|
this.walletMap = {} // todo: store in session storage
|
|
1077
1458
|
;
|
|
1078
1459
|
this.isApiClientAuthenticated = false;
|
|
1460
|
+
this.forwardMPCEnabled = false;
|
|
1079
1461
|
this.environmentId = environmentId;
|
|
1080
1462
|
this.baseMPCRelayApiUrl = baseMPCRelayApiUrl;
|
|
1081
1463
|
this.baseApiUrl = baseApiUrl;
|
|
1082
1464
|
this.debug = Boolean(debug);
|
|
1083
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
|
+
}
|
|
1084
1477
|
}
|
|
1085
1478
|
}
|
|
1086
1479
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
})
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
});
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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", {
|
|
1120
1657
|
enumerable: true,
|
|
1121
|
-
get: function () { return
|
|
1658
|
+
get: function () { return core.SOLANA_RPC_URL; }
|
|
1122
1659
|
});
|
|
1123
|
-
Object.defineProperty(exports, "
|
|
1660
|
+
Object.defineProperty(exports, "ThresholdSignatureScheme", {
|
|
1124
1661
|
enumerable: true,
|
|
1125
|
-
get: function () { return
|
|
1662
|
+
get: function () { return core.ThresholdSignatureScheme; }
|
|
1126
1663
|
});
|
|
1127
|
-
Object.defineProperty(exports, "
|
|
1664
|
+
Object.defineProperty(exports, "WalletOperation", {
|
|
1128
1665
|
enumerable: true,
|
|
1129
|
-
get: function () { return
|
|
1666
|
+
get: function () { return core.WalletOperation; }
|
|
1130
1667
|
});
|
|
1131
|
-
Object.defineProperty(exports, "
|
|
1668
|
+
Object.defineProperty(exports, "getMPCChainConfig", {
|
|
1132
1669
|
enumerable: true,
|
|
1133
|
-
get: function () { return
|
|
1670
|
+
get: function () { return core.getMPCChainConfig; }
|
|
1134
1671
|
});
|
|
1135
1672
|
exports.DynamicWalletClient = DynamicWalletClient;
|
|
1136
1673
|
exports.base64ToBytes = base64ToBytes;
|
|
1137
1674
|
exports.bytesToBase64 = bytesToBase64;
|
|
1675
|
+
exports.createDelegatedWalletClient = createDelegatedWalletClient;
|
|
1676
|
+
exports.delegatedSignMessage = delegatedSignMessage;
|
|
1138
1677
|
exports.ensureBase64Padding = ensureBase64Padding;
|
|
1678
|
+
exports.formatEvmMessage = formatEvmMessage;
|
|
1679
|
+
exports.formatMessage = formatMessage;
|
|
1139
1680
|
exports.getExternalServerKeyShareBackupInfo = getExternalServerKeyShareBackupInfo;
|
|
1140
1681
|
exports.getMPCSignatureScheme = getMPCSignatureScheme;
|
|
1141
1682
|
exports.getMPCSigner = getMPCSigner;
|
|
1142
1683
|
exports.isHexString = isHexString;
|
|
1143
1684
|
exports.mergeUniqueKeyShares = mergeUniqueKeyShares;
|
|
1144
1685
|
exports.retryPromise = retryPromise;
|
|
1686
|
+
exports.revokeDelegation = revokeDelegation;
|
|
1145
1687
|
exports.stringToBytes = stringToBytes;
|
|
1146
|
-
Object.keys(core).forEach(function (k) {
|
|
1688
|
+
Object.keys(core$1).forEach(function (k) {
|
|
1147
1689
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
1148
1690
|
enumerable: true,
|
|
1149
|
-
get: function () { return core[k]; }
|
|
1691
|
+
get: function () { return core$1[k]; }
|
|
1150
1692
|
});
|
|
1151
1693
|
});
|