| Crates.io | khodpay-bip44 |
| lib.rs | khodpay-bip44 |
| version | 0.1.0 |
| created_at | 2025-11-02 10:59:25.427005+00 |
| updated_at | 2025-11-02 10:59:25.427005+00 |
| description | Production-ready Rust implementation of BIP44 multi-account hierarchy for deterministic wallets |
| homepage | https://github.com/khodpay/rust-wallet |
| repository | https://github.com/khodpay/rust-wallet |
| max_upload_size | |
| id | 1912978 |
| size | 446,504 |
Production-ready Rust implementation of BIP-44 (Multi-Account Hierarchy for Deterministic Wallets).
Add this to your Cargo.toml:
[dependencies]
khodpay-bip44 = "0.1.0"
khodpay-bip32 = "0.2.0"
khodpay-bip39 = "0.2.0"
For serialization support:
[dependencies]
khodpay-bip44 = { version = "0.1.0", features = ["serde"] }
use khodpay_bip44::{Wallet, Purpose, CoinType, Language};
use khodpay_bip32::Network;
// Create a wallet from a mnemonic
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mut wallet = Wallet::from_mnemonic(
mnemonic,
"", // password (optional)
Language::English,
Network::BitcoinMainnet,
)?;
// Get the first Bitcoin account
let account = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
// Derive receiving addresses
let addr0 = account.derive_external(0)?; // m/44'/0'/0'/0/0
let addr1 = account.derive_external(1)?; // m/44'/0'/0'/0/1
// Derive change addresses
let change0 = account.derive_internal(0)?; // m/44'/0'/0'/1/0
BIP-44 defines a logical hierarchy for deterministic wallets:
m / purpose' / coin_type' / account' / change / address_index
| Level | Hardened | Description | Example |
|---|---|---|---|
| purpose | Yes (') | BIP standard (44, 49, 84, 86) | 44' |
| coin_type | Yes (') | Cryptocurrency type (SLIP-44) | 0' (Bitcoin) |
| account | Yes (') | Account index | 0' |
| change | No | 0=external (receiving), 1=internal (change) | 0 |
| address_index | No | Address index within chain | 0 |
m/44'/0'/0'/0/0m/44'/0'/0'/1/0m/44'/60'/0'/0/0m/84'/0'/0'/0/0m/86'/0'/0'/0/0use khodpay_bip44::{Wallet, Purpose, CoinType};
let mut wallet = Wallet::from_english_mnemonic(mnemonic, "", Network::BitcoinMainnet)?;
// Bitcoin account
let btc = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
let btc_addr = btc.derive_external(0)?;
// Ethereum account
let eth = wallet.get_account(Purpose::BIP44, CoinType::Ethereum, 0)?;
let eth_addr = eth.derive_external(0)?;
// Litecoin account
let ltc = wallet.get_account(Purpose::BIP44, CoinType::Litecoin, 0)?;
let ltc_addr = ltc.derive_external(0)?;
use khodpay_bip44::WalletBuilder;
let mut wallet = WalletBuilder::new()
.mnemonic("your mnemonic phrase here")
.password("optional-password")
.language(Language::English)
.network(Network::BitcoinMainnet)
.build()?;
use khodpay_bip44::Chain;
let account = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
// Generate 20 receiving addresses at once
let addresses = account.derive_address_range(Chain::External, 0, 20)?;
// Generate 10 change addresses
let change_addresses = account.derive_address_range(Chain::Internal, 0, 10)?;
use khodpay_bip44::Bip44Path;
// Parse a BIP-44 path
let path: Bip44Path = "m/44'/0'/0'/0/0".parse()?;
// Access path components
println!("Purpose: {}", path.purpose().value());
println!("Coin: {}", path.coin_type().index());
println!("Account: {}", path.account());
println!("Chain: {:?}", path.chain());
println!("Index: {}", path.address_index());
// Convert back to string
assert_eq!(path.to_string(), "m/44'/0'/0'/0/0");
// BIP-84: Native SegWit (bc1q... addresses)
let segwit = wallet.get_account(Purpose::BIP84, CoinType::Bitcoin, 0)?;
let segwit_addr = segwit.derive_external(0)?;
// BIP-86: Taproot (bc1p... addresses)
let taproot = wallet.get_account(Purpose::BIP86, CoinType::Bitcoin, 0)?;
let taproot_addr = taproot.derive_external(0)?;
// First access - derives and caches
let account1 = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
// Second access - uses cached account (faster)
let account2 = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
// Check cache size
println!("Cached accounts: {}", wallet.cached_account_count());
// Clear cache if needed
wallet.clear_cache();
use khodpay_bip44::AddressIterator;
let account = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
// Iterate over addresses
for (i, result) in AddressIterator::new_external(&account).take(10).enumerate() {
let address = result?;
println!("Address {}: depth={}", i, address.depth());
}
| Standard | Purpose | Address Type | Example |
|---|---|---|---|
| BIP-44 | 44' | Legacy P2PKH | 1... |
| BIP-49 | 49' | SegWit wrapped in P2SH | 3... |
| BIP-84 | 84' | Native SegWit (Bech32) | bc1q... |
| BIP-86 | 86' | Taproot (Bech32m) | bc1p... |
This crate supports all SLIP-44 registered coin types:
| Cryptocurrency | Coin Type | Enum |
|---|---|---|
| Bitcoin | 0 | CoinType::Bitcoin |
| Bitcoin Testnet | 1 | CoinType::BitcoinTestnet |
| Litecoin | 2 | CoinType::Litecoin |
| Dogecoin | 3 | CoinType::Dogecoin |
| Dash | 5 | CoinType::Dash |
| Ethereum | 60 | CoinType::Ethereum |
| Ethereum Classic | 61 | CoinType::EthereumClassic |
| Custom | Any | CoinType::Custom(u32) |
Mnemonic Storage
Key Material Handling
Password Protection
Gap Limit
AccountDiscovery trait for wallet recoveryNetwork Security
// ✅ Good: Use password protection
let wallet = Wallet::from_mnemonic(
mnemonic,
"strong-password-here", // BIP-39 passphrase
Language::English,
Network::BitcoinMainnet,
)?;
// ✅ Good: Clear sensitive data
drop(wallet); // Clears cached accounts
// ✅ Good: Use testnet for development
let test_wallet = Wallet::from_english_mnemonic(
mnemonic,
"",
Network::BitcoinTestnet, // Use testnet
)?;
For wallet recovery, use the gap limit to discover used accounts:
use khodpay_bip44::{AccountDiscovery, GapLimitChecker, DEFAULT_GAP_LIMIT};
// Implement AccountDiscovery trait for your blockchain client
struct MyBlockchain;
impl AccountDiscovery for MyBlockchain {
fn has_transactions(&self, address: &str) -> Result<bool, Box<dyn std::error::Error>> {
// Check if address has transactions on blockchain
Ok(false) // Implement your logic here
}
}
// Scan for used addresses
let blockchain = MyBlockchain;
let checker = GapLimitChecker::new(DEFAULT_GAP_LIMIT);
let account = wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0)?;
let result = checker.scan_chain(&account, Chain::External, &blockchain)?;
println!("Used addresses: {}", result.used_count);
println!("Last used index: {:?}", result.last_used_index);
Enable the serde feature for serialization support:
use khodpay_bip44::{Bip44Path, AccountMetadata};
// Serialize path
let path = Bip44Path::new(Purpose::BIP44, CoinType::Bitcoin, 0, Chain::External, 0)?;
let json = serde_json::to_string(&path)?;
// Deserialize path
let parsed: Bip44Path = serde_json::from_str(&json)?;
// Serialize account metadata (safe - no private keys)
let metadata = AccountMetadata::from_account(&account);
let metadata_json = serde_json::to_string(&metadata)?;
use khodpay_bip44::Error;
match wallet.get_account(Purpose::BIP44, CoinType::Bitcoin, 0) {
Ok(account) => {
// Use account
}
Err(Error::InvalidMnemonic(msg)) => {
eprintln!("Invalid mnemonic: {}", msg);
}
Err(Error::KeyDerivation(msg)) => {
eprintln!("Key derivation failed: {}", msg);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
Run the test suite:
# Run all tests
cargo test -p khodpay-bip44
# Run with serde feature
cargo test -p khodpay-bip44 --features serde
# Run doc tests
cargo test -p khodpay-bip44 --doc
# Run integration tests
cargo test -p khodpay-bip44 --test integration
cargo test -p khodpay-bip44 --test test_vectors
cargo test -p khodpay-bip44 --test compatibility
cargo test -p khodpay-bip44 --test edge_cases
This implementation is compatible with:
See the examples directory for complete working examples:
basic.rs - Basic wallet usagemulti_coin.rs - Multi-cryptocurrency walletmulti_account.rs - Multiple accounts per coindiscovery.rs - Account discovery with gap limitContributions are welcome! Please see CONTRIBUTING.md for guidelines.
Licensed under either of:
at your option.
See CHANGELOG.md for version history.