@dynamic-labs-wallet/browser 1.0.89 → 1.0.91

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 CHANGED
@@ -652,13 +652,17 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
652
652
  // canRefresh only proves a local callback exists, not that the host answers it.
653
653
  if (!this.supportsReverseChannel()) {
654
654
  this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
655
- status
655
+ http: {
656
+ status_code: status
657
+ }
656
658
  }, logContext));
657
659
  return alsoRetry ? alsoRetry(status) : false;
658
660
  }
659
661
  onRefreshed(await this.resolve());
660
662
  this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
661
- status
663
+ http: {
664
+ status_code: status
665
+ }
662
666
  }, logContext));
663
667
  return true;
664
668
  }
@@ -674,7 +678,9 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
674
678
  this.logger.info(NONCE_FAILURE_EVENT, _extends({}, logContext, {
675
679
  operationName,
676
680
  attempt,
677
- status,
681
+ http: {
682
+ status_code: status
683
+ },
678
684
  reason: classifyNonceFailure(body),
679
685
  errorCode: body.code,
680
686
  errorMessage: body.message,
@@ -2114,17 +2120,21 @@ const initializeCloudKit = async (config, signInButtonId, onSignInRequired, onSi
2114
2120
  // Downgrade them to warn so monitors can filter on status:error and
2115
2121
  // still catch real system-side failures (5xx/network/unknown) without
2116
2122
  // the user-side noise drowning out the signal.
2117
- const logFn = isUserActionable ? logger.warn : logger.error;
2118
2123
  // When every location failed for the same reason (the dominant case)
2119
2124
  // surface a single string; otherwise emit the array so consumers can
2120
2125
  // see the mix.
2121
2126
  const logErrorReason = uniqueReasons.length === 1 ? uniqueReasons[0] : uniqueReasons;
2122
- logFn.call(logger, '[DynamicWaasWalletClient] Google Drive backup failed', _extends({}, logContext, {
2127
+ const logPayload = _extends({}, logContext, {
2123
2128
  errorCount: failures.length,
2124
2129
  errorReason: logErrorReason,
2125
2130
  isUserActionable,
2126
2131
  errors: failures.map((f)=>`Failed to backup keyshares to ${f.locationName}: ${f.message}`)
2127
- }));
2132
+ });
2133
+ if (isUserActionable) {
2134
+ logger.warn('[DynamicWaasWalletClient] Google Drive backup failed', logPayload);
2135
+ } else {
2136
+ logger.error('[DynamicWaasWalletClient] Google Drive backup failed', logPayload);
2137
+ }
2128
2138
  // Both upload destinations (appDataFolder + personal) commonly fail with
2129
2139
  // the same underlying error (e.g., missing OAuth scope). When all failures
2130
2140
  // share one message, surface it once without per-location wrapping;
@@ -2171,7 +2181,12 @@ const classifyWaasErrorKind = (error)=>{
2171
2181
  // only if the deadline passes; per-attempt we just tag the kind.
2172
2182
  const classifyTransientPollError = (error)=>{
2173
2183
  const status = readHttpStatus(error);
2174
- if (status === 429 || status !== undefined && status >= 500) return 'transient_exhausted';
2184
+ // `NETWORK_ERROR_STATUS` (0) is "no response received" as transient as a 5xx.
2185
+ // It previously arrived here as 500 because handleAxiosError coerced every
2186
+ // unmapped failure to that, so it must stay classified as transient.
2187
+ if (status === 429 || status === core.NETWORK_ERROR_STATUS || status !== undefined && status >= 500) {
2188
+ return 'transient_exhausted';
2189
+ }
2175
2190
  return classifyWaasErrorKind(error);
2176
2191
  };
2177
2192
  // Builds a structured log payload tagged with the HTTPS transport, dropping
@@ -2187,18 +2202,35 @@ const buildHttpCeremonyMeta = (fields)=>_extends({
2187
2202
  * so they don't pollute error dashboards with user mistakes.
2188
2203
  */ const isExpectedUserError = (error)=>error instanceof InvalidPasswordError;
2189
2204
  /**
2190
- * Returns true for transient API errors that are already logged at the
2191
- * appropriate level by `handleAxiosError` but may be re-thrown as
2192
- * `WalletApiError` and caught again (e.g. by queue error handlers).
2193
- * Logging these at `warn` prevents double-counting them as real errors.
2194
- */ const isTransientApiError = (error)=>error instanceof core.WalletApiError && error.status === 429;
2205
+ * Reads the HTTP status off a `WalletApiError`.
2206
+ *
2207
+ * Matches on `name` as well as `instanceof` because core is inlined into more
2208
+ * than one bundle, and a second copy of the class makes `instanceof` fail
2209
+ * against the first which would silently defeat every downgrade below. An
2210
+ * error without a numeric status is treated as unrecognised (fail safe: stays
2211
+ * at the caller's fallback level).
2212
+ */ const getWalletApiErrorStatus = (error)=>{
2213
+ const isWalletApiError = error instanceof core.WalletApiError || error instanceof Error && error.name === 'WalletApiError';
2214
+ if (!isWalletApiError) return undefined;
2215
+ const { status } = error;
2216
+ return typeof status === 'number' ? status : undefined;
2217
+ };
2195
2218
  /**
2196
- * Returns true for missing/expired end-user auth (401). This is a normal
2197
- * session-lifecycle event (cookie/JWT expired, user must re-authenticate),
2198
- * not an actionable server error retrying cannot add credentials. Logged
2199
- * at `warn` so it stays queryable without firing error-level monitors. It was
2200
- * the dominant `Error in getWallet` catch-all contributor.
2201
- */ const isExpiredAuthError = (error)=>error instanceof core.WalletApiError && error.status === 401;
2219
+ * Returns true for client-class API errors (4xx, including 429 and 401)
2220
+ * surfaced as `WalletApiError`. A bad request, missing/invalid auth, forbidden,
2221
+ * or rate-limit is caused by the caller or is transient, so none of them are
2222
+ * actionable as backend errors the catch-site `logError` logs them at `warn`
2223
+ * to keep them out of error-level dashboards. 401 in particular was the
2224
+ * dominant `Error in getWallet` catch-all contributor.
2225
+ *
2226
+ * 5xx is deliberately NOT downgraded: those are genuine backend failures and
2227
+ * the 500 monitors depend on them staying at `error`. The `handleAxiosError`
2228
+ * HTTP-layer log also records them with fuller context, now that it no longer
2229
+ * collides with Datadog's reserved `status` field.
2230
+ */ const isDowngradableApiError = (error)=>{
2231
+ const status = getWalletApiErrorStatus(error);
2232
+ return status !== undefined && status < 500;
2233
+ };
2202
2234
  /**
2203
2235
  * Returns true for p-queue TimeoutError. These are transient and
2204
2236
  * retryable client-side, so logging at `warn` prevents them from
@@ -2218,15 +2250,93 @@ const CLIENT_NETWORK_ERROR_MESSAGES = new Set([
2218
2250
  const msg = error.message.toLowerCase();
2219
2251
  return CLIENT_NETWORK_ERROR_MESSAGES.has(msg) || msg.startsWith('webassembly compilation aborted: network error');
2220
2252
  };
2221
- const resolveLogLevel = (error, fallback)=>{
2222
- if (isExpectedUserError(error)) {
2223
- return 'info';
2224
- }
2225
- if (isTransientApiError(error) || isExpiredAuthError(error) || isQueueTimeoutError(error) || isClientNetworkError(error)) {
2226
- return 'warn';
2253
+ const classifyOne = (error)=>{
2254
+ if (isExpectedUserError(error)) return 'info';
2255
+ if (isDowngradableApiError(error) || isQueueTimeoutError(error) || isClientNetworkError(error)) return 'warn';
2256
+ return undefined;
2257
+ };
2258
+ /** Bounded so a self-referencing or cyclic `cause` chain cannot spin. */ const MAX_CAUSE_DEPTH = 5;
2259
+ /**
2260
+ * Single source of truth for "how bad is this error". Exported so callers
2261
+ * classify identically instead of hard-coding `logger.error` for every failure.
2262
+ *
2263
+ * Walks the `cause` chain, because the outermost error is frequently a wrapper
2264
+ * that carries no information about severity — a chain client throws
2265
+ * `new Error(ERROR_SIGN_MESSAGE, { cause })`, so classifying only the top link
2266
+ * sees a bare `Error` and falls through to `error`. That is how a Cloudflare WAF
2267
+ * 429 kept being reported as a hard SDK failure even after the direct call sites
2268
+ * were fixed: the wrapper, not the cause, was being classified.
2269
+ *
2270
+ * The first classifiable link wins, so the most specific known failure in the
2271
+ * chain decides.
2272
+ */ const resolveLogLevel = (error, fallback)=>{
2273
+ let current = error;
2274
+ for(let depth = 0; current !== undefined && current !== null && depth < MAX_CAUSE_DEPTH; depth += 1){
2275
+ const level = classifyOne(current);
2276
+ if (level) return level;
2277
+ const next = current instanceof Error ? current.cause : undefined;
2278
+ if (next === current) break;
2279
+ current = next;
2227
2280
  }
2228
2281
  return fallback;
2229
2282
  };
2283
+ const MAX_DESCRIBED_LENGTH = 200;
2284
+ /**
2285
+ * Builds a human-readable message for a thrown value that is not an `Error` —
2286
+ * most often an API response body, e.g.
2287
+ * `{ existingWalletId, error: 'Multiple wallets per chain not allowed' }`.
2288
+ *
2289
+ * `String(value)` would collapse that to `[object Object]`, losing the only
2290
+ * diagnostic content it had, so prefer a recognisable message field and fall
2291
+ * back to bounded JSON.
2292
+ */ const describeNonError = (value)=>{
2293
+ if (typeof value === 'string') return value.slice(0, MAX_DESCRIBED_LENGTH);
2294
+ if (value !== null && typeof value === 'object') {
2295
+ const record = value;
2296
+ const named = [
2297
+ record['error'],
2298
+ record['message'],
2299
+ record['detail']
2300
+ ].find((candidate)=>typeof candidate === 'string' && candidate.length > 0);
2301
+ if (named) return named.slice(0, MAX_DESCRIBED_LENGTH);
2302
+ try {
2303
+ return JSON.stringify(value).slice(0, MAX_DESCRIBED_LENGTH);
2304
+ } catch (e) {
2305
+ // Circular or non-serialisable — fall through to String().
2306
+ }
2307
+ }
2308
+ return String(value).slice(0, MAX_DESCRIBED_LENGTH);
2309
+ };
2310
+ /**
2311
+ * Decides how an operation failure reaches the Datadog logger, whose signature
2312
+ * is `(message, context, error)`.
2313
+ *
2314
+ * The rule that matters: a non-`Error` value must never be passed as `context`.
2315
+ * Datadog spreads a context object into top-level log attributes, so a thrown
2316
+ * API response body carrying `status: 500` lands on Datadog's reserved `status`
2317
+ * field and silently rewrites the log's own severity to `info` — the failure
2318
+ * then appears in neither the error nor the warn stream. Non-Errors are nested
2319
+ * under `operationError` instead, and a real `Error` is synthesised so the log
2320
+ * still gets a stack and an `error.kind`.
2321
+ *
2322
+ * Returned as a plain object so this is unit-testable without constructing a
2323
+ * wallet client (which pulls the MPC WASM bundle).
2324
+ */ const buildOperationFailureLog = (error)=>{
2325
+ // Classify the original value: an Error's `cause` chain carries the severity,
2326
+ // and normalising first would discard it.
2327
+ const level = resolveLogLevel(error, 'error');
2328
+ return error instanceof Error ? {
2329
+ level,
2330
+ context: undefined,
2331
+ error
2332
+ } : {
2333
+ level,
2334
+ context: {
2335
+ operationError: error
2336
+ },
2337
+ error: new Error(describeNonError(error))
2338
+ };
2339
+ };
2230
2340
  const logError = ({ message, error, context, level = 'error' })=>{
2231
2341
  if (error instanceof axios.AxiosError) {
2232
2342
  core.handleAxiosError(error, message, context);
@@ -2990,6 +3100,17 @@ class DynamicWalletClient {
2990
3100
  return this.walletMap[normalizeAddress(accountAddress)];
2991
3101
  }
2992
3102
  /**
3103
+ * Sync variant of {@link requireWalletFromMap} (no refetch). Replaces the
3104
+ * `getWalletFromMap(...)!` non-null assertions that surfaced a missing
3105
+ * wallet as an opaque property-access TypeError at some later line.
3106
+ */ getRequiredWalletFromMap(accountAddress) {
3107
+ const walletData = this.getWalletFromMap(accountAddress);
3108
+ if (!walletData) {
3109
+ throw new Error(`Wallet not found for address: ${accountAddress}`);
3110
+ }
3111
+ return walletData;
3112
+ }
3113
+ /**
2993
3114
  * Get wallet properties from the map, refetching once if not found.
2994
3115
  * Uses getWallet (with NO_OPERATION) when signedSessionId is available for a
2995
3116
  * more robust fetch, otherwise falls back to getWallets().
@@ -4882,7 +5003,7 @@ class DynamicWalletClient {
4882
5003
  signedSessionId
4883
5004
  });
4884
5005
  const walletId = wallet.walletId;
4885
- const currentScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
5006
+ const currentScheme = this.getRequiredWalletFromMap(accountAddress).thresholdSignatureScheme;
4886
5007
  const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
4887
5008
  // Same-parties reshare — only the caller's share participates.
4888
5009
  const [existingClientShare] = await this.ensureClientShare(accountAddress);
@@ -5797,7 +5918,7 @@ class DynamicWalletClient {
5797
5918
  useShareSetReshare,
5798
5919
  initialSignerRules
5799
5920
  });
5800
- const backupInfo = this.getWalletFromMap(accountAddress).clientKeySharesBackupInfo;
5921
+ const backupInfo = this.getRequiredWalletFromMap(accountAddress).clientKeySharesBackupInfo;
5801
5922
  const delegatedKeyShares = backupInfo.backups[core.BackupLocation.DELEGATED] || [];
5802
5923
  return delegatedKeyShares;
5803
5924
  }
@@ -6260,7 +6381,7 @@ class DynamicWalletClient {
6260
6381
  if (shares.length > 1) {
6261
6382
  this.logger.warn('[DynamicWaasWalletClient] Multiple client key shares found in storage', {
6262
6383
  accountAddress,
6263
- source,
6384
+ shareSource: source,
6264
6385
  count: shares.length
6265
6386
  });
6266
6387
  }
@@ -6311,7 +6432,7 @@ class DynamicWalletClient {
6311
6432
  */ logSharePersistence({ accountAddress, clientKeyShares, source }) {
6312
6433
  this.logger.info('[DynamicWaasWalletClient] Persisting client key shares', {
6313
6434
  accountAddress,
6314
- source,
6435
+ shareSource: source,
6315
6436
  inputCount: clientKeyShares.length
6316
6437
  });
6317
6438
  }
@@ -6818,8 +6939,8 @@ class DynamicWalletClient {
6818
6939
  } catch (error) {
6819
6940
  var _error_cause_context;
6820
6941
  const errorReason = classifyPasswordBackupError(error);
6821
- const logFn = isUserActionablePasswordBackupErrorReason(errorReason) ? this.logger.warn : this.logger.error;
6822
- logFn.call(this.logger, '[backupSharesWithDistribution] failed', _extends({}, logContext, {
6942
+ const isUserActionable = isUserActionablePasswordBackupErrorReason(errorReason);
6943
+ const logPayload = _extends({}, logContext, {
6823
6944
  chainName: walletData == null ? void 0 : walletData.chainName,
6824
6945
  errorReason,
6825
6946
  errorName: error instanceof Error ? error.name : undefined,
@@ -6829,7 +6950,12 @@ class DynamicWalletClient {
6829
6950
  // failures show the underlying KeyShareDecryptionError / kdfVersion.
6830
6951
  errorCauseName: error instanceof Error && error.cause instanceof Error ? error.cause.name : undefined,
6831
6952
  kdfVersion: error instanceof Error && error.cause instanceof KeyShareDecryptionError ? (_error_cause_context = error.cause.context) == null ? void 0 : _error_cause_context.kdfVersion : undefined
6832
- }));
6953
+ });
6954
+ if (isUserActionable) {
6955
+ this.logger.warn('[backupSharesWithDistribution] failed', logPayload);
6956
+ } else {
6957
+ this.logger.error('[backupSharesWithDistribution] failed', logPayload);
6958
+ }
6833
6959
  logError({
6834
6960
  message: 'Error in backupSharesWithDistribution',
6835
6961
  error: error,
@@ -7080,9 +7206,9 @@ class DynamicWalletClient {
7080
7206
  // they don't count against the dashboard's System-side Success Rate metric.
7081
7207
  logPasswordOperationFailure(passwordOperation, error, context) {
7082
7208
  const errorReason = classifyPasswordBackupError(error);
7083
- const logFn = isUserActionablePasswordBackupErrorReason(errorReason) ? this.logger.warn : this.logger.error;
7209
+ const isUserActionable = isUserActionablePasswordBackupErrorReason(errorReason);
7084
7210
  const errorContext = error instanceof KeyShareDecryptionError ? error.context : undefined;
7085
- logFn.call(this.logger, `[${passwordOperation}] failed`, _extends({}, context, {
7211
+ const logPayload = _extends({}, context, {
7086
7212
  passwordOperation,
7087
7213
  errorReason,
7088
7214
  environmentId: this.environmentId,
@@ -7092,7 +7218,12 @@ class DynamicWalletClient {
7092
7218
  errorStack: error instanceof Error ? error.stack : undefined
7093
7219
  }, errorContext ? {
7094
7220
  errorContext
7095
- } : {}));
7221
+ } : {});
7222
+ if (isUserActionable) {
7223
+ this.logger.warn(`[${passwordOperation}] failed`, logPayload);
7224
+ } else {
7225
+ this.logger.error(`[${passwordOperation}] failed`, logPayload);
7226
+ }
7096
7227
  }
7097
7228
  async updatePassword({ accountAddress, existingPassword, newPassword, signedSessionId, sessionPublicKey, passwordUpdateBatchId }) {
7098
7229
  const dynamicRequestId = uuid.v4();
@@ -7407,7 +7538,14 @@ class DynamicWalletClient {
7407
7538
  */ recoverStrategy({ clientKeyShareBackupInfo, thresholdSignatureScheme, walletOperation, shareCount = undefined }) {
7408
7539
  var _clientKeyShareBackupInfo_backups_BackupLocation_DYNAMIC;
7409
7540
  const { backups } = clientKeyShareBackupInfo;
7410
- const { clientThreshold } = core.MPC_CONFIG[thresholdSignatureScheme];
7541
+ // Wallet metadata can carry a missing/unknown scheme (e.g. legacy or
7542
+ // partially-created wallets); indexing MPC_CONFIG with it used to throw a
7543
+ // bare destructuring TypeError that hid the actual problem.
7544
+ const mpcConfig = core.MPC_CONFIG[thresholdSignatureScheme];
7545
+ if (!mpcConfig) {
7546
+ throw new Error(`Unsupported or missing thresholdSignatureScheme: ${String(thresholdSignatureScheme)}`);
7547
+ }
7548
+ const { clientThreshold } = mpcConfig;
7411
7549
  let requiredShareCount = walletOperation === core.WalletOperation.REFRESH || walletOperation === core.WalletOperation.REACH_ALL_PARTIES || walletOperation === core.WalletOperation.RESHARE ? clientThreshold : 1;
7412
7550
  // Override requiredShareCount if shareCount is provided
7413
7551
  if (shareCount !== undefined) {
@@ -7476,7 +7614,7 @@ class DynamicWalletClient {
7476
7614
  }
7477
7615
  async internalRecoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, signedSessionId, shareCount = undefined, storeRecoveredShares = true, mfaToken }) {
7478
7616
  try {
7479
- const wallet = this.getWalletFromMap(accountAddress);
7617
+ const wallet = this.getRequiredWalletFromMap(accountAddress);
7480
7618
  this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
7481
7619
  const { shares } = this.recoverStrategy({
7482
7620
  clientKeyShareBackupInfo: wallet.clientKeySharesBackupInfo,
@@ -7585,7 +7723,7 @@ class DynamicWalletClient {
7585
7723
  password,
7586
7724
  signedSessionId
7587
7725
  });
7588
- const walletData = this.getWalletFromMap(accountAddress);
7726
+ const walletData = this.getRequiredWalletFromMap(accountAddress);
7589
7727
  const currentThresholdSignatureScheme = walletData.thresholdSignatureScheme;
7590
7728
  if (currentThresholdSignatureScheme === core.ThresholdSignatureScheme.TWO_OF_TWO) {
7591
7729
  // Reshare to 2-of-3, which will automatically handle the backup distribution
@@ -7644,7 +7782,7 @@ class DynamicWalletClient {
7644
7782
  * every provider already holding a backup plus the requested one,
7645
7783
  * restricted to providers the reshare can deliver to.
7646
7784
  */ async reshareNonDelegatedTwoOfThree({ accountAddress, backupLocation, password, signedSessionId, sessionPublicKey, googleDriveAccessToken, googleDriveTokenSource }) {
7647
- const walletData = this.getWalletFromMap(accountAddress);
7785
+ const walletData = this.getRequiredWalletFromMap(accountAddress);
7648
7786
  const cloudProviders = new Set([
7649
7787
  ...getActiveCloudProviders(walletData.clientKeySharesBackupInfo).filter((provider)=>provider === core.BackupLocation.GOOGLE_DRIVE || provider === core.BackupLocation.ICLOUD),
7650
7788
  backupLocation
@@ -7783,14 +7921,18 @@ class DynamicWalletClient {
7783
7921
  // misuse of an externally-supplied token (e.g. repeated failures) is
7784
7922
  // traceable, not just completed backups. Enum only; the original error
7785
7923
  // still propagates after this. Failures log at warn to surface them.
7786
- const logFn = success ? this.logger.info : this.logger.warn;
7787
- logFn.call(this.logger, '[DynamicWaasWalletClient] Google Drive backup token source', {
7924
+ const logPayload = {
7788
7925
  accountAddress,
7789
7926
  environmentId: this.environmentId,
7790
7927
  userId: this.userId,
7791
7928
  tokenSource,
7792
7929
  success
7793
- });
7930
+ };
7931
+ if (success) {
7932
+ this.logger.info('[DynamicWaasWalletClient] Google Drive backup token source', logPayload);
7933
+ } else {
7934
+ this.logger.warn('[DynamicWaasWalletClient] Google Drive backup token source', logPayload);
7935
+ }
7794
7936
  }
7795
7937
  }
7796
7938
  /**
@@ -7809,8 +7951,7 @@ class DynamicWalletClient {
7809
7951
  ]);
7810
7952
  const { blocking } = preflightResult;
7811
7953
  if (blocking) {
7812
- const logFn = blocking.isUserActionable ? this.logger.warn : this.logger.error;
7813
- logFn.call(this.logger, '[DynamicWaasWalletClient] Google Drive backup failed (preflight)', {
7954
+ const logPayload = {
7814
7955
  accountAddress,
7815
7956
  environmentId: this.environmentId,
7816
7957
  userId: this.userId,
@@ -7821,8 +7962,13 @@ class DynamicWalletClient {
7821
7962
  `Preflight (${blocking.source}): ${blocking.message}`
7822
7963
  ],
7823
7964
  preflight: true,
7824
- source: blocking.source
7825
- });
7965
+ preflightSource: blocking.source
7966
+ };
7967
+ if (blocking.isUserActionable) {
7968
+ this.logger.warn('[DynamicWaasWalletClient] Google Drive backup failed (preflight)', logPayload);
7969
+ } else {
7970
+ this.logger.error('[DynamicWaasWalletClient] Google Drive backup failed (preflight)', logPayload);
7971
+ }
7826
7972
  throw createGoogleDriveError({
7827
7973
  message: blocking.message,
7828
7974
  isRetryable: false,
@@ -7938,7 +8084,7 @@ class DynamicWalletClient {
7938
8084
  if (encryptedKeyShares.length === 0) {
7939
8085
  throw new Error('No key shares found');
7940
8086
  }
7941
- const thresholdSignatureScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
8087
+ const thresholdSignatureScheme = this.getRequiredWalletFromMap(accountAddress).thresholdSignatureScheme;
7942
8088
  const backupData = createBackupData({
7943
8089
  encryptedKeyShares,
7944
8090
  accountAddress,
@@ -7965,7 +8111,7 @@ class DynamicWalletClient {
7965
8111
  signedSessionId
7966
8112
  });
7967
8113
  const accessToken = googleDriveAccessToken != null ? googleDriveAccessToken : await this.fetchGoogleDriveAccessToken(accountAddress);
7968
- const thresholdSignatureScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
8114
+ const thresholdSignatureScheme = this.getRequiredWalletFromMap(accountAddress).thresholdSignatureScheme;
7969
8115
  const backupFileName = getClientKeyShareExportFileName({
7970
8116
  thresholdSignatureScheme,
7971
8117
  accountAddress,
@@ -8185,7 +8331,7 @@ class DynamicWalletClient {
8185
8331
  });
8186
8332
  const { requiredShareCount } = this.recoverStrategy({
8187
8333
  clientKeyShareBackupInfo: clientKeySharesBackupInfo,
8188
- thresholdSignatureScheme: this.getWalletFromMap(accountAddress).thresholdSignatureScheme,
8334
+ thresholdSignatureScheme: this.getRequiredWalletFromMap(accountAddress).thresholdSignatureScheme,
8189
8335
  walletOperation
8190
8336
  });
8191
8337
  if (clientKeyShares.length >= requiredShareCount) {
@@ -8290,7 +8436,7 @@ class DynamicWalletClient {
8290
8436
  accountAddress,
8291
8437
  walletOperation
8292
8438
  })) {
8293
- const walletData = this.getWalletFromMap(accountAddress);
8439
+ const walletData = this.getRequiredWalletFromMap(accountAddress);
8294
8440
  const isPasswordEncrypted = isWalletPasswordEncrypted(walletData);
8295
8441
  // Locked wallet (password-encrypted, no password supplied): its shares need the
8296
8442
  // user's password, not the environmentId. Return it ENCRYPTED for any operation
@@ -8595,7 +8741,7 @@ class DynamicWalletClient {
8595
8741
  additionalWalletsUnlocked,
8596
8742
  additionalWalletsFailed
8597
8743
  });
8598
- return this.getWalletFromMap(accountAddress);
8744
+ return this.getRequiredWalletFromMap(accountAddress);
8599
8745
  } catch (error) {
8600
8746
  this.logPasswordOperationFailure('unlockWallet', error, {
8601
8747
  accountAddress
@@ -9042,6 +9188,16 @@ class DynamicWalletClient {
9042
9188
  time: (traceContext == null ? void 0 : traceContext.startTime) ? now - traceContext.startTime : 0
9043
9189
  }, traceContext);
9044
9190
  }
9191
+ /**
9192
+ * Log a chain-client operation failure at a severity that matches the
9193
+ * underlying cause. Logs through `this.logger` (not `logError`) so the
9194
+ * structured `error.causes` chain survives — that chain is what makes these
9195
+ * failures diagnosable — while still honouring the shared level policy, so a
9196
+ * transient 429/401/network blip does not read as a hard SDK error.
9197
+ */ logOperationFailure(message, error) {
9198
+ const { level, context, error: normalizedError } = buildOperationFailureLog(error);
9199
+ this.logger[level](message, context, normalizedError);
9200
+ }
9045
9201
  constructor({ environmentId, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, argon2idRebackupTargets, authMode = core.AuthMode.HEADER, authToken = undefined, backupServiceAuthToken, // Represents the version of the client SDK used by developer
9046
9202
  sdkVersion, forwardMPCClient, baseClientKeysharesRelayApiUrl, iCloudConfig, logger }, internalOptions){
9047
9203
  this.userId = undefined;
@@ -9281,6 +9437,7 @@ exports.isStaleClientSharesError = isStaleClientSharesError;
9281
9437
  exports.listICloudBackups = listICloudBackups;
9282
9438
  exports.markCeremonyErrorNonRetryable = markCeremonyErrorNonRetryable;
9283
9439
  exports.readEnvironmentSettings = readEnvironmentSettings;
9440
+ exports.resolveLogLevel = resolveLogLevel;
9284
9441
  exports.retryPromise = retryPromise;
9285
9442
  exports.shouldReshareToSameBackups = shouldReshareToSameBackups;
9286
9443
  exports.timeoutPromise = timeoutPromise;