|
| 1 | +#![allow(clippy::print_stdout)] |
| 2 | + |
| 3 | +use std::collections::HashMap; |
| 4 | +use std::str::FromStr; |
| 5 | + |
| 6 | +use bdk_chain::BlockId; |
| 7 | +use bdk_chain::ConfirmationBlockTime; |
| 8 | +use bdk_wallet::psbt::{PsbtParams, SelectionStrategy::*}; |
| 9 | +use bdk_wallet::test_utils::*; |
| 10 | +use bdk_wallet::{KeychainKind::External, Wallet}; |
| 11 | +use bitcoin::{ |
| 12 | + bip32, consensus, |
| 13 | + secp256k1::{self, rand}, |
| 14 | + Address, Amount, TxIn, TxOut, |
| 15 | +}; |
| 16 | +use rand::Rng; |
| 17 | + |
| 18 | +// This example shows how to create a PSBT using BDK Wallet. |
| 19 | + |
| 20 | +const NETWORK: bitcoin::Network = bitcoin::Network::Signet; |
| 21 | +const SEND_TO: &str = "tb1pw3g5qvnkryghme7pyal228ekj6vq48zc5k983lqtlr2a96n4xw0q5ejknw"; |
| 22 | +const AMOUNT: Amount = Amount::from_sat(42_000); |
| 23 | +const FEERATE: f64 = 2.0; // sat/vb |
| 24 | + |
| 25 | +fn main() -> anyhow::Result<()> { |
| 26 | + let (desc, change_desc) = get_test_wpkh_and_change_desc(); |
| 27 | + let secp = secp256k1::Secp256k1::new(); |
| 28 | + |
| 29 | + // Xpriv to be used for signing the PSBT |
| 30 | + let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L")?; |
| 31 | + |
| 32 | + // Create wallet and fund it. |
| 33 | + let mut wallet = Wallet::create(desc, change_desc) |
| 34 | + .network(NETWORK) |
| 35 | + .create_wallet_no_persist()?; |
| 36 | + |
| 37 | + fund_wallet(&mut wallet)?; |
| 38 | + |
| 39 | + let utxos = wallet |
| 40 | + .list_unspent() |
| 41 | + .map(|output| (output.outpoint, output)) |
| 42 | + .collect::<HashMap<_, _>>(); |
| 43 | + |
| 44 | + // Build params. |
| 45 | + let mut params = PsbtParams::default(); |
| 46 | + let addr = Address::from_str(SEND_TO)?.require_network(NETWORK)?; |
| 47 | + let feerate = feerate_unchecked(FEERATE); |
| 48 | + params |
| 49 | + .add_recipients([(addr, AMOUNT)]) |
| 50 | + .feerate(feerate) |
| 51 | + .coin_selection(SingleRandomDraw); |
| 52 | + |
| 53 | + // Create PSBT (which also returns the Finalizer). |
| 54 | + let (mut psbt, finalizer) = wallet.create_psbt(params)?; |
| 55 | + |
| 56 | + dbg!(&psbt); |
| 57 | + |
| 58 | + let tx = &psbt.unsigned_tx; |
| 59 | + for txin in &tx.input { |
| 60 | + let op = txin.previous_output; |
| 61 | + let output = utxos.get(&op).unwrap(); |
| 62 | + println!("TxIn: {}", output.txout.value); |
| 63 | + } |
| 64 | + for txout in &tx.output { |
| 65 | + println!("TxOut: {}", txout.value); |
| 66 | + } |
| 67 | + |
| 68 | + let _ = psbt.sign(&xprv, &secp); |
| 69 | + println!("Signed: {}", !psbt.inputs[0].partial_sigs.is_empty()); |
| 70 | + let finalize_res = finalizer.finalize(&mut psbt); |
| 71 | + println!("Finalized: {}", finalize_res.is_finalized()); |
| 72 | + |
| 73 | + let tx = psbt.extract_tx()?; |
| 74 | + let feerate = wallet.calculate_fee_rate(&tx)?; |
| 75 | + println!("Fee rate: {} sat/vb", bdk_wallet::floating_rate!(feerate)); |
| 76 | + |
| 77 | + println!("{}", consensus::encode::serialize_hex(&tx)); |
| 78 | + |
| 79 | + Ok(()) |
| 80 | +} |
| 81 | + |
| 82 | +fn fund_wallet(wallet: &mut Wallet) -> anyhow::Result<()> { |
| 83 | + let anchor = ConfirmationBlockTime { |
| 84 | + block_id: BlockId { |
| 85 | + height: 260071, |
| 86 | + hash: "000000099f67ae6469d1ad0525d756e24d4b02fbf27d65b3f413d5feb367ec48".parse()?, |
| 87 | + }, |
| 88 | + confirmation_time: 1752184658, |
| 89 | + }; |
| 90 | + insert_checkpoint(wallet, anchor.block_id); |
| 91 | + |
| 92 | + let mut rng = rand::thread_rng(); |
| 93 | + |
| 94 | + // Fund wallet with several random utxos |
| 95 | + for i in 0..21 { |
| 96 | + let addr = wallet.reveal_next_address(External).address; |
| 97 | + let value = 10_000 * (i + 1) + (100 * rng.gen_range(0..10)); |
| 98 | + let tx = bitcoin::Transaction { |
| 99 | + lock_time: bitcoin::absolute::LockTime::ZERO, |
| 100 | + version: bitcoin::transaction::Version::TWO, |
| 101 | + input: vec![TxIn::default()], |
| 102 | + output: vec![TxOut { |
| 103 | + script_pubkey: addr.script_pubkey(), |
| 104 | + value: Amount::from_sat(value), |
| 105 | + }], |
| 106 | + }; |
| 107 | + insert_tx_anchor(wallet, tx, anchor.block_id); |
| 108 | + } |
| 109 | + |
| 110 | + let tip = BlockId { |
| 111 | + height: 260171, |
| 112 | + hash: "0000000b9efb77450e753ae9fd7be9f69219511c27b6e95c28f4126f3e1591c3".parse()?, |
| 113 | + }; |
| 114 | + insert_checkpoint(wallet, tip); |
| 115 | + |
| 116 | + Ok(()) |
| 117 | +} |
0 commit comments