The Java SDK is in beta. The API surface is still settling, and releases may include breaking changes until we ship a stable
1.0.0. Pin to an exact version in your pom.xml (e.g. <version>1.2.0-beta</version>) so a transitive bump doesn’t move you onto a new shape. Not recommended for production traffic yet; great for prototypes, internal services, and design feedback.Found a rough edge? Email support@dynamic.xyz.- Agent-friendly
- Step-by-step
Add the Dynamic docs MCP to your AI editor first — your agent can then query the docs directly. Instructions are on the Welcome page.
# Java SDK Quickstart
## Purpose and Usage
This document provides structured quickstart information for the Dynamic Java SDK. It is designed to be consumed by LLMs to generate accurate installation commands and code examples based on user requirements.
> **Important scope note:** The Dynamic Java SDK is for **server-side wallet operations only** — creating MPC wallets, signing messages/transactions, exporting keys, and delegated access on EVM and Solana. It does NOT handle frontend authentication or UI. The SDK is **stateless** — you must persist wallet state yourself.
> **Beta notice:** The Java SDK is v1.2.0-beta. Pin exact versions in `pom.xml`. Ships native binaries for `linux-x86_64` and `osx-aarch_64` only.
---
## How to use this document
Start by asking the user which path they want:
> "Would you like a **Quick start** (EVM + TWO_OF_TWO threshold scheme, Maven — recommended if you're just getting started) or a **Custom setup** (choose your chains, threshold scheme, and build tool)?"
- If the user chooses **Quick start**: follow the Quick Start Path section exactly. Do not ask any configuration questions — execute the happy path as written.
- If the user chooses **Custom setup**: follow the Custom Setup Path section and ask all configuration questions before proceeding.
---
## Quick Start Path
> Use this path when the user chooses Quick start. Execute every step in order. Do not skip steps. Do not ask configuration questions.
**Stack:** Java + Maven + EVM only + TWO_OF_TWO threshold scheme
### Step 1 — Prerequisites
Tell the user:
- JDK 21 LTS or higher is required (install via https://adoptium.net/)
- A supported host platform: `linux-x86_64` or `osx-aarch_64` (other platforms throw `UnsatisfiedLinkError`)
- A Dynamic environment ID and API token are required — get them at https://app.dynamic.xyz/dashboard/developer/api
Ask the user for their environment ID and API token before proceeding.
### Step 2 — Install the SDK
Download `dynamic-waas-sdk-java-1.2.0-beta.zip` and install to local Maven repo:
```bash
unzip dynamic-waas-sdk-java-1.2.0-beta.zip
cp -r dynamic-waas-sdk-java-1.2.0-beta/m2/xyz ~/.m2/repository/
```
Add to `pom.xml`:
```xml
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-evm</artifactId>
<version>1.2.0-beta</version>
</dependency>
```
Add the `os-maven-plugin` build extension for native binary resolution:
```xml
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
</build>
```
### Step 3 — Set up environment variables
```bash
export DYNAMIC_ENV_ID=your_environment_id_here
export DYNAMIC_API_TOKEN=your_api_token_here
export WALLET_PASSWORD=your_secure_wallet_password_here
```
> These names are a convention — the SDK does not auto-read environment variables. Pass values directly to the client constructor and `authenticateApiToken`.
### Step 4 — Initialize the client
```java
import xyz.dynamic.waas.DynamicWalletClientOpts;
import xyz.dynamic.waas.evm.DynamicEvmWalletClient;
String envId = System.getenv("DYNAMIC_ENV_ID");
String apiToken = System.getenv("DYNAMIC_API_TOKEN");
// Application-scoped: construct once, reuse, close on shutdown.
DynamicEvmWalletClient client = new DynamicEvmWalletClient(
DynamicWalletClientOpts.builder(envId).build());
client.authenticateApiToken(apiToken).join();
```
> All methods return `CompletableFuture<T>`. Use `.join()` for blocking calls, `.thenCompose(...)` for async composition. Construct the client **once** at startup and reuse — do NOT create per-request.
### Step 5 — Create a wallet
```java
import xyz.dynamic.waas.ThresholdSignatureScheme;
import xyz.dynamic.waas.opts.CreateWalletOpts;
String password = System.getenv("WALLET_PASSWORD");
var result = client.createWalletAccount(
CreateWalletOpts.builder()
.thresholdSignatureScheme(ThresholdSignatureScheme.TWO_OF_TWO)
.password(password)
.backUpToDynamic(true)
.build()
).join();
System.out.println("Wallet: " + result.walletProperties().accountAddress());
// IMPORTANT: Persist these yourself:
// - result.walletProperties() → Redis / Postgres (non-sensitive)
// - result.externalServerKeyShares() → secrets vault / KMS (sensitive MPC key material)
```
> The SDK is **stateless**. `createWalletAccount()` returns a `KeygenResult` with `WalletProperties` + `List<ServerKeyShare>` — you must store them and pass to every subsequent sign/export call.
### Step 6 — Sign a message
```java
import xyz.dynamic.waas.opts.SignMessageOpts;
String signature = client.signMessage(
SignMessageOpts.builder()
.message("Hello from Dynamic!".getBytes())
.walletProperties(result.walletProperties())
.externalServerKeyShares(result.externalServerKeyShares())
.password(password)
.build()
).join();
System.out.println("Signature: " + signature);
```
### Step 7 — Shutdown
```java
// On application shutdown (e.g. Spring @PreDestroy)
client.close();
```
### Step 8 — Run the app
```bash
mvn compile exec:java -Dexec.mainClass="com.example.Main"
```
---
## Custom Setup Path
> Use this path when the user chooses Custom setup. Ask ALL questions below before generating any code.
**Questions to ask the user:**
1. Which chains? (EVM only / Solana only / Both EVM and Solana)
2. Which threshold signature scheme? (TWO_OF_TWO recommended, or TWO_OF_THREE)
3. Build tool? (Maven / Gradle)
4. Do you need key export, key import, or delegated access?
### Chain-specific Maven dependencies
**EVM only:**
```xml
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-evm</artifactId>
<version>1.2.0-beta</version>
</dependency>
```
**Solana only:**
```xml
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-svm</artifactId>
<version>1.2.0-beta</version>
</dependency>
```
**Both (pulls in core + MPC + natives transitively):**
```xml
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-evm</artifactId>
<version>1.2.0-beta</version>
</dependency>
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-svm</artifactId>
<version>1.2.0-beta</version>
</dependency>
```
### Gradle alternative
Use `osdetector-gradle-plugin` for native classifier resolution:
```groovy
plugins {
id 'com.google.osdetector' version '1.7.3'
}
dependencies {
implementation 'xyz.dynamic.waas:dynamic-waas-sdk-evm:1.2.0-beta'
}
```
### SVM client initialization
```java
import xyz.dynamic.waas.svm.DynamicSvmWalletClient;
DynamicSvmWalletClient svmClient = new DynamicSvmWalletClient(
DynamicWalletClientOpts.builder(envId).build());
svmClient.authenticateApiToken(apiToken).join();
```
### Pointing at preprod or sandbox
```java
var opts = DynamicWalletClientOpts.builder(envId)
.baseApiUrl("https://app.dynamic-preprod.xyz")
.build();
```
### SDK capabilities (v1.2.0-beta)
- EVM: EIP-191 messages, EIP-712 typed data, EIP-1559 transaction signing + broadcast, off-chain verification
- SVM: Ed25519 messages, transaction signing + broadcast, gas sponsorship
- Key import: bring external private key into MPC custody
- Key export: export raw private keys for migration/disaster recovery
- Delegated access: per-wallet API key flow with RSA-OAEP-SHA256 + AES-256-GCM webhook decryption
- Password & state queries: rotate backup password, check operation prerequisites
---
## Critical Reference
| Correct | Incorrect | Notes |
|---|---|---|
| Pin exact version `<version>1.2.0-beta</version>` | Use version ranges | Breaking changes in beta releases |
| Construct client once at startup, reuse | Create client per request | Reinitializes native MPC layer each call |
| `client.close()` on shutdown | Skip close / use try-with-resources in handlers | `try-with-resources` defeats connection-pool reuse in request handlers |
| Persist `WalletProperties` + `List<ServerKeyShare>` | Assume SDK stores state | SDK is stateless — you own persistence |
| Store key shares in secrets vault (HSM/KMS) | Store in plaintext DB | Sensitive MPC key material |
| Add `os-maven-plugin` for native binary resolution | Skip the build extension | Build fails to find platform-specific native JAR |
---
## Troubleshooting — Common Issues
### 1 — `UnsatisfiedLinkError` at runtime
v1.2.0-beta ships native binaries for `linux-x86_64` and `osx-aarch_64` only. Other platforms are not yet supported — contact Dynamic representative.
### 2 — Embedded wallets not enabled
Go to **Wallets** in the Dynamic dashboard and enable embedded wallets.
### 3 — Chains not enabled
Go to **Chains & Networks** and enable Ethereum or Solana depending on which client you're using.
### 4 — `os-maven-plugin` not resolving classifier
Ensure the plugin is in `<build><extensions>`. Alternative: pin the native classifier explicitly with `<classifier>linux-x86_64</classifier>` on `dynamic-waas-sdk-mpc-natives`.
### 5 — API token permissions
Go to **Developer → API Tokens** and confirm the token has wallet operation scopes. See https://www.dynamic.xyz/docs/overview/developer-dashboard/api-token-permissions.
If the issue persists, check https://docs.dynamic.xyz/overview/troubleshooting/general.
---
## Beyond This Quickstart
This prompt covers initial setup only. For deeper guides (EVM transactions, Solana operations, key import/export, delegated access, etc.), fetch the full documentation index:
**https://www.dynamic.xyz/docs/llms.txt**
This file contains every page in the Dynamic documentation organized by SDK. Search for the "Java SDK" section to discover all available pages and their URLs. Use it as your navigation source for any question beyond what this quickstart covers.
Alternatively, if the Dynamic MCP server is available in your environment, query it directly for any documentation page.
Prerequisites
- JDK 21 LTS or higher (install via Adoptium or your distribution’s package manager)
- Dynamic Account: set up your project and get credentials from the Dynamic Developer Console
- A supported
(os, arch)host. v1.2.0-beta ships native binaries forlinux-x86_64andosx-aarch_64. The other classifiers (linux-aarch_64,osx-x86_64,windows-x86_64) are not yet supported and will throwUnsatisfiedLinkErrorat runtime; contact your Dynamic representative if you need one of those platforms.
Install
1. Install the artifacts into your local Maven repo
After downloadingdynamic-waas-sdk-java-1.2.0-beta.zip:unzip dynamic-waas-sdk-java-1.2.0-beta.zip
cp -r dynamic-waas-sdk-java-1.2.0-beta/m2/xyz ~/.m2/repository/
Prefer not to copy into your shared
~/.m2? Add a file-based repository to your pom.xml instead:<repositories>
<repository>
<id>dynamic-waas-local</id>
<url>file://${project.basedir}/path/to/dynamic-waas-sdk-java-1.2.0-beta/m2</url>
</repository>
</repositories>
2. Add the chain modules you need to your pom.xml
Both pull in the core + MPC + native modules transitively:<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-evm</artifactId>
<version>1.2.0-beta</version>
</dependency>
<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-svm</artifactId>
<version>1.2.0-beta</version>
</dependency>
3. Add the os-maven-plugin build extension
The MPC native binary ships in classifier-suffixed JARs (...-osx-aarch_64.jar, ...-linux-x86_64.jar, etc.). Add os-maven-plugin so ${os.detected.classifier} resolves to the right one at build time:<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
</build>
Alternative: pin the classifier explicitly. If you can’t add the build extension (for example, you’re producing portable CI artifacts where the build host’s OS differs from the runtime target), depend on
dynamic-waas-sdk-mpc-natives directly with a hard-coded classifier:<dependency>
<groupId>xyz.dynamic.waas</groupId>
<artifactId>dynamic-waas-sdk-mpc-natives</artifactId>
<version>1.2.0-beta</version>
<classifier>linux-x86_64</classifier>
</dependency>
osdetector-gradle-plugin, which exposes osdetector.classifier.Get Your Credentials
Navigate to the Dynamic Developer Console and:- Copy your
Environment ID - Create a new API token

Enable Features in Dashboard
Before you can use wallet features, enable them in your Dynamic developer console:- Go to your Dynamic Developer Console
- Select your project
- Go to Wallets and enable embedded wallets
- Go to Chains and enable the networks you want to support (Ethereum, Solana, etc.)
Environment Variables
# .env
export DYNAMIC_ENV_ID=your_environment_id_here
export DYNAMIC_API_TOKEN=your_api_token_here
authenticateApiToken.Initialize the Client
The Java SDK is chain-first: constructDynamicEvmWalletClient for Ethereum / EVM chains and DynamicSvmWalletClient for Solana. Both are AutoCloseable and hold a pooled HTTP client + native MPC handles. Construct once at application startup and reuse across requests; only close() at shutdown.import xyz.dynamic.waas.DynamicWalletClientOpts;
import xyz.dynamic.waas.evm.DynamicEvmWalletClient;
String envId = System.getenv("DYNAMIC_ENV_ID");
String apiToken = System.getenv("DYNAMIC_API_TOKEN");
// Application-scoped: construct once, reuse, close on shutdown.
DynamicEvmWalletClient client = new DynamicEvmWalletClient(
DynamicWalletClientOpts.builder(envId).build());
client.authenticateApiToken(apiToken).join();
// ... wallet ops here, across many requests ...
// On shutdown (e.g. Spring `@PreDestroy`, manual hook):
client.close();
try-with-resources (try (var client = ...) { ... }) works for short-lived scripts and tests, but in a request handler it closes the client at the end of every request, defeating the connection-pool reuse and reinitializing the native MPC layer each call.CompletableFuture<T>. Use .join() for blocking call sites, .thenCompose(...) to compose asynchronously.Stateless contract
The Java SDK is stateless from day 1: it does not hold wallet state between calls.createWalletAccount() returns a KeygenResult carrying two pieces of state you must persist:WalletProperties: non-sensitive identity + backup-pointer info. Cache it in Redis / Postgres. Pass it to every subsequent sign / export operation.List<ServerKeyShare>: sensitive MPC key material. Store in a secrets vault (HSM, KMS-wrapped column, AWS Secrets Manager, etc.).
WalletProperties is the same concept as the Node SDK’s WalletMetadata, with the same fields (walletId, accountAddress, chainName, derivationPath, externalServerKeySharesBackupInfo). WalletProperties.toJson() / fromJson() use snake_case so it round-trips cleanly with the Python and Rust SDKs.Threshold signature schemes
When you configure signing, you choose how key shares combine to authorize a signature:TWO_OF_TWO(default): requires your server and Dynamic’s infrastructure to sign together.TWO_OF_THREE: requires two of three shares to sign. Tolerates one party being offline.
Pointing at preprod or sandbox
The SDK defaults to production. To target a different environment, passbaseApiUrl(...) on DynamicWalletClientOpts:var opts = DynamicWalletClientOpts.builder(envId)
.baseApiUrl("https://app.dynamic-preprod.xyz")
.build();
SDK capabilities
- Embedded wallets: create and use MPC wallets from your server (EVM + Solana)
- EVM signing: EIP-191 messages, EIP-712 typed data, EIP-1559 transaction signing + broadcast, off-chain signature verification
- EVM gas sponsorship:
sponsorTransactionplus status polling (getSponsoredTransactionStatus,waitForSponsoredTransaction) - SVM signing: Ed25519 messages, Solana transaction signing + broadcast, gas sponsorship, local address derivation
- Key import: bring an external private key into MPC custody (EVM + Solana)
- Key export: export raw private keys for migration / disaster recovery
- Backup & password checks:
backupKeyShares/recoverKeySharesfor Dynamic-stored key-share backups, plus localverifyPassword - Delegated access: per-wallet API key flow with
RSA-OAEP-SHA256 + AES-256-GCMwebhook decryption, mirroring Node SDK v1 / Python / Rust
Logging
The SDK uses JDK Platform Logging (System.Logger). Customers using SLF4J / Logback can route SDK logs by adding the slf4j-jdk-platform-logging adapter to their classpath.Next Steps
EVM Support
- Create EVM Wallets: create Ethereum wallets
- Sign EVM Messages: EIP-191 personal_sign + off-chain verification
- Sign Typed Data (EIP-712)
- Sign EVM Transactions: EIP-1559 transaction signing
- Send EVM Transactions: sign + broadcast in one call
- Sponsor EVM Transactions: gas sponsorship for EVM wallets
- Export EVM Private Key
- EVM Delegated Access
Solana Support
- Create SVM Wallets
- Sign SVM Messages: Ed25519 raw-bytes signing
- Sign SVM Transactions
- Send SVM Transactions
- Sponsor Transactions: gas sponsorship
- Export SVM Private Key
- SVM Delegated Access
Cross-chain & lifecycle
- Import a Private Key — Bring an external key into MPC
- Password & State Queries — Rotate the backup password, check operation prerequisites