This page provides an overview of how BDK can be leveraged to create and sync a wallet using an Esplora client. You can find working code examples of this workflow in three programming languages: Rust, Swift, and Kotlin.
Tip
This page is up-to-date with version 1.0.0-beta.5 of bdk_wallet.
repositories{mavenCentral()}dependencies{// for jvmimplementation'org.bitcoindevkit:bdk-jvm:<version>'// OR for androidimplementation'org.bitcoindevkit:bdk-android:<version>'}
Create a wallet, sync it and display the balance
We'll give a breakdown of the key pieces of this code in the next section.
usebdk_wallet::AddressInfo;usebdk_wallet::KeychainKind;usebdk_wallet::bitcoin::Network;usebdk_wallet::Wallet;usebdk_esplora::EsploraExt;usebdk_esplora::esplora_client::Builder;usebdk_esplora::esplora_client;usebdk_wallet::chain::spk_client::{FullScanRequestBuilder,FullScanResult};constSTOP_GAP: usize=50;constPARALLEL_REQUESTS: usize=1;fnmain()-> (){letdescriptor: &str="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/0/*)#z3x5097m";letchange_descriptor: &str="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/1/*)#n9r4jswr";// Create the walletletmutwallet: Wallet=Wallet::create(descriptor,change_descriptor).network(Network::Signet).create_wallet_no_persist().unwrap();// Sync the walletletclient: esplora_client::BlockingClient=Builder::new("https://mutinynet.com/api").build_blocking();println!("Syncing wallet...");letfull_scan_request: FullScanRequestBuilder<KeychainKind>=wallet.start_full_scan();letupdate: FullScanResult<KeychainKind>=client.full_scan(full_scan_request,STOP_GAP,PARALLEL_REQUESTS).unwrap();// Apply the update from the full scan to the walletwallet.apply_update(update).unwrap();letbalance=wallet.balance();println!("Wallet balance: {} sat",balance.total().to_sat());// Reveal a new address from your external keychainletaddress: AddressInfo=wallet.reveal_next_address(KeychainKind::External);println!("Generated address {} at index {}",address.address,address.index);}
letdescriptor="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/0/*)#z3x5097m"letchangeDescriptor="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/1/*)#n9r4jswr"letwallet=tryWallet(descriptor:descriptor,changeDescriptor:changeDescriptor,network:Network.signet)letaddressInfo=wallet.revealNextAddress(keychain:.external)print("Generated address \(addressInfo.address) at index \(addressInfo.index)")letesploraClient=EsploraClient(url:"https://mutinynet.com/api")letsyncRequest=wallet.startSyncWithRevealedSpks()letupdate=tryesploraClient.sync(syncRequest:syncRequest,parallelRequests:UInt64(5))trywallet.applyUpdate(update:update)letbalance=wallet.balance()print("Wallet balance: \(balance.total) sat")
valdescriptor="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/0/*)#z3x5097m"valchangeDescriptor="tr([12071a7c/86'/1'/0']tpubDCaLkqfh67Qr7ZuRrUNrCYQ54sMjHfsJ4yQSGb3aBr1yqt3yXpamRBUwnGSnyNnxQYu7rqeBiPfw3mjBcFNX4ky2vhjj9bDrGstkfUbLB9T/1/*)#n9r4jswr"valwallet=Wallet(descriptor,changeDescriptor,Network.SIGNET)valaddressInfo=wallet.revealNextAddress(KeychainKind.EXTERNAL)println("Generated address ${addressInfo.address} at index ${addressInfo.index}")valesploraClient:EsploraClient=EsploraClient("https://mutinynet.com/api")valsyncRequest=wallet.startSyncWithRevealedSpks()valupdate=tryesploraClient.sync(syncRequest,5uL)wallet.applyUpdate(update)valbalance=wallet.balance()println("Wallet balance: ${balance.total()} sat")
Build and run:
The wallet will take a few seconds to sync, then you should see the wallet balance printed in the terminal.
First we need some descriptors to instantiate our wallet. In this example we use public key descriptors to simply display the balance of a wallet. To sign transactions you will need to use a wallet that is instantiated with private key descriptors. Refer to the Creating Descriptors page for information on how to generate your own private descriptors.
These are taproot tr() descriptors using public keys on testnet (or signet) tpub as described in BIP86. The descriptor is an HD wallet with a path for generating addresses to give out externally for payment. We also have a second change_descriptor that we can use to generate addresses to pay ourseves change when sending payments (remeber that UTXOs must be spent if full, so you often want to make change).
Blockchain Client and Network
This example is using an Esplora client on the Mutinynet Signet network.
Other options for blockchain clients include running an Electrum light wallet or using RPC on a bitcoind fullnode. We are using Esplora in this example.
This example also used the Signet network. You may alternatively want to run this example wallet using a locally hosted Regtest network. The details of how to set that up are beyond the scope of this example.
Scan
Once we have our wallet setup and connected to the network, we scan the network to detect UTXOs relevant to our wallet.
letfull_scan_request: FullScanRequestBuilder<KeychainKind>=wallet.start_full_scan();letupdate: FullScanResult<KeychainKind>=client.full_scan(full_scan_request,STOP_GAP,PARALLEL_REQUESTS).unwrap();// Apply the update from the full scan to the walletwallet.apply_update(update).unwrap();letbalance=wallet.balance();println!("Wallet balance: {} sat",balance.total().to_sat());
This scanning process is detailed on the Full Scan vs Sync page. The scanning process checks child pubkeys for the descriptors specified in the wallet to detect UTXOs that are relevant to the wallet. That data is then applied to the wallet.
Display Wallet Balance
Finally we can print the wallet.balance() to see how many sats we have available based on the information gathered in the scanning process.