Skip to main content

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.

The Java SDK is stateless. It does not hold wallet state between calls. After createWalletAccount(), the returned KeygenResult carries two distinct pieces of state — each belongs in a different storage tier:
Returned valueSensitivityWhere to storeHow it’s used
WalletPropertiesNon-sensitiveNormal cache: Redis, Postgres, etc. Read on every request.Identifies the wallet; passed to every sign / export operation.
List<ServerKeyShare>Sensitive (MPC key material)Secrets vault: HSM, KMS-wrapped DB column, AWS Secrets Manager, etc.The customer-side half of MPC signing. Combined with your Dynamic API key, provides signing authority.
Both must be persisted from creation. The SDK provides no compatibility shim — every subsequent operation requires walletProperties explicitly, and operations without cached List<ServerKeyShare> (and without backup fallback) will fail.

Persisting WalletProperties

WalletProperties is non-sensitive identity + backup-pointer metadata. It exposes toJson() / fromJson(...) with snake_case keys (cross-SDK compatible with Python and Rust) and toNodeJson() / fromNodeJson(...) with camelCase (Node v1 interop). Cache it like any other per-user record:
KeygenResult result = client.createWalletAccount(
    CreateWalletOpts.builder()
        .password("user-password")
        .backUpToDynamic(true)
        .build()
).join();

WalletProperties walletProperties = result.walletProperties();

// Cache the full struct — including backup-pointer info.
redis.set(
    "wallet:" + walletProperties.accountAddress(),
    walletProperties.toJson()
);
Cache the full record. The externalServerKeySharesBackupInfo field on WalletProperties is not recoverable via SDK-scoped endpoints — fetchWalletMetadata(accountAddress) returns identity only. Operations that need it (password verification, share recovery) will fail if it’s missing. Mutating operations (updatePassword, refresh, reshare) return new backup info — merge it into the cached WalletProperties via WalletProperties.withBackupInfo(...) before writing back to your cache.

Storing List<ServerKeyShare>

When you set .backUpToDynamic(false) during wallet creation, you are responsible for securely storing the List<ServerKeyShare> returned by createWalletAccount. These key shares, combined with your Dynamic developer API key, provide signing authority and must be protected with defense-in-depth strategies. ServerKeyShare is JSON-serializable — use ServerKeyShare.toJson() / fromJson(...) or your vault’s native format before persisting.
Use google-cloud-kms or google-cloud-secretmanager — same envelope pattern as AWS KMS, integrated with Google Cloud IAM and audit logging.
Use azure-security-keyvault-secrets — Azure-managed secrets with Managed Identity auth.
Use vault-java-driver or spring-cloud-vault to read/write secrets to a Vault KV mount. Works well if your stack already runs Vault.

Security Requirements Checklist

Regardless of your storage method, follow these requirements:
  • Never log plaintext key shares — redact externalServerKeyShares from all logs, error messages, and monitoring. ServerKeyShare.toString() is redacted by the SDK; mirror that in your own code.
  • Encrypt at rest — use AES-256-GCM or equivalent; ensure database/storage has encryption enabled
  • Encrypt in transit — all communication must use TLS 1.3
  • Implement access controls — restrict which services and roles can decrypt key shares
  • Enable audit logging — track all access to encrypted materials with timestamps and actor identity
  • Separate encryption keys — don’t reuse keys across environments (dev/staging/prod)
  • Use unique encryption per record — generate new IVs for each encryption operation
  • Implement key rotation — rotate encryption keys periodically (e.g., every 90 days)
  • Plan for key compromise — document incident response for key material exposure
  • Secure deletion — null out decrypted byte arrays after use; prefer char[] for passwords so you can clear them deterministically

Storage Schema Example

CREATE TABLE server_wallet_key_shares (
  id UUID PRIMARY KEY,
  wallet_id VARCHAR(255) NOT NULL,
  account_address VARCHAR(255) NOT NULL,

  -- Encrypted with KMS/Vault
  encrypted_key_shares BYTEA NOT NULL,

  -- Metadata for key management
  encryption_key_id VARCHAR(255) NOT NULL,
  encryption_algorithm VARCHAR(50) DEFAULT 'AES-256-GCM',

  -- Audit fields
  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  last_accessed_at TIMESTAMP,
  access_count INTEGER DEFAULT 0
);

What NOT to Do

  • Never store plaintext key shares in databases, files, or environment variables
  • Never commit encryption keys or key shares to version control
  • Never use the same encryption key across all wallets
  • Never rely solely on database encryption without application-level encryption
  • Never expose key shares through APIs or logs