BDK

A modern, lightweight, descriptor-based wallet library written in Rust!

Crate Info MIT or Apache-2.0 Licensed CI Status API Docs Rustc Version 1.63.0+ Chat on Discord

Project Homepage | Documentation

## About The `bdk` library aims to be the core building block for Bitcoin wallets of any kind. * It uses [Miniscript](https://github.com/rust-bitcoin/rust-miniscript) to support descriptors with generalized conditions. This exact same library can be used to build single-sig wallets, multisigs, timelocked contracts and more. * It supports multiple blockchain backends and databases, allowing developers to choose exactly what's right for their projects. * It's built to be cross-platform: the core logic works on desktop, mobile, and even WebAssembly. * It's very easy to extend: developers can implement customized logic for blockchain backends, databases, signers, coin selection, and more, without having to fork and modify this library. ## Examples ### Sync the balance of a descriptor ```rust,no_run use bdk::Wallet; use bdk::database::MemoryDatabase; use bdk::blockchain::ElectrumBlockchain; use bdk::SyncOptions; use bdk::electrum_client::Client; use bdk::bitcoin::Network; fn main() -> Result<(), bdk::Error> { let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), Network::Testnet, MemoryDatabase::default(), )?; wallet.sync(&blockchain, SyncOptions::default())?; println!("Descriptor balance: {} SAT", wallet.get_balance()?); Ok(()) } ``` ### Generate a few addresses ```rust use bdk::{Wallet, database::MemoryDatabase}; use bdk::wallet::AddressIndex::New; use bdk::bitcoin::Network; fn main() -> Result<(), bdk::Error> { let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), Network::Testnet, MemoryDatabase::default(), )?; println!("Address #0: {}", wallet.get_address(New)?); println!("Address #1: {}", wallet.get_address(New)?); println!("Address #2: {}", wallet.get_address(New)?); Ok(()) } ``` ### Create a transaction ```rust,no_run use bdk::{FeeRate, Wallet, SyncOptions}; use bdk::database::MemoryDatabase; use bdk::blockchain::ElectrumBlockchain; use bdk::electrum_client::Client; use bdk::wallet::AddressIndex::New; use bitcoin::base64; use bdk::bitcoin::consensus::serialize; use bdk::bitcoin::Network; fn main() -> Result<(), bdk::Error> { let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), Network::Testnet, MemoryDatabase::default(), )?; wallet.sync(&blockchain, SyncOptions::default())?; let send_to = wallet.get_address(New)?; let (psbt, details) = { let mut builder = wallet.build_tx(); builder .add_recipient(send_to.script_pubkey(), 50_000) .enable_rbf() .do_not_spend_change() .fee_rate(FeeRate::from_sat_per_vb(5.0)); builder.finish()? }; println!("Transaction details: {:#?}", details); println!("Unsigned PSBT: {}", base64::encode(psbt.serialize())); Ok(()) } ``` ### Sign a transaction ```rust,no_run use bdk::{Wallet, SignOptions, database::MemoryDatabase}; use bitcoin::base64; use bdk::bitcoin::consensus::deserialize; use bdk::bitcoin::{psbt::Psbt, Network}; fn main() -> Result<(), bdk::Error> { let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"), Network::Testnet, MemoryDatabase::default(), )?; let psbt = "..."; let mut psbt = Psbt::deserialize(&base64::decode(psbt).unwrap())?; let _finalized = wallet.sign(&mut psbt, SignOptions::default())?; Ok(()) } ``` ## Testing ### Unit testing ```bash cargo test ``` ### Integration testing Integration testing require testing features, for example: ```bash cargo test --features test-electrum ``` The other options are `test-esplora`, `test-rpc` or `test-rpc-legacy` which runs against an older version of Bitcoin Core. Note that `electrs` and `bitcoind` binaries are automatically downloaded (on mac and linux), to specify you already have installed binaries you must use `--no-default-features` and provide `BITCOIND_EXE` and `ELECTRS_EXE` as environment variables. ## Running under WASM If you want to run this library under WASM you will probably have to add the following lines to you `Cargo.toml`: ```toml [dependencies] getrandom = { version = "0.2", features = ["js"] } ``` This enables the `rand` crate to work in environments where JavaScript is available. See [this link](https://docs.rs/getrandom/0.2.8/getrandom/#webassembly-support) to learn more. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ## Minimum Supported Rust Version (MSRV) This library should compile with any combination of features with Rust 1.63.0. To build with the MSRV you will need to pin dependencies as follows: ```shell cargo update -p tokio --precise "1.38.1" cargo update -p tokio-util --precise "0.7.11" cargo update -p home --precise "0.5.5" cargo update -p regex --precise "1.7.3" cargo update -p security-framework-sys --precise "2.11.1" cargo update -p url --precise "2.5.0" cargo update -p rustls@0.23.18 --precise "0.23.17" cargo update -p hashbrown@0.15.1 --precise "0.15.0" ```