Wallets & Key Management
Manage keypairs, HD derivation, and signing flows in Rust wallets and validators without leaking secrets.
Recipe
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
struct KeyStore {
signing: SigningKey,
}
impl KeyStore {
fn generate() -> Self {
Self {
signing: SigningKey::generate(&mut OsRng),
}
}
fn sign(&self, msg: &[u8]) -> ed25519_dalek::Signature {
use ed25519_dalek::Signer;
self.signing.sign(msg)
}
}When to reach for this:
- CLI wallets and bot signers for automated strategies
- Custodial services with HSM-backed signing APIs
- Validator identity keys separate from operational hot wallets
Working Example
HD-style path documentation with explicit derivation crate boundary:
use ed25519_dalek::SigningKey;
use bip39::{Language, Mnemonic, Seed};
use rand::rngs::OsRng;
fn derive_from_mnemonic(words: &str, path: &str) -> anyhow::Result<SigningKey> {
let mnemonic = Mnemonic::parse_in(Language::English, words)?;
let seed = Seed::new(&mnemonic, "");
// Use chain-specific SLIP-0010 or ed25519 derivation crate for `path`
let _ = (seed.as_bytes(), path);
Ok(SigningKey::generate(&mut OsRng)) // placeholder: swap for real derive
}What this demonstrates:
- Mnemonic parsing without printing seed to stdout
- Separation between seed generation and chain-specific path derivation
- Error propagation with
Resultinstead of panics in wallet UX
Deep Dive
How It Works
- Mnemonic encodes entropy as words; BIP-39 standardizes word lists.
- HD paths like
m/44'/501'/0'/0'map accounts per chain (Solana 501). - Keystore JSON encrypts private key with password-derived key.
- HSM/MPC move signing operation off laptop memory.
Key Tiers
| Tier | Location | Use |
|---|---|---|
| Hot | App memory | Bots, small balances |
| Warm | Encrypted disk | Daily ops with limits |
| Cold | Hardware/offline | Treasury, upgrade authority |
Gotchas
- Committing mnemonics to git - instant fund loss. Fix: git-secrets scan; use env + KMS in CI only for test keys.
- Same key for deploy and treasury - one leak drains all. Fix: role-separated keys with multisig on treasury.
- Derivation path typo - user sees empty wallet. Fix: display path in UI and test vectors against known addresses.
- Signing raw user JSON - phishing swaps fields. Fix: structured typed messages with domain tags.
- Clipboard seed backup - malware harvests clipboard. Fix: discourage clipboard; prefer hardware display.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Browser extension wallet | Consumer dApp UX | Headless server signing |
| Fireblocks/Copper custody | Institutional scale | Self-custody product |
| Turnkey API wallets | Hosted signing SLA | Full on-prem requirement |
| Paper wallet | Cold storage drill | Frequent signing needs |
FAQs
Where to store production keys?
HSM or cloud KMS with IAM policy; hot keys only hold limited daily float.
BIP-44 coin types?
Coin type in path selects curve/chain - look up SLIP-0044 registry for your network.
How to rotate keys?
On-chain authority transfer instruction, dual-run period, then revoke old key permissions.
Multisig patterns?
Squads, Gnosis Safe (EVM), or native multisig programs - pick per chain capabilities.
Testing without real funds?
Devnet keys in env vars excluded from prod builds via compile-time cfg flags.
Encrypted keystore?
scrypt + AES from Ethereum keystore format or age-encrypted files for CLI wallets.
Validator keys?
Separate consensus and commission accounts; never reuse browser extension seed.
Signing in CI?
Ephemeral keys only; production signing should never run in shared CI runners.
Audit logging?
Log pubkey, instruction type, and simulation result - never signature preimage secrets.
Crypto primitives?
See Applied Cryptography for algorithms.
Related
- Applied Cryptography - curves and hashes
- Solana Programs - program authorities
- Security & Auditing - key handling audits
- Blockchain Basics in Rust - keygen example
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.