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)
|
assertTrue(walletDidSign)
|
||||||
|
|
||||||
val tx: Transaction = psbt.extractTx()
|
val tx: Transaction = psbt.extractTx()
|
||||||
|
|
||||||
println("Txid is: ${tx.txid()}")
|
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)
|
esploraClient.broadcast(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,17 @@ enum BdkError {
|
|||||||
"Psbt",
|
"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 {
|
enum ChangeSpendPolicy {
|
||||||
"ChangeAllowed",
|
"ChangeAllowed",
|
||||||
"OnlyChange",
|
"OnlyChange",
|
||||||
@ -111,6 +122,12 @@ interface Wallet {
|
|||||||
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
|
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
|
||||||
|
|
||||||
sequence<Transaction> transactions();
|
sequence<Transaction> transactions();
|
||||||
|
|
||||||
|
[Throws=CalculateFeeError]
|
||||||
|
u64 calculate_fee([ByRef] Transaction tx);
|
||||||
|
|
||||||
|
[Throws=CalculateFeeError]
|
||||||
|
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Update {};
|
interface Update {};
|
||||||
|
@ -213,9 +213,15 @@ impl From<BdkTransaction> for Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Transaction> for BdkTransaction {
|
impl From<&BdkTransaction> for Transaction {
|
||||||
fn from(tx: Transaction) -> Self {
|
fn from(tx: &BdkTransaction) -> Self {
|
||||||
tx.inner
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TxOut {
|
pub struct TxOut {
|
||||||
pub value: u64,
|
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 sync();
|
||||||
|
|
||||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
|
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
|
||||||
let bdk_transaction: BdkTransaction = transaction.clone().into();
|
let bdk_transaction: BdkTransaction = transaction.into();
|
||||||
self.0
|
self.0
|
||||||
.broadcast(&bdk_transaction)
|
.broadcast(&bdk_transaction)
|
||||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
mod bitcoin;
|
mod bitcoin;
|
||||||
mod descriptor;
|
mod descriptor;
|
||||||
|
mod error;
|
||||||
mod esplora;
|
mod esplora;
|
||||||
mod keys;
|
mod keys;
|
||||||
mod types;
|
mod types;
|
||||||
@ -13,6 +14,7 @@ use crate::bitcoin::Script;
|
|||||||
use crate::bitcoin::Transaction;
|
use crate::bitcoin::Transaction;
|
||||||
use crate::bitcoin::TxOut;
|
use crate::bitcoin::TxOut;
|
||||||
use crate::descriptor::Descriptor;
|
use crate::descriptor::Descriptor;
|
||||||
|
use crate::error::CalculateFeeError;
|
||||||
use crate::esplora::EsploraClient;
|
use crate::esplora::EsploraClient;
|
||||||
use crate::keys::DerivationPath;
|
use crate::keys::DerivationPath;
|
||||||
use crate::keys::DescriptorPublicKey;
|
use crate::keys::DescriptorPublicKey;
|
||||||
@ -21,6 +23,7 @@ use crate::keys::Mnemonic;
|
|||||||
use crate::types::AddressIndex;
|
use crate::types::AddressIndex;
|
||||||
use crate::types::AddressInfo;
|
use crate::types::AddressInfo;
|
||||||
use crate::types::Balance;
|
use crate::types::Balance;
|
||||||
|
use crate::types::FeeRate;
|
||||||
use crate::types::LocalUtxo;
|
use crate::types::LocalUtxo;
|
||||||
use crate::types::ScriptAmount;
|
use crate::types::ScriptAmount;
|
||||||
use crate::wallet::BumpFeeTxBuilder;
|
use crate::wallet::BumpFeeTxBuilder;
|
||||||
|
@ -7,8 +7,22 @@ use bdk::KeychainKind;
|
|||||||
|
|
||||||
use bdk::LocalUtxo as BdkLocalUtxo;
|
use bdk::LocalUtxo as BdkLocalUtxo;
|
||||||
|
|
||||||
|
use bdk::FeeRate as BdkFeeRate;
|
||||||
|
|
||||||
use std::sync::Arc;
|
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 struct ScriptAmount {
|
||||||
pub script: Arc<Script>,
|
pub script: Arc<Script>,
|
||||||
pub amount: u64,
|
pub amount: u64,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
|
use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
|
||||||
use crate::descriptor::Descriptor;
|
use crate::descriptor::Descriptor;
|
||||||
use crate::types::Balance;
|
use crate::error::CalculateFeeError;
|
||||||
use crate::types::ScriptAmount;
|
use crate::types::ScriptAmount;
|
||||||
|
use crate::types::{Balance, FeeRate};
|
||||||
use crate::Script;
|
use crate::Script;
|
||||||
use crate::{AddressIndex, AddressInfo, Network};
|
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::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||||
use bdk::wallet::Update as BdkUpdate;
|
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 bdk::{SignOptions, Wallet as BdkWallet};
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -88,16 +89,29 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
|
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 }
|
SentAndReceivedValues { sent, received }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transactions(&self) -> Vec<Arc<Transaction>> {
|
pub fn transactions(&self) -> Vec<Arc<Transaction>> {
|
||||||
self.get_wallet()
|
self.get_wallet()
|
||||||
.transactions()
|
.transactions()
|
||||||
.map(|tx| Arc::new(tx.tx_node.tx.clone().into()))
|
.map(|tx| Arc::new(tx.tx_node.tx.into()))
|
||||||
.collect()
|
.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 {
|
pub struct SentAndReceivedValues {
|
||||||
@ -473,7 +487,7 @@ impl TxBuilder {
|
|||||||
tx_builder.manually_selected_only();
|
tx_builder.manually_selected_only();
|
||||||
}
|
}
|
||||||
if let Some(sat_per_vb) = self.fee_rate {
|
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 {
|
if let Some(fee_amount) = self.fee_absolute {
|
||||||
tx_builder.fee_absolute(fee_amount);
|
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()))?;
|
Txid::from_str(self.txid.as_str()).map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||||
let mut wallet = wallet.get_wallet();
|
let mut wallet = wallet.get_wallet();
|
||||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
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 {
|
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||||
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
|
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,14 @@ class LiveWalletTest {
|
|||||||
assertTrue(walletDidSign)
|
assertTrue(walletDidSign)
|
||||||
|
|
||||||
val tx: Transaction = psbt.extractTx()
|
val tx: Transaction = psbt.extractTx()
|
||||||
|
|
||||||
println("Txid is: ${tx.txid()}")
|
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)
|
esploraClient.broadcast(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,11 @@ class TestLiveWallet(unittest.TestCase):
|
|||||||
walletDidSign = wallet.sign(psbt)
|
walletDidSign = wallet.sign(psbt)
|
||||||
self.assertTrue(walletDidSign)
|
self.assertTrue(walletDidSign)
|
||||||
tx = psbt.extract_tx()
|
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)
|
esploraClient.broadcast(tx)
|
||||||
|
|
||||||
|
@ -69,6 +69,11 @@ final class LiveWalletTests: XCTestCase {
|
|||||||
|
|
||||||
let tx: Transaction = psbt.extractTx()
|
let tx: Transaction = psbt.extractTx()
|
||||||
print(tx.txid())
|
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)
|
try esploraClient.broadcast(transaction: tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user