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 Rust 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:
  • Security: Highest — requires both your server and Dynamic’s infrastructure
  • Availability: Lower — both parties must be online
  • Use case: High-value transactions, maximum security

TwoOfThree

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

Step 2: Choose Your Backup Mode

Every call to create_wallet_account returns a Vec<ServerKeyShare> — the customer-side half of the MPC key. You always vault this share in your own infrastructure for day-to-day signing; the chain clients in 0.0.3 require it as an explicit argument on every sign / export call. The back_up_to_dynamic flag controls whether Dynamic also keeps an encrypted copy for disaster recovery: The SDK encrypts your share with password (AES-256-GCM) and uploads the encrypted blob to Dynamic’s key share service. You still receive the share locally in the return value and still vault it yourself. If you ever lose your vaulted copy, you can recover it from Dynamic’s backup using run_recover_key_shares with the same password.
password is required when back_up_to_dynamic: true. The SDK rejects the call with Error::InvalidArgument if you pass None. The password never leaves your server — it’s used locally to derive an AES-256-GCM key that wraps the share before upload, and the same password is needed later to unwrap the recovered blob.

back_up_to_dynamic: false

Dynamic does not store any copy of the share. You are the only holder. password is optional in this mode. Losing the share means permanently losing access to the wallet.
ModeLocal vaultDynamic backuppassword at createDisaster recovery
back_up_to_dynamic: trueRequiredEncrypted copy storedRequiredrun_recover_key_shares + password
back_up_to_dynamic: falseRequiredNoneOptionalNone — share loss = wallet loss
Don’t reuse a single password across all wallets — derive one per wallet from a KMS-wrapped secret. A leaked password + leaked walletId lets an attacker decrypt the share from Dynamic’s backup.

Step 3: Create Your First Wallet

Here’s a complete example of creating an EVM wallet:
use dynamic_waas_sdk::{
    DynamicWalletClient, DynamicWalletClientOpts, ServerKeyShare,
    ThresholdSignatureScheme, WalletProperties,
};
use dynamic_waas_sdk_evm::DynamicEvmWalletClient;

pub async fn authenticated_client(
    env_id: &str,
    api_token: &str,
) -> dynamic_waas_sdk::Result<DynamicWalletClient> {
    let mut client = DynamicWalletClient::new(
        DynamicWalletClientOpts::new(env_id),
    )?;
    client.authenticate_api_token(api_token).await?;
    Ok(client)
}

pub async fn create_evm_wallet(
    client: &DynamicWalletClient,
    threshold_signature_scheme: ThresholdSignatureScheme,
    password: String, // required when back_up_to_dynamic = true
) -> dynamic_waas_sdk::Result<(WalletProperties, Vec<ServerKeyShare>)> {
    let evm = DynamicEvmWalletClient::new(client);

    evm.create_wallet_account(
        threshold_signature_scheme,
        Some(password),
        /* back_up_to_dynamic */ true,
    )
    .await
}

// Usage
let client = authenticated_client(&env_id, &api_token).await?;
let (wallet_properties, external_server_key_shares) = create_evm_wallet(
    &client,
    ThresholdSignatureScheme::TwoOfTwo,
    "your-secure-password".to_string(),
).await?;

println!("Wallet created: {}", wallet_properties.account_address);

Step 4: Handle Errors

The SDK returns dynamic_waas_sdk::Error. Match on the variants you care about:
use dynamic_waas_sdk::Error;

match create_evm_wallet(&client, ThresholdSignatureScheme::TwoOfTwo, password).await {
    Ok((wp, _shares)) => println!("Wallet created: {}", wp.account_address),
    Err(Error::InvalidArgument(msg)) if msg.contains("password") => {
        eprintln!("Password is required when backing up to Dynamic");
    }
    Err(Error::Auth(_)) => {
        eprintln!("Invalid session — re-authenticate with authenticate_api_token");
    }
    Err(e) => eprintln!("Wallet creation failed: {e:?}"),
}

Step 5: Persist WalletProperties and Vec<ServerKeyShare>

create_wallet_account() returns two pieces of state — each belongs in a different storage tier:
let (wallet_properties, external_server_key_shares) = evm
    .create_wallet_account(
        ThresholdSignatureScheme::TwoOfTwo,
        Some("your-secure-password".to_string()),
        /* back_up_to_dynamic */ true,
    )
    .await?;

// WalletProperties — non-sensitive identity + backup-pointer info. Cache it.
redis.set(
    format!("wallet:{}", wallet_properties.account_address),
    serde_json::to_string(&wallet_properties)?,
).await?;

// Vec<ServerKeyShare> — sensitive MPC key material. Vault it.
vault.write(
    format!("wallet:{}/shares", wallet_properties.account_address),
    serde_json::to_vec(&external_server_key_shares)?,
).await?;
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; never log them
  2. Error Handling — match on dynamic_waas_sdk::Error variants rather than string-matching
  3. Tracing — the SDK uses tracing::instrument and skips sensitive args; mirror that in your code
  4. Backup Strategy — if back_up_to_dynamic: false, persist shares to a vault before returning success to the caller

Next Steps

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