Skip to content

Commit 2459eb4

Browse files
committed
feat: add calculate_fee and calculate_fee_rate on wallet
1 parent cdec63e commit 2459eb4

File tree

10 files changed

+119
-7
lines changed

10 files changed

+119
-7
lines changed

bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,14 @@ class LiveWalletTest {
5959
assertTrue(walletDidSign)
6060

6161
val tx: Transaction = psbt.extractTx()
62-
6362
println("Txid is: ${tx.txid()}")
63+
64+
val txFee = wallet.calculateFee(tx)
65+
println("Tx fee is: ${tx.txid()}")
66+
67+
val feeRate = wallet.calculateFeeRate(tx)
68+
println("Tx fee is: ${tx.txid()} sat/vB")
69+
6470
esploraClient.broadcast(tx)
6571
}
6672
}

bdk-ffi/src/bdk.udl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ enum BdkError {
8282
"Psbt",
8383
};
8484

85+
[Error]
86+
interface CalculateFeeError {
87+
MissingTxOut(sequence<OutPoint> out_points);
88+
NegativeFee(i64 fee);
89+
};
90+
91+
interface FeeRate {
92+
f32 as_sat_per_vb();
93+
};
94+
8595
enum ChangeSpendPolicy {
8696
"ChangeAllowed",
8797
"OnlyChange",
@@ -111,6 +121,12 @@ interface Wallet {
111121
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
112122

113123
sequence<Transaction> transactions();
124+
125+
[Throws=CalculateFeeError]
126+
u64 calculate_fee([ByRef] Transaction tx);
127+
128+
[Throws=CalculateFeeError]
129+
FeeRate calculate_fee_rate([ByRef] Transaction tx);
114130
};
115131

116132
interface Update {};
@@ -348,4 +364,4 @@ interface PartiallySignedTransaction {
348364
dictionary OutPoint {
349365
string txid;
350366
u32 vout;
351-
};
367+
};

bdk-ffi/src/bitcoin.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ impl From<&OutPoint> for BdkOutPoint {
300300
}
301301
}
302302

303+
impl From<&BdkOutPoint> for OutPoint {
304+
fn from(outpoint: &BdkOutPoint) -> Self {
305+
OutPoint {
306+
txid: outpoint.txid.to_string(),
307+
vout: outpoint.vout,
308+
}
309+
}
310+
}
311+
303312
#[derive(Debug, Clone)]
304313
pub struct TxOut {
305314
pub value: u64,

bdk-ffi/src/error.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use crate::bitcoin::OutPoint;
2+
3+
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
4+
5+
use std::fmt;
6+
7+
#[derive(Debug)]
8+
pub enum CalculateFeeError {
9+
MissingTxOut { out_points: Vec<OutPoint> },
10+
NegativeFee { fee: i64 },
11+
}
12+
13+
impl fmt::Display for CalculateFeeError {
14+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15+
match self {
16+
CalculateFeeError::MissingTxOut { out_points } => {
17+
write!(f, "Missing transaction output: {:?}", out_points)
18+
}
19+
CalculateFeeError::NegativeFee { fee } => write!(f, "Negative fee value: {}", fee),
20+
}
21+
}
22+
}
23+
24+
impl From<BdkCalculateFeeError> for CalculateFeeError {
25+
fn from(error: BdkCalculateFeeError) -> Self {
26+
match error {
27+
BdkCalculateFeeError::MissingTxOut(out_points) => CalculateFeeError::MissingTxOut {
28+
out_points: out_points.iter().map(|op| op.into()).collect(),
29+
},
30+
BdkCalculateFeeError::NegativeFee(fee) => CalculateFeeError::NegativeFee { fee },
31+
}
32+
}
33+
}
34+
35+
impl std::error::Error for CalculateFeeError {}

bdk-ffi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod bitcoin;
22
mod descriptor;
3+
mod error;
34
mod esplora;
45
mod keys;
56
mod types;
@@ -13,6 +14,7 @@ use crate::bitcoin::Script;
1314
use crate::bitcoin::Transaction;
1415
use crate::bitcoin::TxOut;
1516
use crate::descriptor::Descriptor;
17+
use crate::error::CalculateFeeError;
1618
use crate::esplora::EsploraClient;
1719
use crate::keys::DerivationPath;
1820
use crate::keys::DescriptorPublicKey;
@@ -21,6 +23,7 @@ use crate::keys::Mnemonic;
2123
use crate::types::AddressIndex;
2224
use crate::types::AddressInfo;
2325
use crate::types::Balance;
26+
use crate::types::FeeRate;
2427
use crate::types::LocalUtxo;
2528
use crate::types::ScriptAmount;
2629
use crate::wallet::BumpFeeTxBuilder;

bdk-ffi/src/types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,22 @@ use bdk::KeychainKind;
77

88
use bdk::LocalUtxo as BdkLocalUtxo;
99

10+
use bdk::FeeRate as BdkFeeRate;
11+
1012
use std::sync::Arc;
1113

14+
pub struct FeeRate(BdkFeeRate);
15+
16+
impl FeeRate {
17+
pub fn new(bdk_fee_rate: BdkFeeRate) -> Self {
18+
FeeRate(bdk_fee_rate)
19+
}
20+
21+
pub fn as_sat_per_vb(&self) -> f32 {
22+
self.0.as_sat_per_vb()
23+
}
24+
}
25+
1226
pub struct ScriptAmount {
1327
pub script: Arc<Script>,
1428
pub amount: u64,

bdk-ffi/src/wallet.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
22
use crate::descriptor::Descriptor;
3-
use crate::types::Balance;
3+
use crate::error::CalculateFeeError;
44
use crate::types::ScriptAmount;
5+
use crate::types::{Balance, FeeRate};
56
use crate::Script;
67
use crate::{AddressIndex, AddressInfo, Network};
78

@@ -10,7 +11,7 @@ use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransact
1011
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
1112
use bdk::wallet::tx_builder::ChangeSpendPolicy;
1213
use bdk::wallet::Update as BdkUpdate;
13-
use bdk::{Error as BdkError, FeeRate};
14+
use bdk::{Error as BdkError, FeeRate as BdkFeeRate};
1415
use bdk::{SignOptions, Wallet as BdkWallet};
1516

1617
use std::collections::HashSet;
@@ -98,6 +99,18 @@ impl Wallet {
9899
.map(|tx| Arc::new(tx.tx_node.tx.clone().into()))
99100
.collect()
100101
}
102+
103+
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
104+
let fee_result = self.get_wallet().calculate_fee(&tx.clone().into());
105+
fee_result.map_err(|err| err.into())
106+
}
107+
108+
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<Arc<FeeRate>, CalculateFeeError> {
109+
self.get_wallet()
110+
.calculate_fee_rate(&tx.clone().into())
111+
.map(|bdk_fee_rate| Arc::new(FeeRate::new(bdk_fee_rate)))
112+
.map_err(|err| err.into())
113+
}
101114
}
102115

103116
pub struct SentAndReceivedValues {
@@ -473,7 +486,7 @@ impl TxBuilder {
473486
tx_builder.manually_selected_only();
474487
}
475488
if let Some(sat_per_vb) = self.fee_rate {
476-
tx_builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
489+
tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(sat_per_vb));
477490
}
478491
if let Some(fee_amount) = self.fee_absolute {
479492
tx_builder.fee_absolute(fee_amount);
@@ -551,7 +564,7 @@ impl BumpFeeTxBuilder {
551564
Txid::from_str(self.txid.as_str()).map_err(|e| BdkError::Generic(e.to_string()))?;
552565
let mut wallet = wallet.get_wallet();
553566
let mut tx_builder = wallet.build_fee_bump(txid)?;
554-
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
567+
tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(self.fee_rate));
555568
if let Some(allow_shrinking) = &self.allow_shrinking {
556569
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
557570
}

bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/LiveWalletTest.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,14 @@ class LiveWalletTest {
5555
assertTrue(walletDidSign)
5656

5757
val tx: Transaction = psbt.extractTx()
58-
5958
println("Txid is: ${tx.txid()}")
59+
60+
val txFee = wallet.calculateFee(tx)
61+
println("Tx fee is: ${tx.txid()}")
62+
63+
val feeRate = wallet.calculateFeeRate(tx)
64+
println("Tx fee is: ${tx.txid()} sat/vB")
65+
6066
esploraClient.broadcast(tx)
6167
}
6268
}

bdk-python/tests/test_live_wallet.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ def test_broadcast_transaction(self):
6464
walletDidSign = wallet.sign(psbt)
6565
self.assertTrue(walletDidSign)
6666
tx = psbt.extract_tx()
67+
print(f"Transaction Id: {tx.txid}")
68+
fee = wallet.calculate_fee(tx)
69+
print(f"Transaction Fee: {fee}")
70+
fee_rate = wallet.calculate_fee_rate(tx)
71+
print(f"Transaction Fee Rate: {fee_rate} sat/vB")
6772

6873
esploraClient.broadcast(tx)
6974

bdk-swift/Tests/BitcoinDevKitTests/LiveWalletTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ final class LiveWalletTests: XCTestCase {
6969

7070
let tx: Transaction = psbt.extractTx()
7171
print(tx.txid())
72+
let fee = try wallet.calculateFee(tx: tx)
73+
print("Transaction Fee: \(fee)")
74+
let feeRate = try wallet.calculateFeeRate(tx: tx)
75+
print("Transaction Fee Rate: \(feeRate.asSatPerVb()) sat/vB")
76+
7277
try esploraClient.broadcast(transaction: tx)
7378
}
7479
}

0 commit comments

Comments
 (0)