> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Password & State Queries

> Rotate the wallet backup password and check operation prerequisites with Dynamic's Java SDK

## Overview

Two related pieces of wallet lifecycle live on the base `DynamicWalletClient`:

1. **`updatePassword`** — Rotate the password protecting the wallet's backup. Local re-encrypt only, no MPC ceremony.
2. **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.

```java theme={"system"}
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

```java theme={"system"}
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.

```java theme={"system"}
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:

```java theme={"system"}
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

* [Storage Best Practices](/java/storage-best-practices) — persist updated `WalletProperties` after rotation
* [Create EVM Wallet](/java/evm/create-wallet) / [Create SVM Wallet](/java/svm/create-wallet)
* [Delegated access](/java/evm/delegated-access) — the alternate auth model that doesn't use a password
