feat: add calculate_fee and calculate_fee_rate on wallet
This commit is contained in:
parent
fc25cd709a
commit
3789c1dcd6
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,17 @@ enum BdkError {
|
||||
"Psbt",
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface CalculateFeeError {
|
||||
MissingTxOut(sequence<OutPoint> 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<Transaction> transactions();
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
u64 calculate_fee([ByRef] Transaction tx);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
|
@ -213,9 +213,15 @@ impl From<BdkTransaction> for Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Transaction> for BdkTransaction {
|
||||
fn from(tx: Transaction) -> Self {
|
||||
tx.inner
|
||||
impl From<&BdkTransaction> for Transaction {
|
||||
fn from(tx: &BdkTransaction) -> Self {
|
||||
Transaction { inner: tx.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Transaction> for BdkTransaction {
|
||||
fn from(tx: &Transaction) -> Self {
|
||||
tx.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,6 +316,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,
|
||||
|
82
bdk-ffi/src/error.rs
Normal file
82
bdk-ffi/src/error.rs
Normal file
@ -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<OutPoint> },
|
||||
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<BdkCalculateFeeError> 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<OutPoint> = 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");
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ impl EsploraClient {
|
||||
// pub fn sync();
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.clone().into();
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.broadcast(&bdk_transaction)
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
|
@ -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;
|
||||
|
@ -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<Script>,
|
||||
pub amount: u64,
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::types::Balance;
|
||||
use crate::error::CalculateFeeError;
|
||||
use crate::types::ScriptAmount;
|
||||
use crate::types::{Balance, FeeRate};
|
||||
use crate::Script;
|
||||
use crate::{AddressIndex, AddressInfo, Network};
|
||||
|
||||
@ -10,7 +11,7 @@ use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransact
|
||||
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::wallet::Update as BdkUpdate;
|
||||
use bdk::{Error as BdkError, FeeRate};
|
||||
use bdk::{Error as BdkError, FeeRate as BdkFeeRate};
|
||||
use bdk::{SignOptions, Wallet as BdkWallet};
|
||||
|
||||
use std::collections::HashSet;
|
||||
@ -88,16 +89,29 @@ impl Wallet {
|
||||
}
|
||||
|
||||
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
|
||||
let (sent, received): (u64, u64) = self.get_wallet().sent_and_received(&tx.clone().into());
|
||||
let (sent, received): (u64, u64) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues { sent, received }
|
||||
}
|
||||
|
||||
pub fn transactions(&self) -> Vec<Arc<Transaction>> {
|
||||
self.get_wallet()
|
||||
.transactions()
|
||||
.map(|tx| Arc::new(tx.tx_node.tx.clone().into()))
|
||||
.map(|tx| Arc::new(tx.tx_node.tx.into()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
|
||||
self.get_wallet()
|
||||
.calculate_fee(&tx.into())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<Arc<FeeRate>, CalculateFeeError> {
|
||||
self.get_wallet()
|
||||
.calculate_fee_rate(&tx.into())
|
||||
.map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate)))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SentAndReceivedValues {
|
||||
@ -473,7 +487,7 @@ impl TxBuilder {
|
||||
tx_builder.manually_selected_only();
|
||||
}
|
||||
if let Some(sat_per_vb) = self.fee_rate {
|
||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
|
||||
tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(sat_per_vb));
|
||||
}
|
||||
if let Some(fee_amount) = self.fee_absolute {
|
||||
tx_builder.fee_absolute(fee_amount);
|
||||
@ -551,7 +565,7 @@ impl BumpFeeTxBuilder {
|
||||
Txid::from_str(self.txid.as_str()).map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
|
||||
tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(self.fee_rate));
|
||||
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
|
||||
}
|
||||
|
@ -55,8 +55,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)
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,11 @@ class TestLiveWallet(unittest.TestCase):
|
||||
walletDidSign = wallet.sign(psbt)
|
||||
self.assertTrue(walletDidSign)
|
||||
tx = psbt.extract_tx()
|
||||
print(f"Transaction Id: {tx.txid}")
|
||||
fee = wallet.calculate_fee(tx)
|
||||
print(f"Transaction Fee: {fee}")
|
||||
fee_rate = wallet.calculate_fee_rate(tx)
|
||||
print(f"Transaction Fee Rate: {fee_rate.as_sat_per_vb()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
|
||||
|
@ -69,6 +69,11 @@ final class LiveWalletTests: XCTestCase {
|
||||
|
||||
let tx: Transaction = psbt.extractTx()
|
||||
print(tx.txid())
|
||||
let fee: UInt64 = try wallet.calculateFee(tx: tx)
|
||||
print("Transaction Fee: \(fee)")
|
||||
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
|
||||
print("Transaction Fee Rate: \(feeRate.asSatPerVb()) sat/vB")
|
||||
|
||||
try esploraClient.broadcast(transaction: tx)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user