bdk 0.29.0

A modern, lightweight, descriptor-based wallet library
Documentation
use bdk::bitcoin::{Address, Network};
use bdk::blockchain::{Blockchain, ElectrumBlockchain};
use bdk::database::MemoryDatabase;
use bdk::hwi::HWIClient;
use bdk::miniscript::{Descriptor, DescriptorPublicKey};
use bdk::signer::SignerOrdering;
use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex};
use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet};
use electrum_client::Client;
use std::str::FromStr;
use std::sync::Arc;

// This example shows how to sync a wallet, create a transaction, sign it
// and broadcast it using an external hardware wallet.
// The hardware wallet must be connected to the computer and unlocked before
// running the example. Also, the `hwi` python package should be installed
// and available in the environment.
//
// To avoid loss of funds, consider using an hardware wallet simulator:
// * Coldcard: https://github.com/Coldcard/firmware
// * Ledger: https://github.com/LedgerHQ/speculos
// * Trezor: https://docs.trezor.io/trezor-firmware/core/emulator/index.html
fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Hold tight, I'm connecting to your hardware wallet...");

    // Listing all the available hardware wallet devices...
    let mut devices = HWIClient::enumerate()?;
    if devices.is_empty() {
        panic!("No devices found. Either plug in a hardware wallet, or start a simulator.");
    }
    let first_device = devices.remove(0)?;
    // ...and creating a client out of the first one
    let client = HWIClient::get_client(&first_device, true, Network::Testnet.into())?;
    println!("Look what I found, a {}!", first_device.model);

    // Getting the HW's public descriptors
    let descriptors = client.get_descriptors::<Descriptor<DescriptorPublicKey>>(None)?;
    println!(
        "The hardware wallet's descriptor is: {}",
        descriptors.receive[0]
    );

    // Creating a custom signer from the device
    let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
    let mut wallet = Wallet::new(
        descriptors.receive[0].clone(),
        Some(descriptors.internal[0].clone()),
        Network::Testnet,
        MemoryDatabase::default(),
    )?;

    // Adding the hardware signer to the BDK wallet
    wallet.add_signer(
        KeychainKind::External,
        SignerOrdering(200),
        Arc::new(custom_signer),
    );

    // create client for Blockstream's testnet electrum server
    let blockchain =
        ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);

    println!("Syncing the wallet...");
    wallet.sync(&blockchain, SyncOptions::default())?;

    // get deposit address
    let deposit_address = wallet.get_address(AddressIndex::New)?;

    let balance = wallet.get_balance()?;
    println!("Wallet balances in SATs: {}", balance);

    if balance.get_total() < 10000 {
        println!(
            "Send some sats from the u01.net testnet faucet to address '{addr}'.\nFaucet URL: https://bitcoinfaucet.uo1.net/?to={addr}",
            addr = deposit_address.address
        );
        return Ok(());
    }

    let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?
        .require_network(Network::Testnet)?;
    let (mut psbt, _details) = {
        let mut builder = wallet.build_tx();
        builder
            .drain_wallet()
            .drain_to(return_address.script_pubkey())
            .enable_rbf()
            .fee_rate(FeeRate::from_sat_per_vb(5.0));
        builder.finish()?
    };

    // `sign` will call the hardware wallet asking for a signature
    assert!(
        wallet.sign(&mut psbt, SignOptions::default())?,
        "The hardware wallet couldn't finalize the transaction :("
    );

    println!("Let's broadcast your tx...");
    let raw_transaction = psbt.extract_tx();
    let txid = raw_transaction.txid();

    blockchain.broadcast(&raw_transaction)?;
    println!("Transaction broadcasted! TXID: {txid}.\nExplorer URL: https://mempool.space/testnet/tx/{txid}", txid = txid);

    Ok(())
}