Overview
Two related pieces of wallet lifecycle live on the base DynamicWalletClient:
updatePassword — Rotate the password protecting the wallet’s backup. Local re-encrypt only, no MPC ceremony.
- State predicates —
isPasswordEncrypted, requiresPasswordForOperation, requiresRestoreBackupSharesForOperation. Pure-local static helpers that tell you what inputs the next operation will need.
Use the predicates in UI code to decide whether to prompt the user for a password or trigger a backup-shares recovery before the underlying signing / export call. Use updatePassword to rotate the AES-256-GCM password that wraps the backup shares Dynamic stores on your behalf.
State Predicates
All three predicates are pure-local — no MPC, no network. They inspect the WalletProperties.externalServerKeySharesBackupInfo field and return a boolean.
import xyz.dynamic.waas.DynamicWalletClient;
import xyz.dynamic.waas.core.types.WalletOperation;
import xyz.dynamic.waas.core.types.WalletProperties;
// Is the wallet's backup protected by a password?
boolean encrypted = DynamicWalletClient.isPasswordEncrypted(walletProperties);
// Will the next signMessage call need a password input from the user?
boolean needsPw = DynamicWalletClient.requiresPasswordForOperation(
walletProperties,
WalletOperation.SIGN_MESSAGE
);
// Will the next refresh call need a backup-shares recovery first?
boolean needsRestore = DynamicWalletClient.requiresRestoreBackupSharesForOperation(
walletProperties,
WalletOperation.REFRESH
);
WalletOperation enum
The operation argument is one of:
SIGN_MESSAGE / SIGN_TYPED_DATA / SIGN_TRANSACTION / SEND_TRANSACTION
EXPORT_PRIVATE_KEY
REFRESH / RESHARE
UPDATE_PASSWORD
The predicates encode the minimum input the SDK will demand for that operation given the wallet’s current state — feed them into your UI gating so users only see the password prompt when it’s actually required.
Use in UI Gating
if (DynamicWalletClient.requiresRestoreBackupSharesForOperation(
walletProperties, WalletOperation.SIGN_MESSAGE)) {
// No cached shares + backup exists → trigger recovery flow first.
promptForBackupRecovery();
} else if (DynamicWalletClient.requiresPasswordForOperation(
walletProperties, WalletOperation.SIGN_MESSAGE)) {
// Have shares but the wallet is password-protected.
promptForPassword();
} else {
// Free to sign.
proceedToSign();
}
Rotate the Backup Password
updatePassword is a local re-encrypt only (no MPC ceremony). It decrypts the existing backup shares with existingPassword, re-encrypts with newPassword, and POSTs the new ciphertext. The MPC secret-share material inside the shares is unchanged — only the AES key wrapping it.
import xyz.dynamic.waas.core.types.UpdatePasswordResult;
import xyz.dynamic.waas.opts.UpdatePasswordOpts;
UpdatePasswordResult result = client.updatePassword(UpdatePasswordOpts.builder()
.walletProperties(walletProperties)
.existingPassword(currentPassword)
.newPassword(newPassword)
.externalServerKeyShares(externalServerKeyShares)
.build()
).join();
// Persist result.backupInfo() — the backup metadata has been updated.
// Merge it into your cached WalletProperties via withBackupInfo(...).
WalletProperties updated = walletProperties.withBackupInfo(result.backupInfo());
redis.set("wallet:" + updated.accountAddress(), updated.toJson());
Skipping the recovery round-trip
When the caller already has plaintext externalServerKeyShares cached client-side, existingPassword can be omitted — the SDK skips the recovery round-trip entirely:
UpdatePasswordResult result = client.updatePassword(UpdatePasswordOpts.builder()
.walletProperties(walletProperties)
.newPassword(newPassword)
.externalServerKeyShares(externalServerKeyShares) // already plaintext
.build()
).join();
This is the common case for active wallets that have cached shares — pay the recovery round-trip only when shares have been evicted.
Why Both Predicates and updatePassword Are Local
Both fall in the same conceptual bucket: the SDK already has everything it needs to answer / perform them client-side, so there’s no MPC ceremony involved. This matters for:
- Throughput — no Dynamic backend round-trip on the hot path (predicates)
- Recovery —
updatePassword works the same in disaster scenarios as it does in steady state
- Cost — no MPC compute consumed for a password rotation
The trade-off is that you must hold the existing password (or plaintext shares) to call updatePassword. If both are lost, the wallet’s MPC shares are recoverable only through the standard refresh / reshare flows (not yet exposed in v0.1.0).
Next Steps
Last modified on May 25, 2026