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.

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:

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.
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:
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:
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 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 ManagementDynamicEvmWalletClient 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: