diff --git a/CHANGELOG.md b/CHANGELOG.md index 73fd99e..c8cb22e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Update BDK to version 0.18.0 +- Add BumpFeeTxBuilder to bump the fee on an unconfirmed tx created by the Wallet +- Change TxBuilder.build() to TxBuilder.finish() to align with bdk function name ## [v0.5.0] diff --git a/src/bdk.udl b/src/bdk.udl index 5429127..46a1ead 100644 --- a/src/bdk.udl +++ b/src/bdk.udl @@ -155,7 +155,16 @@ interface TxBuilder { TxBuilder enable_rbf(); TxBuilder enable_rbf_with_sequence(u32 nsequence); [Throws=BdkError] - PartiallySignedBitcoinTransaction build([ByRef] Wallet wallet); + PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet); +}; + +interface BumpFeeTxBuilder { + constructor(string txid, float new_fee_rate); + BumpFeeTxBuilder allow_shrinking(string address); + BumpFeeTxBuilder enable_rbf(); + BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence); + [Throws=BdkError] + PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet); }; dictionary ExtendedKeyInfo { diff --git a/src/lib.rs b/src/lib.rs index caa1396..9a5868f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ use bdk::bitcoin::hashes::hex::ToHex; use bdk::bitcoin::secp256k1::Secp256k1; use bdk::bitcoin::util::psbt::PartiallySignedTransaction; -use bdk::bitcoin::{Address, Network, Script}; +use bdk::bitcoin::{Address, Network, Script, Txid}; use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; use bdk::blockchain::{ electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig, ConfigurableBlockchain, @@ -406,7 +406,7 @@ impl TxBuilder { }) } - fn build(&self, wallet: &Wallet) -> Result, Error> { + fn finish(&self, wallet: &Wallet) -> Result, Error> { let wallet = wallet.get_wallet(); let mut tx_builder = wallet.build_tx(); for (address, amount) in &self.recipients { @@ -440,4 +440,78 @@ impl TxBuilder { } } +struct BumpFeeTxBuilder { + txid: String, + fee_rate: f32, + allow_shrinking: Option, + rbf: Option, +} + +impl BumpFeeTxBuilder { + fn new(txid: String, fee_rate: f32) -> Self { + Self { + txid, + fee_rate, + allow_shrinking: None, + rbf: None, + } + } + + fn allow_shrinking(&self, address: String) -> Arc { + Arc::new(Self { + txid: self.txid.clone(), + fee_rate: self.fee_rate, + allow_shrinking: Some(address), + rbf: self.rbf.clone(), + }) + } + + fn enable_rbf(&self) -> Arc { + Arc::new(Self { + txid: self.txid.clone(), + fee_rate: self.fee_rate, + allow_shrinking: self.allow_shrinking.clone(), + rbf: Some(RbfValue::Default), + }) + } + + fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc { + Arc::new(Self { + txid: self.txid.clone(), + fee_rate: self.fee_rate, + allow_shrinking: self.allow_shrinking.clone(), + rbf: Some(RbfValue::Value(nsequence)), + }) + } + + fn finish(&self, wallet: &Wallet) -> Result, Error> { + let wallet = wallet.get_wallet(); + let txid = Txid::from_str(self.txid.as_str())?; + let mut tx_builder = wallet.build_fee_bump(txid)?; + tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate)); + if let Some(allow_shrinking) = &self.allow_shrinking { + let address = + Address::from_str(allow_shrinking).map_err(|e| Error::Generic(e.to_string()))?; + let script = address.script_pubkey(); + tx_builder.allow_shrinking(script)?; + } + if let Some(rbf) = &self.rbf { + match *rbf { + RbfValue::Default => { + tx_builder.enable_rbf(); + } + RbfValue::Value(nsequence) => { + tx_builder.enable_rbf_with_sequence(nsequence); + } + } + } + tx_builder + .finish() + .map(|(psbt, _)| PartiallySignedBitcoinTransaction { + internal: Mutex::new(psbt), + }) + .map(Arc::new) + } +} + uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);