> ## 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.

# Create EVM Wallet

> Learn how to create and manage EVM server wallets with Dynamic's Java SDK

## Overview

This guide walks you through creating EVM wallets using Dynamic's Java SDK. You'll learn how to set up different threshold signature schemes and understand the security implications of each choice.

## Prerequisites

Before you begin, make sure you have:

* [Set up your Dynamic project](/java/quickstart)
* [Configured the Maven dependencies](/java/quickstart#install)
* [Enabled EVM chains in your dashboard](https://app.dynamic.xyz/dashboard/chains)

## Step 1: Choose Your Security Model

Dynamic supports two threshold signature schemes, each offering different security and availability trade-offs:

### `TWO_OF_TWO` (Default)

* **Security**: Highest — requires both your server and Dynamic's infrastructure
* **Availability**: Lower — both parties must be online
* **Use case**: High-value transactions, maximum security

### `TWO_OF_THREE`

* **Security**: High — requires 2 out of 3 shares
* **Availability**: Medium — can tolerate one party being offline
* **Use case**: Balanced security and availability

## Step 2: Create Your First Wallet

`DynamicEvmWalletClient::createWalletAccount` runs an MPC keygen ceremony and returns a `KeygenResult` carrying the wallet metadata and the server key shares the customer must persist.

```java theme={"system"}
import xyz.dynamic.waas.DynamicWalletClientOpts;
import xyz.dynamic.waas.KeygenResult;
import xyz.dynamic.waas.core.types.ThresholdSignatureScheme;
import xyz.dynamic.waas.evm.DynamicEvmWalletClient;
import xyz.dynamic.waas.opts.CreateWalletOpts;

public final class CreateEvmWalletExample {
    public static KeygenResult createEvmWallet(
            String envId,
            String apiToken,
            ThresholdSignatureScheme scheme,
            String password
    ) {
        try (var client = new DynamicEvmWalletClient(
                DynamicWalletClientOpts.builder(envId).build())) {

            client.authenticateApiToken(apiToken).join();

            return client.createWalletAccount(CreateWalletOpts.builder()
                .thresholdSignatureScheme(scheme)
                .password(password)        // required when backUpToDynamic = true
                .backUpToDynamic(true)
                .build()
            ).join();
        }
    }

    public static void main(String[] args) {
        KeygenResult result = createEvmWallet(
            System.getenv("DYNAMIC_ENV_ID"),
            System.getenv("DYNAMIC_API_TOKEN"),
            ThresholdSignatureScheme.TWO_OF_TWO,
            "your-secure-password"
        );

        System.out.println("Wallet created: " + result.walletProperties().accountAddress());
    }
}
```

## Step 3: Handle Errors

The SDK throws `DynamicSdkException` subclasses inside the `CompletableFuture` — unwrap with `CompletionException::getCause` and match on the type:

```java theme={"system"}
import java.util.concurrent.CompletionException;
import xyz.dynamic.waas.core.exceptions.AuthException;
import xyz.dynamic.waas.core.exceptions.DynamicSdkException;
import xyz.dynamic.waas.core.exceptions.InvalidArgumentException;

try {
    KeygenResult result = client.createWalletAccount(opts).join();
    System.out.println("Wallet created: " + result.walletProperties().accountAddress());
} catch (CompletionException ce) {
    Throwable cause = ce.getCause();
    if (cause instanceof InvalidArgumentException iae && iae.getMessage().contains("password")) {
        System.err.println("Password is required when backing up to Dynamic.");
    } else if (cause instanceof AuthException) {
        System.err.println("Invalid session — re-authenticate with authenticateApiToken.");
    } else if (cause instanceof DynamicSdkException dse) {
        System.err.println("Wallet creation failed: " + dse.getMessage());
    } else {
        throw ce;
    }
}
```

## Step 4: Persist `WalletProperties` and `List<ServerKeyShare>`

`KeygenResult` carries two pieces of state — each belongs in a different storage tier:

```java theme={"system"}
KeygenResult result = client.createWalletAccount(opts).join();

WalletProperties walletProperties = result.walletProperties();
List<ServerKeyShare> externalServerKeyShares = result.externalServerKeyShares();

// WalletProperties — non-sensitive identity + backup-pointer info. Cache it.
redis.set(
    "wallet:" + walletProperties.accountAddress(),
    walletProperties.toJson()
);

// List<ServerKeyShare> — sensitive MPC key material. Vault it.
vault.write(
    "wallet:" + walletProperties.accountAddress() + "/shares",
    ServerKeyShare.toJsonList(externalServerKeyShares)
);
```

The SDK is stateless and you must pass `WalletProperties` to every subsequent sign / export operation. See [Storage Best Practices](/java/storage-best-practices) for the full pattern.

## Best Practices

1. **Password Security** — use strong, unique passwords per wallet; prefer `char[]` over `String` so the value can be cleared deterministically; never log it
2. **Error Handling** — match on exception subclasses (`InvalidArgumentException`, `AuthException`, `DynamicSdkException`) rather than string-matching
3. **Resource Management** — `DynamicEvmWalletClient` is `AutoCloseable`; use try-with-resources or call `close()` explicitly to release the HTTP client + native handles
4. **Backup Strategy** — if `.backUpToDynamic(false)`, persist shares to your vault *before* returning success to the caller

## Next Steps

Now that you've created a wallet, you can:

* [Sign messages](/java/evm/sign-messages)
* [Sign typed data (EIP-712)](/java/evm/sign-typed-data)
* [Sign transactions](/java/evm/sign-transaction)
* [Send transactions](/java/evm/send-transaction)
* [Export the private key](/java/evm/export-private-key)
* [Use delegated access](/java/evm/delegated-access)
