@dynamic-labs-wallet/browser 0.0.156 → 0.0.157

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.esm.js CHANGED
@@ -2,13 +2,14 @@ import { SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode,
2
2
  export * from '@dynamic-labs-wallet/core';
3
3
  import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from './internal/web';
4
4
  export { BIP340, BIP340InitKeygenResult, BIP340KeygenResult, Ecdsa, EcdsaInitKeygenResult, EcdsaKeygenResult, EcdsaPublicKey, EcdsaSignature, Ed25519, Ed25519InitKeygenResult, Ed25519KeygenResult, MessageHash } from './internal/web';
5
- import { v4 } from 'uuid';
6
5
  import { gte } from 'semver';
7
- import { LogLevel, Logger } from '@dynamic-labs/logger';
6
+ import { v4 } from 'uuid';
7
+ import { Logger, LogLevel } from '@dynamic-labs/logger';
8
8
  export { Logger } from '@dynamic-labs/logger';
9
- import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
9
+ import loadArgon2idWasm from 'argon2id';
10
10
  import { AxiosError } from 'axios';
11
11
  import createHttpError from 'http-errors';
12
+ import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
12
13
 
13
14
  function _extends() {
14
15
  _extends = Object.assign || function assign(target) {
@@ -42,65 +43,118 @@ const getMPCSigner = ({ chainName, baseRelayUrl })=>{
42
43
  return signatureScheme;
43
44
  };
44
45
 
45
- const DEFAULT_LOG_LEVEL = LogLevel.DEBUG; //todo: change back to info when done debugging
46
- const STORAGE_KEY = 'dynamic-waas-wallet-client';
47
- const CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX = 'dynamicWalletKeyShareBackup';
48
- const SIGNED_SESSION_ID_MIN_VERSION = '4.25.4';
49
- // Namespace-specific version requirements
50
- const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
51
- WalletKit: '4.25.4',
52
- ClientSDK: '0.1.0-alpha.0'
53
- };
46
+ /**
47
+ * Encryption version identifiers
48
+ */ var EncryptionVersion = /*#__PURE__*/ function(EncryptionVersion) {
49
+ EncryptionVersion["V1_LEGACY"] = "v1";
50
+ EncryptionVersion["V2_PBKDF2"] = "v2";
51
+ EncryptionVersion["V3_ARGON2"] = "v3";
52
+ return EncryptionVersion;
53
+ }({});
54
+ /**
55
+ * Current default version for new encryptions
56
+ */ const ENCRYPTION_VERSION_CURRENT = "v3";
57
+ /**
58
+ * Algorithm constants
59
+ */ const PBKDF2_ALGORITHM = 'PBKDF2';
60
+ const HASH_ALGORITHM = 'SHA-256'; // Generic hash algorithm constant
61
+ const ARGON2_ALGORITHM = 'Argon2id';
62
+ const AES_GCM_ALGORITHM = 'AES-GCM';
63
+ const AES_GCM_LENGTH = 256;
64
+ /**
65
+ * Argon2 configuration constants, values were chosen based on RFC (https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice)
66
+ * taking into account that this runs in the client, possibly in smartphones with limited resources
67
+ */ const ARGON2_MEMORY_SIZE = 65536; // 64 MB in KiB
68
+ const ARGON2_ITERATIONS = 3;
69
+ const ARGON2_PARALLELISM = 2;
70
+ const ARGON2_HASH_LENGTH = 32;
71
+ /**
72
+ * PBKDF2 configuration constants
73
+ */ const PBKDF2_ITERATIONS_V1 = 100000;
74
+ const PBKDF2_ITERATIONS_V2 = 1000000;
54
75
 
55
- const handleAxiosError = (error, message, context)=>{
56
- var _error_response, _error_response1;
57
- logger.debug("[DynamicWaasWalletClient] Axios error: " + message, {
58
- error: (_error_response = error.response) == null ? void 0 : _error_response.status,
59
- message: error.message,
60
- context
76
+ /**
77
+ * Derives a key using Argon2id algorithm
78
+ * @param params - Key derivation parameters
79
+ * @param encryptionConfig - Encryption configuration
80
+ * @returns Promise<CryptoKey>
81
+ */ const deriveArgon2Key = async ({ password, salt }, encryptionConfig)=>{
82
+ const argon2id = await loadArgon2idWasm();
83
+ const argon2Config = encryptionConfig;
84
+ const passwordBytes = new TextEncoder().encode(password);
85
+ const hash = argon2id({
86
+ password: passwordBytes,
87
+ salt: salt,
88
+ parallelism: argon2Config.parallelism || ARGON2_PARALLELISM,
89
+ passes: argon2Config.iterations,
90
+ memorySize: argon2Config.memorySize || ARGON2_MEMORY_SIZE,
91
+ tagLength: argon2Config.hashLength || ARGON2_HASH_LENGTH
61
92
  });
62
- switch((_error_response1 = error.response) == null ? void 0 : _error_response1.status){
63
- case 400:
64
- throw createHttpError(400, 'Invalid request');
65
- case 401:
66
- throw createHttpError(401, 'Authorization header or cookie is required');
67
- case 403:
68
- throw createHttpError(403, 'Forbidden');
69
- case 422:
70
- throw createHttpError(422, 'Unprocessable content');
71
- case 500:
72
- throw createHttpError(500, 'Internal server error');
73
- default:
74
- throw createHttpError(500, 'Internal server error');
75
- }
93
+ return crypto.subtle.importKey('raw', new Uint8Array(hash), {
94
+ name: encryptionConfig.algorithm,
95
+ length: encryptionConfig.algorithmLength
96
+ }, false, [
97
+ 'encrypt',
98
+ 'decrypt'
99
+ ]);
76
100
  };
77
101
 
78
- const logger = new Logger('DynamicWaasWalletClient', LogLevel.DEBUG);
79
- const setLoggerContext = ({ environmentId, authMode = AuthMode.HEADER, sessionId = undefined, userId = undefined })=>{
80
- try {
81
- Logger.setEnvironmentId(environmentId);
82
- Logger.globalMetaData.set('sid', sessionId);
83
- Logger.globalMetaData.set('user_id', userId);
84
- Logger.globalMetaData.set('auth_mode', authMode);
85
- } catch (error) {
86
- logError({
87
- message: '[DynamicWaasWalletClient] Error setting logger context',
88
- error: error,
89
- context: {}
90
- });
102
+ /**
103
+ * Encryption configuration for each version
104
+ */ const ENCRYPTION_VERSIONS = {
105
+ [EncryptionVersion.V1_LEGACY]: {
106
+ version: EncryptionVersion.V1_LEGACY,
107
+ algorithm: AES_GCM_ALGORITHM,
108
+ keyDerivation: PBKDF2_ALGORITHM,
109
+ iterations: PBKDF2_ITERATIONS_V1,
110
+ hashAlgorithm: HASH_ALGORITHM,
111
+ algorithmLength: AES_GCM_LENGTH
112
+ },
113
+ [EncryptionVersion.V2_PBKDF2]: {
114
+ version: EncryptionVersion.V2_PBKDF2,
115
+ algorithm: AES_GCM_ALGORITHM,
116
+ keyDerivation: PBKDF2_ALGORITHM,
117
+ iterations: PBKDF2_ITERATIONS_V2,
118
+ hashAlgorithm: HASH_ALGORITHM,
119
+ algorithmLength: AES_GCM_LENGTH
120
+ },
121
+ [EncryptionVersion.V3_ARGON2]: {
122
+ version: EncryptionVersion.V3_ARGON2,
123
+ algorithm: AES_GCM_ALGORITHM,
124
+ keyDerivation: ARGON2_ALGORITHM,
125
+ iterations: ARGON2_ITERATIONS,
126
+ hashAlgorithm: ARGON2_ALGORITHM,
127
+ algorithmLength: AES_GCM_LENGTH,
128
+ memorySize: ARGON2_MEMORY_SIZE,
129
+ parallelism: ARGON2_PARALLELISM,
130
+ hashLength: ARGON2_HASH_LENGTH
91
131
  }
92
132
  };
93
- const logError = ({ message, error, context })=>{
94
- if (error instanceof AxiosError) {
95
- handleAxiosError(error, message, context);
133
+ /**
134
+ * Helper function to get encryption configuration by version string
135
+ * @param version - The version string (e.g., 'v1', 'v2', 'v3')
136
+ * @returns The encryption configuration for the specified version
137
+ */ const getEncryptionConfig = (version)=>{
138
+ // If no version specified, use legacy for backward compatibility
139
+ if (!version) {
140
+ return ENCRYPTION_VERSIONS[EncryptionVersion.V1_LEGACY];
141
+ }
142
+ const config = ENCRYPTION_VERSIONS[version];
143
+ if (!config) {
144
+ throw new Error(`Unsupported encryption version: ${version}`);
96
145
  }
97
- logger.error(message, {
98
- error: error instanceof Error ? error.message : 'Unknown error',
99
- context
100
- });
146
+ return config;
147
+ };
148
+ /**
149
+ * Check if a configuration uses Argon2
150
+ */ const isArgon2Config = (config)=>{
151
+ return config.keyDerivation === ARGON2_ALGORITHM;
101
152
  };
102
153
 
103
- const bytesToBase64 = (arr)=>{
154
+ /**
155
+ * Utility functions for encryption operations
156
+ * These functions are separated to avoid circular dependencies
157
+ */ const bytesToBase64 = (arr)=>{
104
158
  return btoa(Array.from(arr, (b)=>String.fromCharCode(b)).join(''));
105
159
  };
106
160
  const stringToBytes = (str)=>{
@@ -113,168 +167,13 @@ const base64ToBytes = (base64)=>{
113
167
  const ensureBase64Padding = (str)=>{
114
168
  return str.padEnd(Math.ceil(str.length / 4) * 4, '=');
115
169
  };
116
- const isBrowser = ()=>typeof window !== 'undefined';
117
- const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
118
- return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
119
- };
120
- const getClientKeyShareBackupInfo = (params)=>{
121
- var _params_walletProperties, _params_walletProperties_keyShares_;
122
- const backups = {
123
- [BackupLocation.DYNAMIC]: [],
124
- [BackupLocation.GOOGLE_DRIVE]: [],
125
- [BackupLocation.ICLOUD]: [],
126
- [BackupLocation.USER]: [],
127
- [BackupLocation.EXTERNAL]: []
128
- };
129
- if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
130
- return {
131
- backups,
132
- passwordEncrypted: false
133
- };
134
- }
135
- params.walletProperties.keyShares.forEach((keyShare)=>{
136
- if (backups[keyShare.backupLocation]) {
137
- backups[keyShare.backupLocation].push({
138
- location: keyShare.backupLocation,
139
- keyShareId: keyShare.id,
140
- externalKeyShareId: keyShare == null ? void 0 : keyShare.externalKeyShareId
141
- });
142
- }
143
- });
144
- const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
145
- return {
146
- backups,
147
- passwordEncrypted
148
- };
149
- };
150
- /**
151
- * Helper function to merge keyshares and remove duplicates based on pubkey and secretShare
152
- * @param existingKeyShares - Array of existing keyshares
153
- * @param newKeyShares - Array of new keyshares to merge
154
- * @returns Array of merged unique keyshares
155
- */ const mergeUniqueKeyShares = (existingKeyShares, newKeyShares)=>{
156
- const uniqueKeyShares = newKeyShares.filter((newShare)=>!existingKeyShares.some((existingShare)=>{
157
- if (!(newShare == null ? void 0 : newShare.pubkey) || !(existingShare == null ? void 0 : existingShare.pubkey)) return false;
158
- return newShare.pubkey.toString() === existingShare.pubkey.toString() && newShare.secretShare === existingShare.secretShare;
159
- }));
160
- return [
161
- ...existingKeyShares,
162
- ...uniqueKeyShares
163
- ];
164
- };
165
- const timeoutPromise = ({ timeInMs, activity = 'Ceremony' })=>{
166
- return new Promise((_, reject)=>setTimeout(()=>reject(new Error(`${activity} did not complete in ${timeInMs}ms`)), timeInMs));
167
- };
168
- /**
169
- * Generic helper function to retry a promise-based operations
170
- *
171
- * @param operation - The async operation to retry
172
- * @param config - Configuration options for retry behavior
173
- * @returns Promise with the operation result
174
- * @throws Last error encountered after all retries are exhausted
175
- */ async function retryPromise(operation, { maxAttempts = 5, retryInterval = 500, operationName = 'operation', logContext = {} } = {}) {
176
- let attempts = 0;
177
- while(attempts < maxAttempts){
178
- try {
179
- return await operation();
180
- } catch (error) {
181
- attempts++;
182
- if (attempts === maxAttempts) {
183
- logger.error(`Failed to execute ${operationName} after ${maxAttempts} attempts`, _extends({}, logContext, {
184
- error: error instanceof Error ? error.message : 'Unknown error'
185
- }));
186
- throw error;
187
- }
188
- // Calculate exponential backoff delay
189
- const exponentialDelay = retryInterval * 2 ** (attempts - 1);
190
- await new Promise((resolve)=>setTimeout(resolve, exponentialDelay));
191
- }
192
- }
193
- // TypeScript needs this even though it's unreachable
194
- throw new Error('Unreachable code');
195
- }
196
- const formatEvmMessage = (message)=>{
197
- if (typeof message === 'string' && message.startsWith('0x')) {
198
- const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
199
- return MessageHash.keccak256(serializedTxBytes);
200
- }
201
- return MessageHash.keccak256(message);
202
- };
203
- const isHexString = (str)=>{
204
- // Remove 0x prefix if present
205
- const hex = str.startsWith('0x') ? str.slice(2) : str;
206
- // Check if string contains only hex characters
207
- return /^[0-9A-Fa-f]+$/.test(hex);
208
- };
209
- const formatSolanaMessage = (message)=>{
210
- if (typeof message === 'string') {
211
- if (!isHexString(message)) {
212
- return Buffer.from(message).toString('hex');
213
- } else {
214
- return new Uint8Array(Buffer.from(message, 'hex'));
215
- }
216
- } else {
217
- return message;
218
- }
219
- };
220
- const formatMessage = (chainName, message)=>{
221
- switch(chainName){
222
- case 'EVM':
223
- return formatEvmMessage(message);
224
- case 'SVM':
225
- return formatSolanaMessage(message);
226
- case 'SUI':
227
- return message;
228
- default:
229
- throw new Error('Unsupported chain name');
230
- }
231
- };
232
- const getGoogleOAuthAccountId = (verifiedCredentials)=>{
233
- const googleVerifiedCredential = verifiedCredentials == null ? void 0 : verifiedCredentials.find((credential)=>credential.oauthProvider === ProviderEnum.Google);
234
- return googleVerifiedCredential == null ? void 0 : googleVerifiedCredential.id;
235
- };
236
- const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatureScheme, hasPassword = true })=>{
237
- return {
238
- keyShares: encryptedKeyShares,
239
- metadata: {
240
- createdAt: new Date().toISOString(),
241
- accountAddress,
242
- thresholdSignatureScheme,
243
- hasPassword,
244
- encryption: getEncryptionMetadataForVersion(ENCRYPTION_VERSION_CURRENT),
245
- encryptionVersion: ENCRYPTION_VERSION_CURRENT,
246
- shareCount: encryptedKeyShares.length
247
- }
248
- };
249
- };
250
170
 
251
- const ENCRYPTION_VERSION_LEGACY = 'v1';
252
- const ENCRYPTION_VERSION_CURRENT = 'v2';
253
- const PBKDF2_ALGORITHM = 'PBKDF2';
254
- const PBKDF2_HASH_ALGORITHM = 'SHA-256';
255
- const AES_GCM_ALGORITHM = 'AES-GCM';
256
- const AES_GCM_LENGTH = 256;
257
- const ENCRYPTION_VERSIONS = {
258
- [ENCRYPTION_VERSION_LEGACY]: {
259
- version: ENCRYPTION_VERSION_LEGACY,
260
- algorithm: AES_GCM_ALGORITHM,
261
- keyDerivation: PBKDF2_ALGORITHM,
262
- iterations: 100000,
263
- hashAlgorithm: PBKDF2_HASH_ALGORITHM,
264
- algorithmLength: AES_GCM_LENGTH
265
- },
266
- [ENCRYPTION_VERSION_CURRENT]: {
267
- version: ENCRYPTION_VERSION_CURRENT,
268
- algorithm: AES_GCM_ALGORITHM,
269
- keyDerivation: PBKDF2_ALGORITHM,
270
- iterations: 1000000,
271
- hashAlgorithm: PBKDF2_HASH_ALGORITHM,
272
- algorithmLength: AES_GCM_LENGTH
273
- }
274
- };
275
- ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_CURRENT].iterations;
276
- ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY].iterations;
277
- const getKey = async ({ password, salt, encryptionConfig })=>{
171
+ /**
172
+ * Derives a key using PBKDF2 algorithm
173
+ * @param params - Key derivation parameters
174
+ * @param encryptionConfig - Encryption configuration
175
+ * @returns Promise<CryptoKey>
176
+ */ const derivePBKDF2Key = async ({ password, salt }, encryptionConfig)=>{
278
177
  const passwordBytes = stringToBytes(password);
279
178
  const initialKey = await crypto.subtle.importKey('raw', passwordBytes, {
280
179
  name: 'PBKDF2'
@@ -294,23 +193,30 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
294
193
  'decrypt'
295
194
  ]);
296
195
  };
196
+
197
+ /**
198
+ * Get the appropriate key derivation function based on the encryption config
199
+ */ const getKey = async (params, encryptionConfig)=>{
200
+ // Use Argon2 for v3, PBKDF2 for v1 and v2
201
+ if (encryptionConfig.keyDerivation === ARGON2_ALGORITHM) {
202
+ return deriveArgon2Key(params, encryptionConfig);
203
+ } else {
204
+ return derivePBKDF2Key(params, encryptionConfig);
205
+ }
206
+ };
297
207
  /**
298
208
  * Encrypts data using the specified encryption version.
299
209
  * Always uses the latest encryption configuration for new encryptions by default.
300
210
  */ const encryptData = async ({ data, password, version = ENCRYPTION_VERSION_CURRENT })=>{
301
- const encryptionConfig = ENCRYPTION_VERSIONS[version];
302
- if (!encryptionConfig) {
303
- throw new Error(`Unsupported encryption version: ${version}`);
304
- }
211
+ const encryptionConfig = getEncryptionConfig(version);
305
212
  try {
306
213
  // Generate a random salt and IV
307
214
  const salt = crypto.getRandomValues(new Uint8Array(16));
308
215
  const iv = crypto.getRandomValues(new Uint8Array(12)); // AES-GCM requires 12 bytes
309
216
  const key = await getKey({
310
217
  password,
311
- salt,
312
- encryptionConfig
313
- });
218
+ salt
219
+ }, encryptionConfig);
314
220
  // Convert the input string to bytes
315
221
  const dataBytes = new TextEncoder().encode(data);
316
222
  // Encrypt the data
@@ -342,20 +248,13 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
342
248
  const saltBytes = base64ToBytes(paddedSalt);
343
249
  const ivBytes = base64ToBytes(paddedIv);
344
250
  const cipherBytes = base64ToBytes(paddedCipher);
345
- let encryptionConfig;
346
- // Use version-based configuration if available, otherwise fallback to legacy
347
- if (version && ENCRYPTION_VERSIONS[version]) {
348
- encryptionConfig = ENCRYPTION_VERSIONS[version];
349
- } else {
350
- // Fallback to legacy version for backward compatibility
351
- encryptionConfig = ENCRYPTION_VERSIONS[ENCRYPTION_VERSION_LEGACY];
352
- }
251
+ // Determine which encryption configuration to use
252
+ const encryptionConfig = getEncryptionConfig(version);
353
253
  try {
354
254
  const key = await getKey({
355
255
  password,
356
- salt: saltBytes,
357
- encryptionConfig
358
- });
256
+ salt: saltBytes
257
+ }, encryptionConfig);
359
258
  const decryptedData = await crypto.subtle.decrypt({
360
259
  name: AES_GCM_ALGORITHM,
361
260
  iv: ivBytes
@@ -369,17 +268,21 @@ const getKey = async ({ password, salt, encryptionConfig })=>{
369
268
  * Gets encryption metadata for a specific version.
370
269
  * Used when we need to include metadata in legacy systems or APIs that require it.
371
270
  */ const getEncryptionMetadataForVersion = (version)=>{
372
- const encryptionConfig = ENCRYPTION_VERSIONS[version];
373
- if (!encryptionConfig) {
374
- throw new Error(`Unsupported encryption version: ${version}`);
375
- }
376
- return {
271
+ const encryptionConfig = getEncryptionConfig(version);
272
+ const metadata = {
377
273
  algorithm: encryptionConfig.algorithm,
378
274
  keyDerivation: encryptionConfig.keyDerivation,
379
275
  iterations: encryptionConfig.iterations,
380
276
  hashAlgorithm: encryptionConfig.hashAlgorithm,
381
277
  algorithmLength: encryptionConfig.algorithmLength
382
278
  };
279
+ // Add Argon2-specific metadata if applicable
280
+ if (isArgon2Config(encryptionConfig)) {
281
+ metadata.memorySize = encryptionConfig.memorySize;
282
+ metadata.parallelism = encryptionConfig.parallelism;
283
+ metadata.hashLength = encryptionConfig.hashLength;
284
+ }
285
+ return metadata;
383
286
  };
384
287
 
385
288
  const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
@@ -478,6 +381,199 @@ const downloadFileFromGoogleDrive = async ({ accessToken, fileName })=>{
478
381
  }
479
382
  };
480
383
 
384
+ const handleAxiosError = (error, message, context)=>{
385
+ var _error_response, _error_response1;
386
+ logger.debug("[DynamicWaasWalletClient] Axios error: " + message, {
387
+ error: (_error_response = error.response) == null ? void 0 : _error_response.status,
388
+ message: error.message,
389
+ context
390
+ });
391
+ switch((_error_response1 = error.response) == null ? void 0 : _error_response1.status){
392
+ case 400:
393
+ throw createHttpError(400, 'Invalid request');
394
+ case 401:
395
+ throw createHttpError(401, 'Authorization header or cookie is required');
396
+ case 403:
397
+ throw createHttpError(403, 'Forbidden');
398
+ case 422:
399
+ throw createHttpError(422, 'Unprocessable content');
400
+ case 500:
401
+ throw createHttpError(500, 'Internal server error');
402
+ default:
403
+ throw createHttpError(500, 'Internal server error');
404
+ }
405
+ };
406
+
407
+ const logger = new Logger('DynamicWaasWalletClient', LogLevel.DEBUG);
408
+ const setLoggerContext = ({ environmentId, authMode = AuthMode.HEADER, sessionId = undefined, userId = undefined })=>{
409
+ try {
410
+ Logger.setEnvironmentId(environmentId);
411
+ Logger.globalMetaData.set('sid', sessionId);
412
+ Logger.globalMetaData.set('user_id', userId);
413
+ Logger.globalMetaData.set('auth_mode', authMode);
414
+ } catch (error) {
415
+ logError({
416
+ message: '[DynamicWaasWalletClient] Error setting logger context',
417
+ error: error,
418
+ context: {}
419
+ });
420
+ }
421
+ };
422
+ const logError = ({ message, error, context })=>{
423
+ if (error instanceof AxiosError) {
424
+ handleAxiosError(error, message, context);
425
+ }
426
+ logger.error(message, {
427
+ error: error instanceof Error ? error.message : 'Unknown error',
428
+ context
429
+ });
430
+ };
431
+
432
+ const DEFAULT_LOG_LEVEL = LogLevel.DEBUG; //todo: change back to info when done debugging
433
+ const STORAGE_KEY = 'dynamic-waas-wallet-client';
434
+ const CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX = 'dynamicWalletKeyShareBackup';
435
+ const SIGNED_SESSION_ID_MIN_VERSION = '4.25.4';
436
+ // Namespace-specific version requirements
437
+ const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
438
+ WalletKit: '4.25.4',
439
+ ClientSDK: '0.1.0-alpha.0'
440
+ };
441
+
442
+ const isBrowser = ()=>typeof window !== 'undefined';
443
+ const getClientKeyShareExportFileName = ({ thresholdSignatureScheme, accountAddress })=>{
444
+ return `${CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX}-${thresholdSignatureScheme}-${accountAddress}.json`;
445
+ };
446
+ const getClientKeyShareBackupInfo = (params)=>{
447
+ var _params_walletProperties, _params_walletProperties_keyShares_;
448
+ const backups = {
449
+ [BackupLocation.DYNAMIC]: [],
450
+ [BackupLocation.GOOGLE_DRIVE]: [],
451
+ [BackupLocation.ICLOUD]: [],
452
+ [BackupLocation.USER]: [],
453
+ [BackupLocation.EXTERNAL]: []
454
+ };
455
+ if (!(params == null ? void 0 : (_params_walletProperties = params.walletProperties) == null ? void 0 : _params_walletProperties.keyShares)) {
456
+ return {
457
+ backups,
458
+ passwordEncrypted: false
459
+ };
460
+ }
461
+ params.walletProperties.keyShares.forEach((keyShare)=>{
462
+ if (backups[keyShare.backupLocation]) {
463
+ backups[keyShare.backupLocation].push({
464
+ location: keyShare.backupLocation,
465
+ keyShareId: keyShare.id,
466
+ externalKeyShareId: keyShare == null ? void 0 : keyShare.externalKeyShareId
467
+ });
468
+ }
469
+ });
470
+ const passwordEncrypted = Boolean((_params_walletProperties_keyShares_ = params.walletProperties.keyShares[0]) == null ? void 0 : _params_walletProperties_keyShares_.passwordEncrypted);
471
+ return {
472
+ backups,
473
+ passwordEncrypted
474
+ };
475
+ };
476
+ /**
477
+ * Helper function to merge keyshares and remove duplicates based on pubkey and secretShare
478
+ * @param existingKeyShares - Array of existing keyshares
479
+ * @param newKeyShares - Array of new keyshares to merge
480
+ * @returns Array of merged unique keyshares
481
+ */ const mergeUniqueKeyShares = (existingKeyShares, newKeyShares)=>{
482
+ const uniqueKeyShares = newKeyShares.filter((newShare)=>!existingKeyShares.some((existingShare)=>{
483
+ if (!(newShare == null ? void 0 : newShare.pubkey) || !(existingShare == null ? void 0 : existingShare.pubkey)) return false;
484
+ return newShare.pubkey.toString() === existingShare.pubkey.toString() && newShare.secretShare === existingShare.secretShare;
485
+ }));
486
+ return [
487
+ ...existingKeyShares,
488
+ ...uniqueKeyShares
489
+ ];
490
+ };
491
+ const timeoutPromise = ({ timeInMs, activity = 'Ceremony' })=>{
492
+ return new Promise((_, reject)=>setTimeout(()=>reject(new Error(`${activity} did not complete in ${timeInMs}ms`)), timeInMs));
493
+ };
494
+ /**
495
+ * Generic helper function to retry a promise-based operations
496
+ *
497
+ * @param operation - The async operation to retry
498
+ * @param config - Configuration options for retry behavior
499
+ * @returns Promise with the operation result
500
+ * @throws Last error encountered after all retries are exhausted
501
+ */ async function retryPromise(operation, { maxAttempts = 5, retryInterval = 500, operationName = 'operation', logContext = {} } = {}) {
502
+ let attempts = 0;
503
+ while(attempts < maxAttempts){
504
+ try {
505
+ return await operation();
506
+ } catch (error) {
507
+ attempts++;
508
+ if (attempts === maxAttempts) {
509
+ logger.error(`Failed to execute ${operationName} after ${maxAttempts} attempts`, _extends({}, logContext, {
510
+ error: error instanceof Error ? error.message : 'Unknown error'
511
+ }));
512
+ throw error;
513
+ }
514
+ // Calculate exponential backoff delay
515
+ const exponentialDelay = retryInterval * 2 ** (attempts - 1);
516
+ await new Promise((resolve)=>setTimeout(resolve, exponentialDelay));
517
+ }
518
+ }
519
+ // TypeScript needs this even though it's unreachable
520
+ throw new Error('Unreachable code');
521
+ }
522
+ const formatEvmMessage = (message)=>{
523
+ if (typeof message === 'string' && message.startsWith('0x')) {
524
+ const serializedTxBytes = Uint8Array.from(Buffer.from(message.slice(2), 'hex'));
525
+ return MessageHash.keccak256(serializedTxBytes);
526
+ }
527
+ return MessageHash.keccak256(message);
528
+ };
529
+ const isHexString = (str)=>{
530
+ // Remove 0x prefix if present
531
+ const hex = str.startsWith('0x') ? str.slice(2) : str;
532
+ // Check if string contains only hex characters
533
+ return /^[0-9A-Fa-f]+$/.test(hex);
534
+ };
535
+ const formatSolanaMessage = (message)=>{
536
+ if (typeof message === 'string') {
537
+ if (!isHexString(message)) {
538
+ return Buffer.from(message).toString('hex');
539
+ } else {
540
+ return new Uint8Array(Buffer.from(message, 'hex'));
541
+ }
542
+ } else {
543
+ return message;
544
+ }
545
+ };
546
+ const formatMessage = (chainName, message)=>{
547
+ switch(chainName){
548
+ case 'EVM':
549
+ return formatEvmMessage(message);
550
+ case 'SVM':
551
+ return formatSolanaMessage(message);
552
+ case 'SUI':
553
+ return message;
554
+ default:
555
+ throw new Error('Unsupported chain name');
556
+ }
557
+ };
558
+ const getGoogleOAuthAccountId = (verifiedCredentials)=>{
559
+ const googleVerifiedCredential = verifiedCredentials == null ? void 0 : verifiedCredentials.find((credential)=>credential.oauthProvider === ProviderEnum.Google);
560
+ return googleVerifiedCredential == null ? void 0 : googleVerifiedCredential.id;
561
+ };
562
+ const createBackupData = ({ encryptedKeyShares, accountAddress, thresholdSignatureScheme, hasPassword = true })=>{
563
+ return {
564
+ keyShares: encryptedKeyShares,
565
+ metadata: {
566
+ createdAt: new Date().toISOString(),
567
+ accountAddress,
568
+ thresholdSignatureScheme,
569
+ hasPassword,
570
+ encryption: getEncryptionMetadataForVersion(ENCRYPTION_VERSION_CURRENT),
571
+ encryptionVersion: ENCRYPTION_VERSION_CURRENT,
572
+ shareCount: encryptedKeyShares.length
573
+ }
574
+ };
575
+ };
576
+
481
577
  /**
482
578
  * Uploads a backup to Google Drive App
483
579
  * @param accessToken - The access token for the Google Drive API
@@ -2394,4 +2490,4 @@ const ERROR_VERIFY_TRANSACTION_SIGNATURE = '[DynamicWaasWalletClient]: Error ver
2394
2490
  const ERROR_EXPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error exporting private key';
2395
2491
  const ERROR_IMPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error importing private key';
2396
2492
 
2397
- export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, base64ToBytes, bytesToBase64, createBackupData, ensureBase64Padding, formatEvmMessage, formatMessage, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getMPCSignatureScheme, getMPCSigner, isBrowser, isHexString, mergeUniqueKeyShares, retryPromise, stringToBytes, timeoutPromise };
2493
+ export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, createBackupData, formatEvmMessage, formatMessage, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getMPCSignatureScheme, getMPCSigner, isBrowser, isHexString, mergeUniqueKeyShares, retryPromise, timeoutPromise };
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@dynamic-labs-wallet/browser",
3
- "version": "0.0.156",
3
+ "version": "0.0.157",
4
4
  "license": "Licensed under the Dynamic Labs, Inc. Terms Of Service (https://www.dynamic.xyz/terms-conditions)",
5
5
  "type": "commonjs",
6
6
  "dependencies": {
7
- "@dynamic-labs-wallet/core": "0.0.156",
7
+ "@dynamic-labs-wallet/core": "0.0.157",
8
8
  "@dynamic-labs/logger": "^4.25.3",
9
9
  "@dynamic-labs/sdk-api-core": "^0.0.764",
10
+ "argon2id": "1.0.1",
10
11
  "axios": "1.9.0",
11
12
  "http-errors": "2.0.0",
12
13
  "semver": "^7.6.3",
@@ -38,6 +39,9 @@
38
39
  }
39
40
  },
40
41
  "devDependencies": {
42
+ "@rollup/plugin-commonjs": "^28.0.6",
43
+ "@rollup/plugin-node-resolve": "^16.0.1",
44
+ "@rollup/plugin-wasm": "^6.2.2",
41
45
  "@types/http-errors": "^2.0.0",
42
46
  "@types/semver": "^7.5.8"
43
47
  }