diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt index b9a7d54a..093dde52 100644 --- a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt @@ -59,8 +59,14 @@ class LiveWalletTest { assertTrue(walletDidSign) val tx: Transaction = psbt.extractTx() - println("Txid is: ${tx.txid()}") + + val txFee: ULong = wallet.calculateFee(tx) + println("Tx fee is: ${txFee}") + + val feeRate: FeeRate = wallet.calculateFeeRate(tx) + println("Tx fee rate is: ${feeRate.asSatPerVb()} sat/vB") + esploraClient.broadcast(tx) } } diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index b90f059d..b7c68c47 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -82,6 +82,17 @@ enum BdkError { "Psbt", }; +[Error] +interface CalculateFeeError { + MissingTxOut(sequence out_points); + NegativeFee(i64 fee); +}; + +interface FeeRate { + f32 as_sat_per_vb(); + f32 sat_per_kwu(); +}; + enum ChangeSpendPolicy { "ChangeAllowed", "OnlyChange", @@ -111,6 +122,12 @@ interface Wallet { SentAndReceivedValues sent_and_received([ByRef] Transaction tx); sequence transactions(); + + [Throws=CalculateFeeError] + u64 calculate_fee([ByRef] Transaction tx); + + [Throws=CalculateFeeError] + FeeRate calculate_fee_rate([ByRef] Transaction tx); }; interface Update {}; @@ -348,4 +365,4 @@ interface PartiallySignedTransaction { dictionary OutPoint { string txid; u32 vout; -}; +}; \ No newline at end of file diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index effa01a9..9061c3f0 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -300,6 +300,15 @@ impl From<&OutPoint> for BdkOutPoint { } } +impl From<&BdkOutPoint> for OutPoint { + fn from(outpoint: &BdkOutPoint) -> Self { + OutPoint { + txid: outpoint.txid.to_string(), + vout: outpoint.vout, + } + } +} + #[derive(Debug, Clone)] pub struct TxOut { pub value: u64, diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs new file mode 100644 index 00000000..47a36abf --- /dev/null +++ b/bdk-ffi/src/error.rs @@ -0,0 +1,82 @@ +use crate::bitcoin::OutPoint; + +use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError; + +use std::fmt; + +#[derive(Debug)] +pub enum CalculateFeeError { + MissingTxOut { out_points: Vec }, + NegativeFee { fee: i64 }, +} + +impl fmt::Display for CalculateFeeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + CalculateFeeError::MissingTxOut { out_points } => { + write!(f, "Missing transaction output: {:?}", out_points) + } + CalculateFeeError::NegativeFee { fee } => write!(f, "Negative fee value: {}", fee), + } + } +} + +impl From for CalculateFeeError { + fn from(error: BdkCalculateFeeError) -> Self { + match error { + BdkCalculateFeeError::MissingTxOut(out_points) => CalculateFeeError::MissingTxOut { + out_points: out_points.iter().map(|op| op.into()).collect(), + }, + BdkCalculateFeeError::NegativeFee(fee) => CalculateFeeError::NegativeFee { fee }, + } + } +} + +impl std::error::Error for CalculateFeeError {} + +#[cfg(test)] +mod test { + use crate::CalculateFeeError; + use crate::OutPoint; + + #[test] + fn test_error_missing_tx_out() { + let out_points: Vec = vec![ + OutPoint { + txid: "0000000000000000000000000000000000000000000000000000000000000001" + .to_string(), + vout: 0, + }, + OutPoint { + txid: "0000000000000000000000000000000000000000000000000000000000000002" + .to_string(), + vout: 1, + }, + ]; + + let error = CalculateFeeError::MissingTxOut { out_points }; + + let expected_message: String = format!( + "Missing transaction output: [{:?}, {:?}]", + OutPoint { + txid: "0000000000000000000000000000000000000000000000000000000000000001" + .to_string(), + vout: 0 + }, + OutPoint { + txid: "0000000000000000000000000000000000000000000000000000000000000002" + .to_string(), + vout: 1 + } + ); + + assert_eq!(error.to_string(), expected_message); + } + + #[test] + fn test_error_negative_fee() { + let error = CalculateFeeError::NegativeFee { fee: -100 }; + + assert_eq!(error.to_string(), "Negative fee value: -100"); + } +} diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index b4ce7fda..ec86e0f4 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -1,5 +1,6 @@ mod bitcoin; mod descriptor; +mod error; mod esplora; mod keys; mod types; @@ -13,6 +14,7 @@ use crate::bitcoin::Script; use crate::bitcoin::Transaction; use crate::bitcoin::TxOut; use crate::descriptor::Descriptor; +use crate::error::CalculateFeeError; use crate::esplora::EsploraClient; use crate::keys::DerivationPath; use crate::keys::DescriptorPublicKey; @@ -21,6 +23,7 @@ use crate::keys::Mnemonic; use crate::types::AddressIndex; use crate::types::AddressInfo; use crate::types::Balance; +use crate::types::FeeRate; use crate::types::LocalUtxo; use crate::types::ScriptAmount; use crate::wallet::BumpFeeTxBuilder; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 1f03ea0f..bfef1015 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -7,8 +7,22 @@ use bdk::KeychainKind; use bdk::LocalUtxo as BdkLocalUtxo; +use bdk::FeeRate as BdkFeeRate; + use std::sync::Arc; +pub struct FeeRate(pub BdkFeeRate); + +impl FeeRate { + pub fn as_sat_per_vb(&self) -> f32 { + self.0.as_sat_per_vb() + } + + pub fn sat_per_kwu(&self) -> f32 { + self.0.sat_per_kwu() + } +} + pub struct ScriptAmount { pub script: Arc