/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
- /// let (psbt, details) = wallet.build_tx()
- /// .add_recipient(to_address.script_pubkey(), 50_000)
- /// .finish()?;
+ /// let (psbt, details) = {
+ /// let mut builder = wallet.build_tx();
+ /// builder
+ /// .add_recipient(to_address.script_pubkey(), 50_000);
+ /// builder.finish()?
+ /// };
///
/// // sign and broadcast ...
/// # Ok::<(), bdk::Error>(())
pub fn build_tx(&self) -> TxBuilder<'_, B, D, DefaultCoinSelectionAlgorithm, CreateTx> {
TxBuilder {
wallet: &self,
- params: Some(TxParams::default()),
- coin_selection: Some(DefaultCoinSelectionAlgorithm::default()),
+ params: TxParams::default(),
+ coin_selection: DefaultCoinSelectionAlgorithm::default(),
phantom: core::marker::PhantomData,
}
}
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
- /// let (psbt, _) = wallet.build_tx()
- /// .add_recipient(to_address.script_pubkey(), 50_000)
- /// .enable_rbf()
- /// .finish()?;
+ /// let (psbt, _) = {
+ /// let mut builder = wallet.build_tx();
+ /// builder
+ /// .add_recipient(to_address.script_pubkey(), 50_000)
+ /// .enable_rbf();
+ /// builder.finish()?
+ /// };
/// let (psbt, _) = wallet.sign(psbt, None)?;
/// let tx = psbt.extract_tx();
/// // broadcast tx but it's taking too long to confirm so we want to bump the fee
- /// let (psbt, details) = wallet.build_fee_bump(tx.txid())?
- /// .fee_rate(FeeRate::from_sat_per_vb(5.0))
- /// .finish()?;
+ /// let (psbt, _) = {
+ /// let mut builder = wallet.build_fee_bump(tx.txid())?;
+ /// builder
+ /// .fee_rate(FeeRate::from_sat_per_vb(5.0));
+ /// builder.finish()?
+ /// };
///
/// let (psbt, _) = wallet.sign(psbt, None)?;
/// let fee_bumped_tx = psbt.extract_tx();
Ok(TxBuilder {
wallet: &self,
- params: Some(params),
- coin_selection: Some(DefaultCoinSelectionAlgorithm::default()),
+ params,
+ coin_selection: DefaultCoinSelectionAlgorithm::default(),
phantom: core::marker::PhantomData,
})
}
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
- /// let (psbt, _) = wallet.build_tx().add_recipient(to_address.script_pubkey(), 50_000).finish()?;
+ /// let (psbt, _) = {
+ /// let mut builder = wallet.build_tx();
+ /// builder.add_recipient(to_address.script_pubkey(), 50_000);
+ /// builder.finish()?
+ /// };
/// let (signed_psbt, finalized) = wallet.sign(psbt, None)?;
/// assert!(finalized, "we should have signed all the inputs");
/// # Ok::<(), bdk::Error>(())
fn test_create_tx_manually_selected_empty_utxos() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .manually_selected_only()
- .finish()
- .unwrap();
+ .manually_selected_only();
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_version_0() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .version(0)
- .finish()
- .unwrap();
+ .version(0);
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_version_1_csv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .version(1)
- .finish()
- .unwrap();
+ .version(1);
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_custom_version() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .version(42)
- .finish()
- .unwrap();
+ .version(42);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.version, 42);
}
fn test_create_tx_default_locktime() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.lock_time, 0);
}
fn test_create_tx_default_locktime_cltv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.lock_time, 100_000);
}
fn test_create_tx_custom_locktime() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .nlocktime(630_000)
- .finish()
- .unwrap();
+ .nlocktime(630_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.lock_time, 630_000);
}
fn test_create_tx_custom_locktime_compatible_with_cltv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .nlocktime(630_000)
- .finish()
- .unwrap();
+ .nlocktime(630_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.lock_time, 630_000);
}
fn test_create_tx_custom_locktime_incompatible_with_cltv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .nlocktime(50000)
- .finish()
- .unwrap();
+ .nlocktime(50000);
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_no_rbf_csv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 6);
}
fn test_create_tx_with_default_rbf_csv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, _) = builder.finish().unwrap();
// When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
// It will be set to the OP_CSV value, in this case 6
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 6);
fn test_create_tx_with_custom_rbf_csv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf_with_sequence(3)
- .finish()
- .unwrap();
+ .enable_rbf_with_sequence(3);
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_no_rbf_cltv() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFE);
}
fn test_create_tx_invalid_rbf_sequence() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf_with_sequence(0xFFFFFFFE)
- .finish()
- .unwrap();
+ .enable_rbf_with_sequence(0xFFFFFFFE);
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_custom_rbf_sequence() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf_with_sequence(0xDEADBEEF)
- .finish()
- .unwrap();
+ .enable_rbf_with_sequence(0xDEADBEEF);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xDEADBEEF);
}
fn test_create_tx_default_sequence() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
}
fn test_create_tx_change_policy_no_internal() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .do_not_spend_change()
- .finish()
- .unwrap();
+ .do_not_spend_change();
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_single_recipient_drain_wallet() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
assert_eq!(
fn test_create_tx_default_fee_rate() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, details) = builder.finish().unwrap();
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::default(), @add_signature);
}
fn test_create_tx_custom_fee_rate() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .fee_rate(FeeRate::from_sat_per_vb(5.0))
- .finish()
- .unwrap();
+ .fee_rate(FeeRate::from_sat_per_vb(5.0));
+ let (psbt, details) = builder.finish().unwrap();
assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
}
fn test_create_tx_absolute_fee() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .fee_absolute(100)
- .finish()
- .unwrap();
+ .fee_absolute(100);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.fees, 100);
assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
fn test_create_tx_absolute_zero_fee() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .fee_absolute(0)
- .finish()
- .unwrap();
+ .fee_absolute(0);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.fees, 0);
assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
fn test_create_tx_absolute_high_fee() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (_psbt, _details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .fee_absolute(60_000)
- .finish()
- .unwrap();
+ .fee_absolute(60_000);
+ let (_psbt, _details) = builder.finish().unwrap();
}
#[test]
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .ordering(TxOrdering::Untouched)
- .finish()
- .unwrap();
+ .ordering(TxOrdering::Untouched);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.output.len(), 2);
assert_eq!(psbt.global.unsigned_tx.output[0].value, 25_000);
fn test_create_tx_skip_change_dust() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 49_800)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 49_800);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
assert_eq!(psbt.global.unsigned_tx.output[0].value, 49_800);
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
// very high fee rate, so that the only output would be below dust
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .fee_rate(FeeRate::from_sat_per_vb(453.0))
- .finish()
- .unwrap();
+ .fee_rate(FeeRate::from_sat_per_vb(453.0));
+ builder.finish().unwrap();
}
#[test]
fn test_create_tx_ordering_respected() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
.add_recipient(addr.script_pubkey(), 10_000)
- .ordering(super::tx_builder::TxOrdering::BIP69Lexicographic)
- .finish()
- .unwrap();
+ .ordering(super::tx_builder::TxOrdering::BIP69Lexicographic);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.output.len(), 3);
assert_eq!(
fn test_create_tx_default_sighash() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 30_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 30_000);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.inputs[0].sighash_type, None);
}
fn test_create_tx_custom_sighash() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
- .sighash(bitcoin::SigHashType::Single)
- .finish()
- .unwrap();
+ .sighash(bitcoin::SigHashType::Single);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(
psbt.inputs[0].sighash_type,
let (wallet, _, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.inputs[0].hd_keypaths.len(), 1);
assert_eq!(
wallet.get_new_address().unwrap();
let addr = testutils!(@external descriptors, 5);
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.outputs[0].hd_keypaths.len(), 1);
assert_eq!(
let (wallet, _, _) =
get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(
psbt.inputs[0].redeem_script,
let (wallet, _, _) =
get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.inputs[0].redeem_script, None);
assert_eq!(
let (wallet, _, _) =
get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
let script = Script::from(
Vec::<u8>::from_hex(
let (wallet, _, _) =
get_funded_wallet("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert!(psbt.inputs[0].non_witness_utxo.is_some());
assert!(psbt.inputs[0].witness_utxo.is_none());
let (wallet, _, _) =
get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert!(psbt.inputs[0].non_witness_utxo.is_none());
assert!(psbt.inputs[0].witness_utxo.is_some());
let (wallet, _, _) =
get_funded_wallet("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
assert!(psbt.inputs[0].non_witness_utxo.is_none());
assert!(psbt.inputs[0].witness_utxo.is_some());
let (wallet, _, _) =
get_funded_wallet("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .force_non_witness_utxo()
- .finish()
- .unwrap();
+ .force_non_witness_utxo();
+ let (psbt, _) = builder.finish().unwrap();
assert!(psbt.inputs[0].non_witness_utxo.is_some());
assert!(psbt.inputs[0].witness_utxo.is_some());
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
.add_utxo(OutPoint {
txid: small_output_txid,
vout: 0,
})
- .unwrap()
- .finish()
.unwrap();
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(
psbt.global.unsigned_tx.input.len(),
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
.add_utxo(OutPoint {
txid: small_output_txid,
vout: 0,
})
.unwrap()
- .manually_selected_only()
- .finish()
- .unwrap();
+ .manually_selected_only();
+ builder.finish().unwrap();
}
#[test]
let (wallet, _, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 30_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 30_000);
+ builder.finish().unwrap();
}
#[test]
let path = vec![(root_id, vec![0])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
- .policy_path(path, KeychainKind::External)
- .finish()
- .unwrap();
+ .policy_path(path, KeychainKind::External);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
}
let path = vec![(root_id, vec![1])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 30_000)
- .policy_path(path, KeychainKind::External)
- .finish()
- .unwrap();
+ .policy_path(path, KeychainKind::External);
+ let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 144);
}
let (wallet, _, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .add_global_xpubs()
- .finish()
- .unwrap();
+ .add_global_xpubs();
+ let (psbt, _) = builder.finish().unwrap();
let type_value = 0x01;
let key = base58::from_check("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
fn test_create_tx_global_xpubs_origin_missing() {
let (wallet, _, _) = get_funded_wallet("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
let addr = wallet.get_new_address().unwrap();
- wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .add_global_xpubs()
- .finish()
- .unwrap();
+ .add_global_xpubs();
+ builder.finish().unwrap();
}
#[test]
let (wallet, _, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .add_global_xpubs()
- .finish()
- .unwrap();
+ .add_global_xpubs();
+ let (psbt, _) = builder.finish().unwrap();
let type_value = 0x01;
let key = base58::from_check("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
fn test_bump_fee_irreplaceable_tx() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, mut details) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, mut details) = builder.finish().unwrap();
let tx = psbt.extract_tx();
let txid = tx.txid();
fn test_bump_fee_confirmed_tx() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, mut details) = wallet
- .build_tx()
- .add_recipient(addr.script_pubkey(), 25_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_tx();
+ builder.add_recipient(addr.script_pubkey(), 25_000);
+ let (psbt, mut details) = builder.finish().unwrap();
let tx = psbt.extract_tx();
let txid = tx.txid();
fn test_bump_fee_low_fee_rate() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, mut details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut details) = builder.finish().unwrap();
let tx = psbt.extract_tx();
let txid = tx.txid();
details.transaction = Some(tx);
wallet.database.borrow_mut().set_tx(&details).unwrap();
- wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(1.0))
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_rate(FeeRate::from_sat_per_vb(1.0));
+ builder.finish().unwrap();
}
#[test]
fn test_bump_fee_low_abs() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, mut details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut details) = builder.finish().unwrap();
let tx = psbt.extract_tx();
let txid = tx.txid();
details.transaction = Some(tx);
wallet.database.borrow_mut().set_tx(&details).unwrap();
- wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_absolute(10)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_absolute(10);
+ builder.finish().unwrap();
}
#[test]
fn test_bump_fee_zero_abs() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = wallet.get_new_address().unwrap();
- let (psbt, mut details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut details) = builder.finish().unwrap();
let tx = psbt.extract_tx();
let txid = tx.txid();
details.transaction = Some(tx);
wallet.database.borrow_mut().set_tx(&details).unwrap();
- wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_absolute(0)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_absolute(0);
+ builder.finish().unwrap();
}
#[test]
fn test_bump_fee_reduce_change() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(2.5))
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_rate(FeeRate::from_sat_per_vb(2.5));
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent);
assert_eq!(
fn test_bump_fee_absolute_reduce_change() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 25_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_absolute(200)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_absolute(200);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent);
assert_eq!(
fn test_bump_fee_reduce_single_recipient() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
for txin in &mut tx.input {
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder
.fee_rate(FeeRate::from_sat_per_vb(2.5))
- .maintain_single_recipient()
- .finish()
- .unwrap();
+ .maintain_single_recipient();
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent);
assert!(details.fees > original_details.fees);
fn test_bump_fee_absolute_reduce_single_recipient() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.drain_wallet()
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
for txin in &mut tx.input {
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .maintain_single_recipient()
- .fee_absolute(300)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.maintain_single_recipient().fee_absolute(300);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent);
assert!(details.fees > original_details.fees);
vout: 0,
};
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.add_utxo(outpoint)
.unwrap()
.manually_selected_only()
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
for txin in &mut tx.input {
// for the new feerate, it should be enough to reduce the output, but since we specify
// `drain_wallet` we expect to spend everything
- let (_, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder
.drain_wallet()
.maintain_single_recipient()
- .fee_rate(FeeRate::from_sat_per_vb(5.0))
- .finish()
- .unwrap();
+ .fee_rate(FeeRate::from_sat_per_vb(5.0));
+ let (_, details) = builder.finish().unwrap();
assert_eq!(details.sent, 75_000);
}
vout: 0,
};
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.add_utxo(outpoint)
.unwrap()
.manually_selected_only()
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
for txin in &mut tx.input {
.unwrap();
assert_eq!(original_details.sent, 25_000);
- wallet
- .build_fee_bump(txid)
- .unwrap()
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder
.add_utxo(outpoint)
.unwrap()
.manually_selected_only()
- .fee_rate(FeeRate::from_sat_per_vb(255.0))
- .finish()
- .unwrap();
+ .fee_rate(FeeRate::from_sat_per_vb(255.0));
+ builder.finish().unwrap();
}
#[test]
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(50.0))
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent + 25_000);
assert_eq!(details.fees + details.received, 30_000);
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_absolute(6_000)
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_absolute(6_000);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent + 25_000);
assert_eq!(details.fees + details.received, 30_000);
// initially make a tx without change by using `set_single_recipient`
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
.add_utxo(OutPoint {
txid: incoming_txid,
})
.unwrap()
.manually_selected_only()
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// now bump the fees without using `maintain_single_recipient`. the wallet should add an
// extra input and a change output, and leave the original output untouched
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(50.0))
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
+ let (psbt, details) = builder.finish().unwrap();
let original_send_all_amount = original_details.sent - original_details.fees;
assert_eq!(details.sent, original_details.sent + 50_000);
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
assert_eq!(tx.input.len(), 1);
assert_eq!(tx.output.len(), 2);
.set_tx(&original_details)
.unwrap();
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(140.0))
- .finish()
- .unwrap();
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder.fee_rate(FeeRate::from_sat_per_vb(140.0));
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(original_details.received, 5_000 - original_details.fees);
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
// the new fee_rate is low enough that just reducing the change would be fine, but we force
// the addition of an extra input with `add_utxo()`
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder
.add_utxo(OutPoint {
txid: incoming_txid,
vout: 0,
})
.unwrap()
- .fee_rate(FeeRate::from_sat_per_vb(5.0))
- .finish()
- .unwrap();
+ .fee_rate(FeeRate::from_sat_per_vb(5.0));
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent + 25_000);
assert_eq!(details.fees + details.received, 30_000);
);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, mut original_details) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .enable_rbf()
- .finish()
- .unwrap();
+ .enable_rbf();
+ let (psbt, mut original_details) = builder.finish().unwrap();
let mut tx = psbt.extract_tx();
let txid = tx.txid();
// skip saving the new utxos, we know they can't be used anyways
// the new fee_rate is low enough that just reducing the change would be fine, but we force
// the addition of an extra input with `add_utxo()`
- let (psbt, details) = wallet
- .build_fee_bump(txid)
- .unwrap()
+ let mut builder = wallet.build_fee_bump(txid).unwrap();
+ builder
.add_utxo(OutPoint {
txid: incoming_txid,
vout: 0,
})
.unwrap()
- .fee_absolute(250)
- .finish()
- .unwrap();
+ .fee_absolute(250);
+ let (psbt, details) = builder.finish().unwrap();
assert_eq!(details.sent, original_details.sent + 25_000);
assert_eq!(details.fees + details.received, 30_000);
fn test_sign_single_xprv() {
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
let (signed_psbt, finalized) = wallet.sign(psbt, None).unwrap();
assert_eq!(finalized, true);
fn test_sign_single_xprv_bip44_path() {
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
let (signed_psbt, finalized) = wallet.sign(psbt, None).unwrap();
assert_eq!(finalized, true);
fn test_sign_single_xprv_sh_wpkh() {
let (wallet, _, _) = get_funded_wallet("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
let (signed_psbt, finalized) = wallet.sign(psbt, None).unwrap();
assert_eq!(finalized, true);
let (wallet, _, _) =
get_funded_wallet("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)");
let addr = wallet.get_new_address().unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (psbt, _) = builder.finish().unwrap();
let (signed_psbt, finalized) = wallet.sign(psbt, None).unwrap();
assert_eq!(finalized, true);
fn test_sign_single_xprv_no_hd_keypaths() {
let (wallet, _, _) = get_funded_wallet("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
let addr = wallet.get_new_address().unwrap();
- let (mut psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.set_single_recipient(addr.script_pubkey())
- .drain_wallet()
- .finish()
- .unwrap();
+ .drain_wallet();
+ let (mut psbt, _) = builder.finish().unwrap();
psbt.inputs[0].hd_keypaths.clear();
assert_eq!(psbt.inputs[0].hd_keypaths.len(), 0);
fn test_include_output_redeem_witness_script() {
let (wallet, _, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .include_output_redeem_witness_script()
- .finish()
- .unwrap();
+ .include_output_redeem_witness_script();
+ let (psbt, _) = builder.finish().unwrap();
// p2sh-p2wsh transaction should contain both witness and redeem scripts
assert!(psbt
fn test_signing_only_one_of_multiple_inputs() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
- let (mut psbt, _) = wallet
- .build_tx()
+ let mut builder = wallet.build_tx();
+ builder
.add_recipient(addr.script_pubkey(), 45_000)
- .include_output_redeem_witness_script()
- .finish()
- .unwrap();
+ .include_output_redeem_witness_script();
+ let (mut psbt, _) = builder.finish().unwrap();
// add another input to the psbt that is at least passable.
let mut dud_input = bitcoin::util::psbt::Input::default();
//! // create a TxBuilder from a wallet
//! let mut tx_builder = wallet.build_tx();
//!
-//! let (psbt, tx_details) = tx_builder
+//! tx_builder
//! // Create a transaction with one output to `to_address` of 50_000 satoshi
//! .add_recipient(to_address.script_pubkey(), 50_000)
//! // With a custom fee rate of 5.0 satoshi/vbyte
//! // Only spend non-change outputs
//! .do_not_spend_change()
//! // Turn on RBF signaling
-//! .enable_rbf()
-//! .finish()?;
-//!
+//! .enable_rbf();
+//! let (psbt, tx_details) = tx_builder.finish()?;
//! # Ok::<(), bdk::Error>(())
//! ```
/// A transaction builder
///
-/// A `TxBuilder` is initially created by calling [`build_tx`] or [`build_fee_bump`] on a wallet.
-/// From there you set sepcific options on the builder until finally calling [`finish`] to get the transaction.
+/// A `TxBuilder` is created by calling [`build_tx`] or [`build_fee_bump`] on a wallet. After
+/// assigning it, you set options on it until finally calling [`finish`] to consume the builder and
+/// generate the transaction.
///
-/// Each method on TxBuilder takes and returns `&mut self` so you can use either use a chaining call
-/// or assign the builder and call normally as in the following example:
+/// Each option setting method on `TxBuilder` takes and returns `&mut self` so you can chain calls
+/// as in the following example:
///
/// ```
/// # use bdk::*;
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// # let addr2 = addr1.clone();
/// // chaining
-/// let (psbt1, details) = wallet.build_tx()
+/// let (psbt1, details) = {
+/// let mut builder = wallet.build_tx();
+/// builder
/// .ordering(TxOrdering::Untouched)
/// .add_recipient(addr1.script_pubkey(), 50_000)
-/// .add_recipient(addr2.script_pubkey(), 50_000)
-/// .finish()?;
+/// .add_recipient(addr2.script_pubkey(), 50_000);
+/// builder.finish()?
+/// };
///
/// // non-chaining
-/// let mut builder = wallet.build_tx();
-/// for addr in &[addr1, addr2] {
-/// builder.add_recipient(addr.script_pubkey(), 50_000);
-/// }
-/// let (psbt2, details) = builder.ordering(TxOrdering::Untouched).finish()?;
-/// //
+/// let (psbt2, details) = {
+/// let mut builder = wallet.build_tx();
+/// builder.ordering(TxOrdering::Untouched);
+/// for addr in &[addr1, addr2] {
+/// builder.add_recipient(addr.script_pubkey(), 50_000);
+/// }
+/// builder.finish()?
+/// };
+///
/// assert_eq!(psbt1.global.unsigned_tx.output[..2], psbt2.global.unsigned_tx.output[..2]);
/// # Ok::<(), bdk::Error>(())
/// ```
///
-/// At the moment [`coin_selection`] is an exception - it consumes `self`.
-/// This means it is usually best to call [`coin_selection`] first before calling other methods.
-///
-/// Note that calling methods on the builder after calling [`finish`] will result in a panic.
+/// At the moment [`coin_selection`] is an exception to the rule as it consumes `self`.
+/// This means it is usually best to call [`coin_selection`] on the return value of `build_tx` before assigning it.
///
/// For further examples see [this module](super::tx_builder)'s documentation;
///
// params and coin_selection are Options not becasue they are optionally set (they are always
// there) but because `.finish()` uses `Option::take` to get an owned value from a &mut self.
// They are only `None` after `.finish()` is called.
- pub(crate) params: Option<TxParams>,
- pub(crate) coin_selection: Option<Cs>,
+ pub(crate) params: TxParams,
+ pub(crate) coin_selection: Cs,
pub(crate) phantom: PhantomData<Ctx>,
}
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
TxBuilder<'a, B, D, Cs, Ctx>
{
- fn params(&mut self) -> &mut TxParams {
- self.params
- .as_mut()
- .expect("method called on transaction builder after it was finalized")
- }
/// Set a custom fee rate
pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
- self.params().fee_policy = Some(FeePolicy::FeeRate(fee_rate));
+ self.params.fee_policy = Some(FeePolicy::FeeRate(fee_rate));
self
}
/// Set an absolute fee
pub fn fee_absolute(&mut self, fee_amount: u64) -> &mut Self {
- self.params().fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
+ self.params.fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
self
}
keychain: KeychainKind,
) -> &mut Self {
let to_update = match keychain {
- KeychainKind::Internal => &mut self.params().internal_policy_path,
- KeychainKind::External => &mut self.params().external_policy_path,
+ KeychainKind::Internal => &mut self.params.internal_policy_path,
+ KeychainKind::External => &mut self.params.external_policy_path,
};
*to_update = Some(policy_path);
/// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
/// the "utxos" and the "unspendable" list, it will be spent.
pub fn add_utxo(&mut self, outpoint: OutPoint) -> Result<&mut Self, Error> {
- if self.params().utxos.get(&outpoint).is_none() {
+ if self.params.utxos.get(&outpoint).is_none() {
let deriv_ctx = crate::wallet::descriptor_to_pk_ctx(self.wallet.secp_ctx());
let utxo = self.wallet.get_utxo(outpoint)?.ok_or(Error::UnknownUTXO)?;
let descriptor = self.wallet.get_descriptor_for_keychain(utxo.keychain);
let satisfaction_weight = descriptor.max_satisfaction_weight(deriv_ctx).unwrap();
- self.params()
+ self.params
.utxos
.insert(outpoint, (utxo, satisfaction_weight));
}
///
/// [`add_utxo`]: Self::add_utxo
pub fn manually_selected_only(&mut self) -> &mut Self {
- self.params().manually_selected_only = true;
+ self.params.manually_selected_only = true;
self
}
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
/// have priority over these. See the docs of the two linked methods for more details.
pub fn unspendable(&mut self, unspendable: Vec<OutPoint>) -> &mut Self {
- self.params().unspendable = unspendable.into_iter().collect();
+ self.params.unspendable = unspendable.into_iter().collect();
self
}
/// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
/// have priority over this. See the docs of the two linked methods for more details.
pub fn add_unspendable(&mut self, unspendable: OutPoint) -> &mut Self {
- self.params().unspendable.insert(unspendable);
+ self.params.unspendable.insert(unspendable);
self
}
///
/// **Use this option very carefully**
pub fn sighash(&mut self, sighash: SigHashType) -> &mut Self {
- self.params().sighash = Some(sighash);
+ self.params.sighash = Some(sighash);
self
}
/// Choose the ordering for inputs and outputs of the transaction
pub fn ordering(&mut self, ordering: TxOrdering) -> &mut Self {
- self.params().ordering = ordering;
+ self.params.ordering = ordering;
self
}
///
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
pub fn nlocktime(&mut self, locktime: u32) -> &mut Self {
- self.params().locktime = Some(locktime);
+ self.params.locktime = Some(locktime);
self
}
/// The `version` should always be greater than `0` and greater than `1` if the wallet's
/// descriptors contain an "older" (OP_CSV) operator.
pub fn version(&mut self, version: i32) -> &mut Self {
- self.params().version = Some(Version(version));
+ self.params.version = Some(Version(version));
self
}
/// This effectively adds all the change outputs to the "unspendable" list. See
/// [`TxBuilder::unspendable`].
pub fn do_not_spend_change(&mut self) -> &mut Self {
- self.params().change_policy = ChangeSpendPolicy::ChangeForbidden;
+ self.params.change_policy = ChangeSpendPolicy::ChangeForbidden;
self
}
/// This effectively adds all the non-change outputs to the "unspendable" list. See
/// [`TxBuilder::unspendable`].
pub fn only_spend_change(&mut self) -> &mut Self {
- self.params().change_policy = ChangeSpendPolicy::OnlyChange;
+ self.params.change_policy = ChangeSpendPolicy::OnlyChange;
self
}
/// Set a specific [`ChangeSpendPolicy`]. See [`TxBuilder::do_not_spend_change`] and
/// [`TxBuilder::only_spend_change`] for some shortcuts.
pub fn change_policy(&mut self, change_policy: ChangeSpendPolicy) -> &mut Self {
- self.params().change_policy = change_policy;
+ self.params.change_policy = change_policy;
self
}
///
/// This is useful for signers which always require it, like Trezor hardware wallets.
pub fn force_non_witness_utxo(&mut self) -> &mut Self {
- self.params().force_non_witness_utxo = true;
+ self.params.force_non_witness_utxo = true;
self
}
///
/// This is useful for signers which always require it, like ColdCard hardware wallets.
pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
- self.params().include_output_redeem_witness_script = true;
+ self.params.include_output_redeem_witness_script = true;
self
}
/// This is useful for offline signers that take part to a multisig. Some hardware wallets like
/// BitBox and ColdCard are known to require this.
pub fn add_global_xpubs(&mut self) -> &mut Self {
- self.params().add_global_xpubs = true;
+ self.params.add_global_xpubs = true;
self
}
/// Spend all the available inputs. This respects filters like [`TxBuilder::unspendable`] and the change policy.
pub fn drain_wallet(&mut self) -> &mut Self {
- self.params().drain_wallet = true;
+ self.params.drain_wallet = true;
self
}
self,
coin_selection: P,
) -> TxBuilder<'a, B, D, P, Ctx> {
- assert!(
- self.coin_selection.is_some(),
- "can't set coin_selection after finish() has been called"
- );
TxBuilder {
wallet: self.wallet,
params: self.params,
- coin_selection: Some(coin_selection),
+ coin_selection,
phantom: PhantomData,
}
}
/// Returns the [`BIP174`] "PSBT" and summary details about the transaction.
///
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
- pub fn finish(&mut self) -> Result<(PSBT, TransactionDetails), Error> {
- self.wallet.create_tx(
- self.coin_selection.take().unwrap(),
- self.params.take().unwrap(),
- )
+ pub fn finish(self) -> Result<(PSBT, TransactionDetails), Error> {
+ self.wallet.create_tx(self.coin_selection, self.params)
}
}
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, B, D, Cs, CreateTx> {
/// Replace the recipients already added with a new list
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self {
- self.params().recipients = recipients;
+ self.params.recipients = recipients;
self
}
/// Add a recipient to the internal list
pub fn add_recipient(&mut self, script_pubkey: Script, amount: u64) -> &mut Self {
- self.params().recipients.push((script_pubkey, amount));
+ self.params.recipients.push((script_pubkey, amount));
self
}
/// add [`maintain_single_recipient`](Self::maintain_single_recipient) to correctly update the
/// single output instead of adding one more for the change.
pub fn set_single_recipient(&mut self, recipient: Script) -> &mut Self {
- self.params().single_recipient = Some(recipient);
- self.params().recipients.clear();
+ self.params.single_recipient = Some(recipient);
+ self.params.recipients.clear();
self
}
///
/// This will use the default nSequence value of `0xFFFFFFFD`.
pub fn enable_rbf(&mut self) -> &mut Self {
- self.params().rbf = Some(RBFValue::Default);
+ self.params.rbf = Some(RBFValue::Default);
self
}
/// If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not
/// be a valid nSequence to signal RBF.
pub fn enable_rbf_with_sequence(&mut self, nsequence: u32) -> &mut Self {
- self.params().rbf = Some(RBFValue::Value(nsequence));
+ self.params.rbf = Some(RBFValue::Value(nsequence));
self
}
}
///
/// [`add_utxo`]: Self::add_utxo
pub fn maintain_single_recipient(&mut self) -> &mut Self {
- let mut recipients = self.params().recipients.drain(..).collect::<Vec<_>>();
+ let mut recipients = self.params.recipients.drain(..).collect::<Vec<_>>();
assert_eq!(recipients.len(), 1, "maintain_single_recipient must not be called while bumping a transactions with more than one output");
- self.params().single_recipient = Some(recipients.pop().unwrap().0);
+ self.params.single_recipient = Some(recipients.pop().unwrap().0);
// Since we are fee bumping and maintaining a single recipient we never want to add any more non-manual inputs.
self
}