@dynamic-labs/waas 5.0.0-rc.0 → 5.0.0-rc.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/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
 
2
- ## [5.0.0-rc.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.6...v5.0.0-rc.0) (2026-07-05)
2
+ ## [5.0.0-rc.1](https://github.com/dynamic-labs/dynamic-auth/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-07-17)
3
+
4
+
5
+ ### ⚠ BREAKING CHANGES
6
+
7
+ * move legacy embedded wallet to waas upgrade flow to new legacy-embedded-wallet-migration package (#11841)
8
+ * drop default injection of legacy embedded wallet connectors (#11833)
9
+ * remove legacy embedded wallet support (#11979)
10
+ * throw when legacy embedded wallet is detected without migration provider (#11855)
11
+ * remove deprecated eclipse package (#11831)
12
+ * remove deprecated functions and props (#11832)
13
+
14
+
15
+ ### [4.92.4](https://github.com/dynamic-labs/dynamic-auth/compare/v4.92.3...v4.92.4) (2026-07-16)
16
+
17
+
18
+ ### Features
19
+
20
+ * **bitcoin:** per-network address switching and dust-limit send validation [v4] ([#11970](https://github.com/dynamic-labs/dynamic-auth/issues/11970)) ([76a4a9a](https://github.com/dynamic-labs/dynamic-auth/commit/76a4a9a5025d78798ebd1beffe6c0af59d2d029a)), closes [#11896](https://github.com/dynamic-labs/dynamic-auth/issues/11896) [#11800](https://github.com/dynamic-labs/dynamic-auth/issues/11800)
21
+ * **midnight:** per-network addresses in deposit and wallet info views [v4] ([#11949](https://github.com/dynamic-labs/dynamic-auth/issues/11949)) ([978446a](https://github.com/dynamic-labs/dynamic-auth/commit/978446aca8546eb038ba9ed3be0503d10594ee15))
22
+ * **midnight:** resolve wallet address per selected network [v4] ([#11948](https://github.com/dynamic-labs/dynamic-auth/issues/11948)) ([d6f858c](https://github.com/dynamic-labs/dynamic-auth/commit/d6f858c5e35defda7ed73cf97b3fdbe5ed9d131d))
23
+ * set useMetamaskSdk to true by default ([#11926](https://github.com/dynamic-labs/dynamic-auth/issues/11926)) ([75b5a4a](https://github.com/dynamic-labs/dynamic-auth/commit/75b5a4a0fe8b1a375afce145c0b8bdc3ce761117))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * **midnight:** align per-network code with the final main iteration [v4] ([#11971](https://github.com/dynamic-labs/dynamic-auth/issues/11971)) ([3ef3ba8](https://github.com/dynamic-labs/dynamic-auth/commit/3ef3ba8c090dfd9e4286b4e51bf350ea5e6feac7))
29
+ * settle social sign-in when captcha is backed out or oauth popup never responds (v4) ([#11956](https://github.com/dynamic-labs/dynamic-auth/issues/11956)) ([ad49477](https://github.com/dynamic-labs/dynamic-auth/commit/ad49477f2acf61a154bb79cbe26b24f803cdb0e2))
30
+ * validate that private key is not empty before upgrading to WaaS ([#11952](https://github.com/dynamic-labs/dynamic-auth/issues/11952)) ([21dfb83](https://github.com/dynamic-labs/dynamic-auth/commit/21dfb83cb29e42b335bc94a0cdd2062cf14a262a))
31
+
32
+ ### [4.92.3](https://github.com/dynamic-labs/dynamic-auth/compare/v4.92.2...v4.92.3) (2026-07-10)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * **sdk-react-core:** fix deterministic 'Wallet not found' crash in embedded wallet export (v4) ([#11906](https://github.com/dynamic-labs/dynamic-auth/issues/11906)) ([913ef23](https://github.com/dynamic-labs/dynamic-auth/commit/913ef236b26245488e1cd5db7e874601b65ff0a2)), closes [#11905](https://github.com/dynamic-labs/dynamic-auth/issues/11905) [#11729](https://github.com/dynamic-labs/dynamic-auth/issues/11729)
38
+
39
+ ### [4.92.2](https://github.com/dynamic-labs/dynamic-auth/compare/v4.92.1...v4.92.2) (2026-07-09)
40
+
41
+
42
+ ### Bug Fixes
43
+
44
+ * engage captcha during step-up OTP re-auth ([#11879](https://github.com/dynamic-labs/dynamic-auth/issues/11879)) ([585460a](https://github.com/dynamic-labs/dynamic-auth/commit/585460a8976803a8ed0aad16a4a00047bd6c115a))
45
+ * **waas:** gate v1 wallet upgrade on recovery for email-only wallets ([#11897](https://github.com/dynamic-labs/dynamic-auth/issues/11897)) ([5a6f0c6](https://github.com/dynamic-labs/dynamic-auth/commit/5a6f0c6bf3120e59cf502004d09fae56f193e977))
46
+
47
+ ### [4.92.1](https://github.com/dynamic-labs/dynamic-auth/compare/v4.92.0...v4.92.1) (2026-07-09)
48
+
49
+
50
+ ### Bug Fixes
51
+
52
+ * **ethereum:** fire in-app-browser redirect on sign for injected EVM wallets that use in-app-browser experience ([#11888](https://github.com/dynamic-labs/dynamic-auth/issues/11888)) ([5116d91](https://github.com/dynamic-labs/dynamic-auth/commit/5116d91ab2c71000a490d72c5d278549d4d8ae9a))
53
+ * **sdk-react-core:** show clear message for Phantom redirect signing failures (v4 backport) ([#11886](https://github.com/dynamic-labs/dynamic-auth/issues/11886)) ([60ac897](https://github.com/dynamic-labs/dynamic-auth/commit/60ac897ba81f7758ccd7a05f97b619785e1f50a7))
54
+ * **social:** use redirect strategy for step-up OAuth on mobile [V4 backport] ([#11881](https://github.com/dynamic-labs/dynamic-auth/issues/11881)) ([42a202e](https://github.com/dynamic-labs/dynamic-auth/commit/42a202e6255de8db73df02eff37c0b10b24fef1a))
55
+ * **solana:** fire in-app-browser redirect on sign and wallet switch ([#11871](https://github.com/dynamic-labs/dynamic-auth/issues/11871)) ([4e2d6b7](https://github.com/dynamic-labs/dynamic-auth/commit/4e2d6b7ac8fdef5bf7c62b8a726ecaef1c6210ec))
56
+ * **solana:** resolve Phantom/Backpack provider collision and Backpack >=0.10.x connect hangs ([#11856](https://github.com/dynamic-labs/dynamic-auth/issues/11856)) ([#11882](https://github.com/dynamic-labs/dynamic-auth/issues/11882)) ([5784aff](https://github.com/dynamic-labs/dynamic-auth/commit/5784aff9641bbc85445fb466f3ab1d6a14331e73))
57
+
58
+ ## [4.92.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.6...v4.92.0) (2026-07-08)
59
+
60
+
61
+ ### Features
62
+
63
+ * **captcha:** add Cloudflare Turnstile provider support alongside hCaptcha (v4) ([#11798](https://github.com/dynamic-labs/dynamic-auth/issues/11798)) ([114c770](https://github.com/dynamic-labs/dynamic-auth/commit/114c7702e900d8dedeebe7be867de35d11a93f31))
64
+
65
+
66
+ ### Bug Fixes
67
+
68
+ * **ethereum-aa:** resolve kernel client config per wallet (v4) ([#11870](https://github.com/dynamic-labs/dynamic-auth/issues/11870)) ([1b66d79](https://github.com/dynamic-labs/dynamic-auth/commit/1b66d79b4a32b4b6de433b7536bc46e6719224c6))
69
+ * **iframe-setup:** block dangerous URL schemes and verify sender in OPEN_URL ([#11819](https://github.com/dynamic-labs/dynamic-auth/issues/11819)) ([329b27e](https://github.com/dynamic-labs/dynamic-auth/commit/329b27e9fac825916ab227ec8cf93dc343898923))
70
+ * improve social OAuth error messages ([#11837](https://github.com/dynamic-labs/dynamic-auth/issues/11837)) ([8e57c06](https://github.com/dynamic-labs/dynamic-auth/commit/8e57c067c5a89111a32a12fb82e190cf6c46ad67))
71
+ * **react-native-extension:** reject path traversal in downloadFile fileName ([#11816](https://github.com/dynamic-labs/dynamic-auth/issues/11816)) ([9367a56](https://github.com/dynamic-labs/dynamic-auth/commit/9367a56b6daa853d7f7e7f5534261bc68825b239))
72
+ * **sdk-react-core:** display exactly the bytes signed in personal_sign preview ([#11818](https://github.com/dynamic-labs/dynamic-auth/issues/11818)) ([30b9382](https://github.com/dynamic-labs/dynamic-auth/commit/30b93829122f00060cb484aef09a03294c03f651))
73
+
3
74
 
4
75
  ### [4.91.6](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.5...v4.91.6) (2026-07-03)
5
76
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "5.0.0-rc.0";
6
+ var version = "5.0.0-rc.1";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "5.0.0-rc.0";
2
+ var version = "5.0.0-rc.1";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/waas",
3
- "version": "5.0.0-rc.0",
3
+ "version": "5.0.0-rc.1",
4
4
  "type": "module",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -16,18 +16,18 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "dependencies": {
19
- "@dynamic-labs-sdk/client": "1.12.1",
20
- "@dynamic-labs/assert-package-version": "5.0.0-rc.0",
21
- "@dynamic-labs/sdk-api-core": "0.0.1046",
22
- "@dynamic-labs-wallet/browser-wallet-client": "1.0.48",
23
- "@dynamic-labs-wallet/forward-mpc-client": "0.12.0",
24
- "@dynamic-labs/ethereum-core": "5.0.0-rc.0",
25
- "@dynamic-labs/logger": "5.0.0-rc.0",
26
- "@dynamic-labs/solana-core": "5.0.0-rc.0",
27
- "@dynamic-labs/sui-core": "5.0.0-rc.0",
28
- "@dynamic-labs/utils": "5.0.0-rc.0",
29
- "@dynamic-labs/wallet-book": "5.0.0-rc.0",
30
- "@dynamic-labs/wallet-connector-core": "5.0.0-rc.0"
19
+ "@dynamic-labs-sdk/client": "1.18.0",
20
+ "@dynamic-labs/assert-package-version": "5.0.0-rc.1",
21
+ "@dynamic-labs/sdk-api-core": "0.0.1082",
22
+ "@dynamic-labs-wallet/browser-wallet-client": "1.0.70",
23
+ "@dynamic-labs-wallet/forward-mpc-client": "1.0.1",
24
+ "@dynamic-labs/ethereum-core": "5.0.0-rc.1",
25
+ "@dynamic-labs/logger": "5.0.0-rc.1",
26
+ "@dynamic-labs/solana-core": "5.0.0-rc.1",
27
+ "@dynamic-labs/sui-core": "5.0.0-rc.1",
28
+ "@dynamic-labs/utils": "5.0.0-rc.1",
29
+ "@dynamic-labs/wallet-book": "5.0.0-rc.1",
30
+ "@dynamic-labs/wallet-connector-core": "5.0.0-rc.1"
31
31
  },
32
32
  "peerDependencies": {}
33
33
  }
@@ -150,7 +150,7 @@ const withDynamicWaas = (BaseClass) => {
150
150
  accountAddress,
151
151
  authToken,
152
152
  password: resolvedPassword,
153
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
153
+ sessionPublicKey: (yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this))),
154
154
  signedSessionId,
155
155
  });
156
156
  });
@@ -189,12 +189,38 @@ const withDynamicWaas = (BaseClass) => {
189
189
  }
190
190
  const original = this.getSignedSessionId;
191
191
  let revoked = false;
192
- this.revokeSignedSession = () => {
192
+ let revokedReason;
193
+ let revokedInvocationCount = 0;
194
+ this.revokeSignedSession = (reason = 'unspecified') => {
193
195
  revoked = true;
196
+ revokedReason = reason;
197
+ this.instrument('[DynamicWaasMixin] signed-session callback revoked', {
198
+ key: 'signed-session-callback-revoked',
199
+ operation: 'revokeSignedSession',
200
+ reason,
201
+ time: 0,
202
+ });
194
203
  };
195
204
  return {
196
205
  getSignedSessionId: () => _tslib.__awaiter(this, void 0, void 0, function* () {
197
206
  if (revoked) {
207
+ revokedInvocationCount += 1;
208
+ // The stale client fetched a nonce after being replaced — this is
209
+ // the race the revoke guards against. We log it (rather than only
210
+ // throwing) so Datadog can distinguish this benign guard from a
211
+ // real backend nonce_already_used failure: a revoked hit means the
212
+ // new client, not this one, owns the fresh nonce. `firstInvocation`
213
+ // flags the leading edge so false-positive logouts (a revoked
214
+ // callback firing once right after a successful backup) are easy to
215
+ // separate from a stale client that keeps retrying.
216
+ this.instrument('[DynamicWaasMixin] revoked signed-session callback invoked by stale client', {
217
+ firstInvocation: revokedInvocationCount === 1,
218
+ key: 'signed-session-callback-revoked-invoked',
219
+ operation: 'getSignedSessionId',
220
+ reason: revokedReason,
221
+ revokedInvocationCount,
222
+ time: 0,
223
+ });
198
224
  throw new Error('[DynamicWaasMixin] WaaS client replaced — signed session callback revoked');
199
225
  }
200
226
  return original();
@@ -254,7 +280,7 @@ const withDynamicWaas = (BaseClass) => {
254
280
  // The old DynamicWalletClient is not destroyed — it has no cancellation
255
281
  // API — but revoking its reverse-channel callback prevents it from
256
282
  // fetching fresh nonces and competing with the new client's recovery.
257
- (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this);
283
+ (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this, 'force-rebuild');
258
284
  this.revokeSignedSession = undefined;
259
285
  this.dynamicWaasClient = undefined;
260
286
  }
@@ -277,21 +303,48 @@ const withDynamicWaas = (BaseClass) => {
277
303
  }
278
304
  // Common methods that are identical across all connectors
279
305
  createWalletAccount() {
280
- return _tslib.__awaiter(this, arguments, void 0, function* ({ thresholdSignatureScheme = 'TWO_OF_TWO', password, bitcoinConfig, } = {}) {
306
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ thresholdSignatureScheme = 'TWO_OF_TWO', password, bitcoinConfig, businessAccountId, } = {}) {
281
307
  var _a, _b;
282
308
  const walletClient = yield this.getWaasWalletClient();
283
309
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
284
310
  if (!signedSessionId) {
285
311
  throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
286
312
  }
287
- const createdWallet = yield walletClient.createWalletAccount({
288
- authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
289
- bitcoinConfig,
313
+ const createdWallet = yield walletClient.createWalletAccount(Object.assign({ authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this), bitcoinConfig,
314
+ password,
315
+ signedSessionId, thresholdSignatureScheme: thresholdSignatureScheme }, (businessAccountId && { workspaceAccountId: businessAccountId })));
316
+ return createdWallet;
317
+ });
318
+ }
319
+ // Add a business account signer via the reshare ceremony.
320
+ addSignerViaReshare(_a) {
321
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, walletId, businessAccountId, targetSignerIdentity, signerType, password, }) {
322
+ var _b, _c, _d;
323
+ const walletClient = yield this.getWaasWalletClient();
324
+ const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
325
+ if (!signedSessionId) {
326
+ throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
327
+ }
328
+ (_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug('[BusinessAccountAddSigner] addSignerViaReshare', {
329
+ accountAddress,
330
+ businessAccountId,
331
+ identifierType: targetSignerIdentity.identifierType,
332
+ identityMode: targetSignerIdentity.userId ? 'userId' : 'identifier',
333
+ signerType,
334
+ walletId,
335
+ });
336
+ // browser-wallet-client@1.0.48 types use workspaceAccountId; cast to
337
+ // bridge the rename while runtime behaviour is correct.
338
+ return walletClient.reshareToNewSignerShareSet({
339
+ accountAddress,
340
+ authToken: (_d = this.getAuthToken) === null || _d === void 0 ? void 0 : _d.call(this),
290
341
  password,
291
342
  signedSessionId,
292
- thresholdSignatureScheme: thresholdSignatureScheme,
343
+ signerType: signerType,
344
+ targetSignerIdentity: targetSignerIdentity,
345
+ walletId,
346
+ workspaceAccountId: businessAccountId,
293
347
  });
294
- return createdWallet;
295
348
  });
296
349
  }
297
350
  importPrivateKey(_a) {
@@ -393,25 +446,80 @@ const withDynamicWaas = (BaseClass) => {
393
446
  });
394
447
  });
395
448
  }
396
- backupKeySharesToGoogleDrive(_a) {
397
- return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
398
- var _b, _c, _d;
449
+ /**
450
+ * Runs the shared cloud-backup lifecycle: resolve the password, build the
451
+ * iframe client, fetch the signed session id and session public key, then
452
+ * hand off to the provider-specific `walletClient.backupKeySharesTo*` call.
453
+ *
454
+ * Every step emits an instrumentation event tagged with a single
455
+ * `backupLifecycleId` so a backup can be followed end-to-end in Datadog.
456
+ * The same id is passed to the wallet client as `traceContext.traceId`, so
457
+ * the client's own sub-steps (client key-share request, backup-locations
458
+ * endpoint, etc.) log under the same trace — making it clear whether a
459
+ * backup finished before an unexpected signed-session fetch/logout.
460
+ *
461
+ * `public` for the same TS4094 mixin constraint as the other helpers here;
462
+ * it is internal-only.
463
+ */
464
+ runBackupLifecycle(_a) {
465
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ provider, accountAddress, password, performBackup, }) {
466
+ var _b, _c;
399
467
  if (!accountAddress) {
400
468
  throw new utils.DynamicError('Account address is required', 'account_address_is_required');
401
469
  }
470
+ const backupLifecycleId = this.generateTraceId();
471
+ const startTime = Date.now();
472
+ const traceContext = { startTime, traceId: backupLifecycleId };
473
+ const lifecycleContext = { accountAddress, backupLifecycleId, provider };
474
+ this.instrument('[backupKeyShares] lifecycle - start', Object.assign({ key: 'backup-lifecycle-start', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
402
475
  const resolvedPassword = password !== null && password !== void 0 ? password : (yield this.getPassword({ accountAddress }));
476
+ this.instrument('[backupKeyShares] password resolved', Object.assign({ hasPassword: resolvedPassword !== undefined, key: 'backup-password-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
403
477
  const walletClient = yield this.getWaasWalletClient();
478
+ this.instrument('[backupKeyShares] wallet client ready', Object.assign({ key: 'backup-wallet-client-ready', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
404
479
  const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
480
+ this.instrument('[backupKeyShares] signed session id resolved', Object.assign({ hasSignedSessionId: Boolean(signedSessionId), key: 'backup-signed-session-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
405
481
  if (!signedSessionId) {
406
482
  throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
407
483
  }
408
- return walletClient.backupKeySharesToGoogleDrive({
484
+ const sessionPublicKey = yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this));
485
+ this.instrument('[backupKeyShares] session public key resolved', Object.assign({ hasSessionPublicKey: Boolean(sessionPublicKey), key: 'backup-session-public-key-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
486
+ // instrumentAsync emits `backup-request-start/completed/failed` so the
487
+ // provider call itself (and the client sub-steps under the shared
488
+ // traceId) is timed and its failure surfaced.
489
+ yield this.instrumentAsync({
490
+ context: lifecycleContext,
491
+ fn: () => _tslib.__awaiter(this, void 0, void 0, function* () {
492
+ return performBackup({
493
+ resolvedPassword,
494
+ sessionPublicKey,
495
+ signedSessionId,
496
+ traceContext,
497
+ walletClient,
498
+ });
499
+ }),
500
+ operation: 'backupKeyShares',
501
+ resource: 'backup-request',
502
+ });
503
+ });
504
+ }
505
+ backupKeySharesToGoogleDrive(_a) {
506
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
507
+ return this.runBackupLifecycle({
409
508
  accountAddress,
410
- authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
411
- googleDriveAccessToken,
412
- password: resolvedPassword,
413
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
414
- signedSessionId,
509
+ password,
510
+ performBackup: (_b) => _tslib.__awaiter(this, [_b], void 0, function* ({ walletClient, resolvedPassword, signedSessionId, sessionPublicKey, traceContext, }) {
511
+ var _c;
512
+ return walletClient.backupKeySharesToGoogleDrive({
513
+ accountAddress,
514
+ authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
515
+ googleDriveAccessToken,
516
+ password: resolvedPassword,
517
+ sessionPublicKey,
518
+ signedSessionId,
519
+ traceContext,
520
+ });
521
+ }),
522
+ provider: 'google-drive',
415
523
  });
416
524
  });
417
525
  }
@@ -437,22 +545,21 @@ const withDynamicWaas = (BaseClass) => {
437
545
  }
438
546
  backupKeySharesToICloud(_a) {
439
547
  return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
440
- var _b, _c, _d;
441
- if (!accountAddress) {
442
- throw new utils.DynamicError('Account address is required', 'account_address_is_required');
443
- }
444
- const resolvedPassword = password !== null && password !== void 0 ? password : (yield this.getPassword({ accountAddress }));
445
- const walletClient = yield this.getWaasWalletClient();
446
- const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
447
- if (!signedSessionId) {
448
- throw new utils.DynamicError('Signed session ID is required', 'signed_session_id_is_required');
449
- }
450
- return walletClient.backupKeySharesToICloud({
548
+ return this.runBackupLifecycle({
451
549
  accountAddress,
452
- authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
453
- password: resolvedPassword,
454
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
455
- signedSessionId,
550
+ password,
551
+ performBackup: (_b) => _tslib.__awaiter(this, [_b], void 0, function* ({ walletClient, resolvedPassword, signedSessionId, sessionPublicKey, traceContext, }) {
552
+ var _c;
553
+ return walletClient.backupKeySharesToICloud({
554
+ accountAddress,
555
+ authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
556
+ password: resolvedPassword,
557
+ sessionPublicKey,
558
+ signedSessionId,
559
+ traceContext,
560
+ });
561
+ }),
562
+ provider: 'icloud',
456
563
  });
457
564
  });
458
565
  }
@@ -626,7 +733,7 @@ const withDynamicWaas = (BaseClass) => {
626
733
  });
627
734
  }
628
735
  signRawMessage(_a) {
629
- return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, message, password, }) {
736
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ accountAddress, context, message, password, }) {
630
737
  var _b, _c, _d, _e;
631
738
  if (!accountAddress) {
632
739
  throw new utils.DynamicError('Account address is required', 'account_address_is_required');
@@ -649,6 +756,7 @@ const withDynamicWaas = (BaseClass) => {
649
756
  return walletClient.signRawMessage({
650
757
  accountAddress,
651
758
  authToken: (_e = this.getAuthToken) === null || _e === void 0 ? void 0 : _e.call(this),
759
+ context,
652
760
  elevatedAccessToken,
653
761
  message,
654
762
  mfaToken,
@@ -746,7 +854,7 @@ const withDynamicWaas = (BaseClass) => {
746
854
  });
747
855
  }
748
856
  }
749
- (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this);
857
+ (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this, 'end-session');
750
858
  this.revokeSignedSession = undefined;
751
859
  this.dynamicWaasClient = undefined;
752
860
  });
@@ -1,7 +1,7 @@
1
- import { BitcoinConfig, DynamicWalletClient, MigrateFromFireblocksResponse } from '@dynamic-labs-wallet/browser-wallet-client';
1
+ import { BitcoinConfig, DynamicWalletClient } from '@dynamic-labs-wallet/browser-wallet-client';
2
2
  import type { LogoutReason } from '@dynamic-labs-sdk/client';
3
3
  import { InstrumentOptions, Logger } from '@dynamic-labs/logger';
4
- import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';
4
+ import { MFAAction, SignMessageContext, TokenScope } from '@dynamic-labs/sdk-api-core';
5
5
  import { GetWalletPasswordFn } from '@dynamic-labs/wallet-connector-core';
6
6
  import { InstrumentContext, InstrumentationTimer, TraceContext } from '../utils/instrumentation';
7
7
  export declare class WaasExportHandler {
@@ -30,7 +30,7 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
30
30
  relayUrl?: string | undefined;
31
31
  baseClientKeysharesRelayApiUrl?: string | undefined;
32
32
  dynamicWaasClient: DynamicWalletClient | undefined;
33
- revokeSignedSession: (() => void) | undefined;
33
+ revokeSignedSession: ((reason?: string) => void) | undefined;
34
34
  chainName: string;
35
35
  authMode: 'cookie' | 'header';
36
36
  logger: Logger;
@@ -85,16 +85,31 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
85
85
  getWaasWalletClient(traceContext?: TraceContext, { forceRebuild }?: {
86
86
  forceRebuild?: boolean;
87
87
  }): Promise<DynamicWalletClient>;
88
- createWalletAccount({ thresholdSignatureScheme, password, bitcoinConfig, }?: {
88
+ createWalletAccount({ thresholdSignatureScheme, password, bitcoinConfig, businessAccountId, }?: {
89
89
  thresholdSignatureScheme?: string;
90
90
  password?: string;
91
91
  bitcoinConfig?: BitcoinConfig;
92
+ businessAccountId?: string;
92
93
  }): Promise<{
93
94
  chainName: string;
94
95
  accountAddress: string;
95
96
  publicKeyHex: string;
96
97
  rawPublicKey: string | Uint8Array | undefined;
97
98
  }>;
99
+ addSignerViaReshare({ accountAddress, walletId, businessAccountId, targetSignerIdentity, signerType, password, }: {
100
+ accountAddress: string;
101
+ walletId?: string;
102
+ businessAccountId: string;
103
+ targetSignerIdentity: {
104
+ userId?: string;
105
+ identifier?: string;
106
+ identifierType?: string;
107
+ };
108
+ signerType?: 'endUser' | 'server';
109
+ password?: string;
110
+ }): Promise<{
111
+ shareSetId: string;
112
+ }>;
98
113
  importPrivateKey({ privateKey, thresholdSignatureScheme, publicAddressCheck, addressType, legacyWalletId, password, }: {
99
114
  privateKey: string;
100
115
  thresholdSignatureScheme?: string;
@@ -111,7 +126,7 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
111
126
  expectedAddresses: Record<string, string>;
112
127
  coinTypes?: Record<string, number>;
113
128
  password?: string;
114
- }): Promise<MigrateFromFireblocksResponse>;
129
+ }): Promise<Record<string, unknown>>;
115
130
  exportPrivateKey({ accountAddress, displayContainer, password, }?: {
116
131
  accountAddress?: string;
117
132
  displayContainer?: HTMLIFrameElement;
@@ -124,6 +139,36 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
124
139
  accountAddress: string;
125
140
  password?: string;
126
141
  }): Promise<void>;
142
+ /**
143
+ * Runs the shared cloud-backup lifecycle: resolve the password, build the
144
+ * iframe client, fetch the signed session id and session public key, then
145
+ * hand off to the provider-specific `walletClient.backupKeySharesTo*` call.
146
+ *
147
+ * Every step emits an instrumentation event tagged with a single
148
+ * `backupLifecycleId` so a backup can be followed end-to-end in Datadog.
149
+ * The same id is passed to the wallet client as `traceContext.traceId`, so
150
+ * the client's own sub-steps (client key-share request, backup-locations
151
+ * endpoint, etc.) log under the same trace — making it clear whether a
152
+ * backup finished before an unexpected signed-session fetch/logout.
153
+ *
154
+ * `public` for the same TS4094 mixin constraint as the other helpers here;
155
+ * it is internal-only.
156
+ */
157
+ runBackupLifecycle({ provider, accountAddress, password, performBackup, }: {
158
+ provider: string;
159
+ accountAddress: string;
160
+ password?: string;
161
+ performBackup: (prerequisites: {
162
+ walletClient: DynamicWalletClient;
163
+ resolvedPassword: string | undefined;
164
+ signedSessionId: string;
165
+ sessionPublicKey: string | undefined;
166
+ traceContext: {
167
+ traceId: string;
168
+ startTime: number;
169
+ };
170
+ }) => Promise<void>;
171
+ }): Promise<void>;
127
172
  backupKeySharesToGoogleDrive({ accountAddress, password, googleDriveAccessToken, }: {
128
173
  accountAddress: string;
129
174
  password?: string;
@@ -164,8 +209,9 @@ export declare const withDynamicWaas: <T extends abstract new (...args: any[]) =
164
209
  accountAddress: string;
165
210
  newPassword: string;
166
211
  }): Promise<void>;
167
- signRawMessage({ accountAddress, message, password, }: {
212
+ signRawMessage({ accountAddress, context, message, password, }: {
168
213
  accountAddress: string;
214
+ context?: SignMessageContext;
169
215
  message: string;
170
216
  password?: string;
171
217
  }): Promise<string>;
@@ -146,7 +146,7 @@ const withDynamicWaas = (BaseClass) => {
146
146
  accountAddress,
147
147
  authToken,
148
148
  password: resolvedPassword,
149
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
149
+ sessionPublicKey: (yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this))),
150
150
  signedSessionId,
151
151
  });
152
152
  });
@@ -185,12 +185,38 @@ const withDynamicWaas = (BaseClass) => {
185
185
  }
186
186
  const original = this.getSignedSessionId;
187
187
  let revoked = false;
188
- this.revokeSignedSession = () => {
188
+ let revokedReason;
189
+ let revokedInvocationCount = 0;
190
+ this.revokeSignedSession = (reason = 'unspecified') => {
189
191
  revoked = true;
192
+ revokedReason = reason;
193
+ this.instrument('[DynamicWaasMixin] signed-session callback revoked', {
194
+ key: 'signed-session-callback-revoked',
195
+ operation: 'revokeSignedSession',
196
+ reason,
197
+ time: 0,
198
+ });
190
199
  };
191
200
  return {
192
201
  getSignedSessionId: () => __awaiter(this, void 0, void 0, function* () {
193
202
  if (revoked) {
203
+ revokedInvocationCount += 1;
204
+ // The stale client fetched a nonce after being replaced — this is
205
+ // the race the revoke guards against. We log it (rather than only
206
+ // throwing) so Datadog can distinguish this benign guard from a
207
+ // real backend nonce_already_used failure: a revoked hit means the
208
+ // new client, not this one, owns the fresh nonce. `firstInvocation`
209
+ // flags the leading edge so false-positive logouts (a revoked
210
+ // callback firing once right after a successful backup) are easy to
211
+ // separate from a stale client that keeps retrying.
212
+ this.instrument('[DynamicWaasMixin] revoked signed-session callback invoked by stale client', {
213
+ firstInvocation: revokedInvocationCount === 1,
214
+ key: 'signed-session-callback-revoked-invoked',
215
+ operation: 'getSignedSessionId',
216
+ reason: revokedReason,
217
+ revokedInvocationCount,
218
+ time: 0,
219
+ });
194
220
  throw new Error('[DynamicWaasMixin] WaaS client replaced — signed session callback revoked');
195
221
  }
196
222
  return original();
@@ -250,7 +276,7 @@ const withDynamicWaas = (BaseClass) => {
250
276
  // The old DynamicWalletClient is not destroyed — it has no cancellation
251
277
  // API — but revoking its reverse-channel callback prevents it from
252
278
  // fetching fresh nonces and competing with the new client's recovery.
253
- (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this);
279
+ (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this, 'force-rebuild');
254
280
  this.revokeSignedSession = undefined;
255
281
  this.dynamicWaasClient = undefined;
256
282
  }
@@ -273,21 +299,48 @@ const withDynamicWaas = (BaseClass) => {
273
299
  }
274
300
  // Common methods that are identical across all connectors
275
301
  createWalletAccount() {
276
- return __awaiter(this, arguments, void 0, function* ({ thresholdSignatureScheme = 'TWO_OF_TWO', password, bitcoinConfig, } = {}) {
302
+ return __awaiter(this, arguments, void 0, function* ({ thresholdSignatureScheme = 'TWO_OF_TWO', password, bitcoinConfig, businessAccountId, } = {}) {
277
303
  var _a, _b;
278
304
  const walletClient = yield this.getWaasWalletClient();
279
305
  const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
280
306
  if (!signedSessionId) {
281
307
  throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
282
308
  }
283
- const createdWallet = yield walletClient.createWalletAccount({
284
- authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
285
- bitcoinConfig,
309
+ const createdWallet = yield walletClient.createWalletAccount(Object.assign({ authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this), bitcoinConfig,
310
+ password,
311
+ signedSessionId, thresholdSignatureScheme: thresholdSignatureScheme }, (businessAccountId && { workspaceAccountId: businessAccountId })));
312
+ return createdWallet;
313
+ });
314
+ }
315
+ // Add a business account signer via the reshare ceremony.
316
+ addSignerViaReshare(_a) {
317
+ return __awaiter(this, arguments, void 0, function* ({ accountAddress, walletId, businessAccountId, targetSignerIdentity, signerType, password, }) {
318
+ var _b, _c, _d;
319
+ const walletClient = yield this.getWaasWalletClient();
320
+ const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
321
+ if (!signedSessionId) {
322
+ throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
323
+ }
324
+ (_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug('[BusinessAccountAddSigner] addSignerViaReshare', {
325
+ accountAddress,
326
+ businessAccountId,
327
+ identifierType: targetSignerIdentity.identifierType,
328
+ identityMode: targetSignerIdentity.userId ? 'userId' : 'identifier',
329
+ signerType,
330
+ walletId,
331
+ });
332
+ // browser-wallet-client@1.0.48 types use workspaceAccountId; cast to
333
+ // bridge the rename while runtime behaviour is correct.
334
+ return walletClient.reshareToNewSignerShareSet({
335
+ accountAddress,
336
+ authToken: (_d = this.getAuthToken) === null || _d === void 0 ? void 0 : _d.call(this),
286
337
  password,
287
338
  signedSessionId,
288
- thresholdSignatureScheme: thresholdSignatureScheme,
339
+ signerType: signerType,
340
+ targetSignerIdentity: targetSignerIdentity,
341
+ walletId,
342
+ workspaceAccountId: businessAccountId,
289
343
  });
290
- return createdWallet;
291
344
  });
292
345
  }
293
346
  importPrivateKey(_a) {
@@ -389,25 +442,80 @@ const withDynamicWaas = (BaseClass) => {
389
442
  });
390
443
  });
391
444
  }
392
- backupKeySharesToGoogleDrive(_a) {
393
- return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
394
- var _b, _c, _d;
445
+ /**
446
+ * Runs the shared cloud-backup lifecycle: resolve the password, build the
447
+ * iframe client, fetch the signed session id and session public key, then
448
+ * hand off to the provider-specific `walletClient.backupKeySharesTo*` call.
449
+ *
450
+ * Every step emits an instrumentation event tagged with a single
451
+ * `backupLifecycleId` so a backup can be followed end-to-end in Datadog.
452
+ * The same id is passed to the wallet client as `traceContext.traceId`, so
453
+ * the client's own sub-steps (client key-share request, backup-locations
454
+ * endpoint, etc.) log under the same trace — making it clear whether a
455
+ * backup finished before an unexpected signed-session fetch/logout.
456
+ *
457
+ * `public` for the same TS4094 mixin constraint as the other helpers here;
458
+ * it is internal-only.
459
+ */
460
+ runBackupLifecycle(_a) {
461
+ return __awaiter(this, arguments, void 0, function* ({ provider, accountAddress, password, performBackup, }) {
462
+ var _b, _c;
395
463
  if (!accountAddress) {
396
464
  throw new DynamicError('Account address is required', 'account_address_is_required');
397
465
  }
466
+ const backupLifecycleId = this.generateTraceId();
467
+ const startTime = Date.now();
468
+ const traceContext = { startTime, traceId: backupLifecycleId };
469
+ const lifecycleContext = { accountAddress, backupLifecycleId, provider };
470
+ this.instrument('[backupKeyShares] lifecycle - start', Object.assign({ key: 'backup-lifecycle-start', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
398
471
  const resolvedPassword = password !== null && password !== void 0 ? password : (yield this.getPassword({ accountAddress }));
472
+ this.instrument('[backupKeyShares] password resolved', Object.assign({ hasPassword: resolvedPassword !== undefined, key: 'backup-password-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
399
473
  const walletClient = yield this.getWaasWalletClient();
474
+ this.instrument('[backupKeyShares] wallet client ready', Object.assign({ key: 'backup-wallet-client-ready', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
400
475
  const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
476
+ this.instrument('[backupKeyShares] signed session id resolved', Object.assign({ hasSignedSessionId: Boolean(signedSessionId), key: 'backup-signed-session-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
401
477
  if (!signedSessionId) {
402
478
  throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
403
479
  }
404
- return walletClient.backupKeySharesToGoogleDrive({
480
+ const sessionPublicKey = yield ((_c = this.getSessionPublicKey) === null || _c === void 0 ? void 0 : _c.call(this));
481
+ this.instrument('[backupKeyShares] session public key resolved', Object.assign({ hasSessionPublicKey: Boolean(sessionPublicKey), key: 'backup-session-public-key-resolved', operation: 'backupKeyShares', time: 0 }, lifecycleContext));
482
+ // instrumentAsync emits `backup-request-start/completed/failed` so the
483
+ // provider call itself (and the client sub-steps under the shared
484
+ // traceId) is timed and its failure surfaced.
485
+ yield this.instrumentAsync({
486
+ context: lifecycleContext,
487
+ fn: () => __awaiter(this, void 0, void 0, function* () {
488
+ return performBackup({
489
+ resolvedPassword,
490
+ sessionPublicKey,
491
+ signedSessionId,
492
+ traceContext,
493
+ walletClient,
494
+ });
495
+ }),
496
+ operation: 'backupKeyShares',
497
+ resource: 'backup-request',
498
+ });
499
+ });
500
+ }
501
+ backupKeySharesToGoogleDrive(_a) {
502
+ return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, googleDriveAccessToken, }) {
503
+ return this.runBackupLifecycle({
405
504
  accountAddress,
406
- authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
407
- googleDriveAccessToken,
408
- password: resolvedPassword,
409
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
410
- signedSessionId,
505
+ password,
506
+ performBackup: (_b) => __awaiter(this, [_b], void 0, function* ({ walletClient, resolvedPassword, signedSessionId, sessionPublicKey, traceContext, }) {
507
+ var _c;
508
+ return walletClient.backupKeySharesToGoogleDrive({
509
+ accountAddress,
510
+ authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
511
+ googleDriveAccessToken,
512
+ password: resolvedPassword,
513
+ sessionPublicKey,
514
+ signedSessionId,
515
+ traceContext,
516
+ });
517
+ }),
518
+ provider: 'google-drive',
411
519
  });
412
520
  });
413
521
  }
@@ -433,22 +541,21 @@ const withDynamicWaas = (BaseClass) => {
433
541
  }
434
542
  backupKeySharesToICloud(_a) {
435
543
  return __awaiter(this, arguments, void 0, function* ({ accountAddress, password, }) {
436
- var _b, _c, _d;
437
- if (!accountAddress) {
438
- throw new DynamicError('Account address is required', 'account_address_is_required');
439
- }
440
- const resolvedPassword = password !== null && password !== void 0 ? password : (yield this.getPassword({ accountAddress }));
441
- const walletClient = yield this.getWaasWalletClient();
442
- const signedSessionId = yield ((_b = this.getSignedSessionId) === null || _b === void 0 ? void 0 : _b.call(this));
443
- if (!signedSessionId) {
444
- throw new DynamicError('Signed session ID is required', 'signed_session_id_is_required');
445
- }
446
- return walletClient.backupKeySharesToICloud({
544
+ return this.runBackupLifecycle({
447
545
  accountAddress,
448
- authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
449
- password: resolvedPassword,
450
- sessionPublicKey: yield ((_d = this.getSessionPublicKey) === null || _d === void 0 ? void 0 : _d.call(this)),
451
- signedSessionId,
546
+ password,
547
+ performBackup: (_b) => __awaiter(this, [_b], void 0, function* ({ walletClient, resolvedPassword, signedSessionId, sessionPublicKey, traceContext, }) {
548
+ var _c;
549
+ return walletClient.backupKeySharesToICloud({
550
+ accountAddress,
551
+ authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
552
+ password: resolvedPassword,
553
+ sessionPublicKey,
554
+ signedSessionId,
555
+ traceContext,
556
+ });
557
+ }),
558
+ provider: 'icloud',
452
559
  });
453
560
  });
454
561
  }
@@ -622,7 +729,7 @@ const withDynamicWaas = (BaseClass) => {
622
729
  });
623
730
  }
624
731
  signRawMessage(_a) {
625
- return __awaiter(this, arguments, void 0, function* ({ accountAddress, message, password, }) {
732
+ return __awaiter(this, arguments, void 0, function* ({ accountAddress, context, message, password, }) {
626
733
  var _b, _c, _d, _e;
627
734
  if (!accountAddress) {
628
735
  throw new DynamicError('Account address is required', 'account_address_is_required');
@@ -645,6 +752,7 @@ const withDynamicWaas = (BaseClass) => {
645
752
  return walletClient.signRawMessage({
646
753
  accountAddress,
647
754
  authToken: (_e = this.getAuthToken) === null || _e === void 0 ? void 0 : _e.call(this),
755
+ context,
648
756
  elevatedAccessToken,
649
757
  message,
650
758
  mfaToken,
@@ -742,7 +850,7 @@ const withDynamicWaas = (BaseClass) => {
742
850
  });
743
851
  }
744
852
  }
745
- (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this);
853
+ (_a = this.revokeSignedSession) === null || _a === void 0 ? void 0 : _a.call(this, 'end-session');
746
854
  this.revokeSignedSession = undefined;
747
855
  this.dynamicWaasClient = undefined;
748
856
  });