]> Untitled Git - bdk/commitdiff
feat: use `Amount` on `calculate_fee`, `fee_absolute`, `fee_amount` and others
authorLeonardo Lima <oleonardolima@users.noreply.github.com>
Sun, 5 May 2024 20:41:31 +0000 (17:41 -0300)
committerLeonardo Lima <oleonardolima@users.noreply.github.com>
Mon, 3 Jun 2024 16:19:16 +0000 (13:19 -0300)
- update to use `bitcoin::Amount` on `CreateTxError::FeeTooLow` variant.
- update to use `bitcoin::Amount` on `Wallet::calculate_fee()`.
- update to use `bitcoin::Amount` on `FeePolicy::fee_absolute()`.
- update to use `bitcoin::SignedAmount` on
  `CalculateFeeError::NegativeFee` variant.
- update to use `bitcoin::Amount` on `TxGraph::calculate_fee()`.
- update to use `bitcoin::Amount` on `PsbUtils::fee_amount()`

crates/chain/src/tx_graph.rs
crates/chain/tests/test_tx_graph.rs
crates/electrum/tests/test_electrum.rs
crates/esplora/tests/async_ext.rs
crates/esplora/tests/blocking_ext.rs
crates/wallet/src/psbt/mod.rs
crates/wallet/src/wallet/error.rs
crates/wallet/src/wallet/mod.rs
crates/wallet/src/wallet/tx_builder.rs
crates/wallet/tests/wallet.rs

index d097b8fa57767dd7b1346f3d9c9dbc3920d4d24c..eb119e3c9022de1c549c13c14a8254e1abdce1ba 100644 (file)
@@ -95,7 +95,7 @@ use crate::{
 use alloc::collections::vec_deque::VecDeque;
 use alloc::sync::Arc;
 use alloc::vec::Vec;
-use bitcoin::{Amount, OutPoint, Script, Transaction, TxOut, Txid};
+use bitcoin::{Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid};
 use core::fmt::{self, Formatter};
 use core::{
     convert::Infallible,
@@ -182,7 +182,7 @@ pub enum CalculateFeeError {
     /// Missing `TxOut` for one or more of the inputs of the tx
     MissingTxOut(Vec<OutPoint>),
     /// When the transaction is invalid according to the graph it has a negative fee
-    NegativeFee(i64),
+    NegativeFee(SignedAmount),
 }
 
 impl fmt::Display for CalculateFeeError {
@@ -196,7 +196,7 @@ impl fmt::Display for CalculateFeeError {
             CalculateFeeError::NegativeFee(fee) => write!(
                 f,
                 "transaction is invalid according to the graph and has negative fee: {}",
-                fee
+                fee.display_dynamic()
             ),
         }
     }
@@ -307,7 +307,7 @@ impl<A> TxGraph<A> {
         })
     }
 
-    /// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
+    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
     /// Returns `OK(_)` if we have all the [`TxOut`]s being spent by `tx` in the graph (either as
     /// the full transactions or individual txouts).
     ///
@@ -318,20 +318,20 @@ impl<A> TxGraph<A> {
     /// Note `tx` does not have to be in the graph for this to work.
     ///
     /// [`insert_txout`]: Self::insert_txout
-    pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
+    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
         if tx.is_coinbase() {
-            return Ok(0);
+            return Ok(Amount::ZERO);
         }
 
         let (inputs_sum, missing_outputs) = tx.input.iter().fold(
-            (0_i64, Vec::new()),
+            (SignedAmount::ZERO, Vec::new()),
             |(mut sum, mut missing_outpoints), txin| match self.get_txout(txin.previous_output) {
                 None => {
                     missing_outpoints.push(txin.previous_output);
                     (sum, missing_outpoints)
                 }
                 Some(txout) => {
-                    sum += txout.value.to_sat() as i64;
+                    sum += txout.value.to_signed().expect("valid `SignedAmount`");
                     (sum, missing_outpoints)
                 }
             },
@@ -343,15 +343,12 @@ impl<A> TxGraph<A> {
         let outputs_sum = tx
             .output
             .iter()
-            .map(|txout| txout.value.to_sat() as i64)
-            .sum::<i64>();
+            .map(|txout| txout.value.to_signed().expect("valid `SignedAmount`"))
+            .sum::<SignedAmount>();
 
         let fee = inputs_sum - outputs_sum;
-        if fee < 0 {
-            Err(CalculateFeeError::NegativeFee(fee))
-        } else {
-            Ok(fee as u64)
-        }
+        fee.to_unsigned()
+            .map_err(|_| CalculateFeeError::NegativeFee(fee))
     }
 
     /// The transactions spending from this output.
index 26e29ed1225ab51bf1a031eb1b5192d6f347e80e..6377588623b93d51c3b98c44d4a967c95fd4e5b0 100644 (file)
@@ -10,8 +10,8 @@ use bdk_chain::{
     Anchor, Append, BlockId, ChainOracle, ChainPosition, ConfirmationHeightAnchor,
 };
 use bitcoin::{
-    absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn,
-    TxOut, Txid,
+    absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount,
+    Transaction, TxIn, TxOut, Txid,
 };
 use common::*;
 use core::iter;
@@ -466,14 +466,14 @@ fn test_calculate_fee() {
         }],
     };
 
-    assert_eq!(graph.calculate_fee(&tx), Ok(100));
+    assert_eq!(graph.calculate_fee(&tx), Ok(Amount::from_sat(100)));
 
     tx.input.remove(2);
 
     // fee would be negative, should return CalculateFeeError::NegativeFee
     assert_eq!(
         graph.calculate_fee(&tx),
-        Err(CalculateFeeError::NegativeFee(-200))
+        Err(CalculateFeeError::NegativeFee(SignedAmount::from_sat(-200)))
     );
 
     // If we have an unknown outpoint, fee should return CalculateFeeError::MissingTxOut.
@@ -505,7 +505,7 @@ fn test_calculate_fee_on_coinbase() {
 
     let graph = TxGraph::<()>::default();
 
-    assert_eq!(graph.calculate_fee(&tx), Ok(0));
+    assert_eq!(graph.calculate_fee(&tx), Ok(Amount::ZERO));
 }
 
 // `test_walk_ancestors` uses the following transaction structure:
index dd7ee6a922a46a917707c7ba2f15e0179fd40e1b..bbf25a4f974b096ed9e4b6370e904f63d3d7d670 100644 (file)
@@ -101,7 +101,8 @@ fn scan_detects_confirmed_tx() -> anyhow::Result<()> {
             .fee
             .expect("Fee must exist")
             .abs()
-            .to_sat() as u64;
+            .to_unsigned()
+            .expect("valid `SignedAmount`");
 
         // Check that the calculated fee matches the fee from the transaction data.
         assert_eq!(fee, tx_fee);
index c6f7d6ce3d756ce0a9e4c5ade8a7f3f1347c529f..2258c9d60b7bcde57d17354c8a7674919e2d31ca 100644 (file)
@@ -92,7 +92,8 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
             .fee
             .expect("Fee must exist")
             .abs()
-            .to_sat() as u64;
+            .to_unsigned()
+            .expect("valid `Amount`");
 
         // Check that the calculated fee matches the fee from the transaction data.
         assert_eq!(fee, tx_fee);
index 1d8d8d78ddba709b5bcfab2d68d842583c32e0f5..2e363f4e6de3e3713fe9b93b111b568552ed702f 100644 (file)
@@ -92,7 +92,8 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
             .fee
             .expect("Fee must exist")
             .abs()
-            .to_sat() as u64;
+            .to_unsigned()
+            .expect("valid `Amount`");
 
         // Check that the calculated fee matches the fee from the transaction data.
         assert_eq!(fee, tx_fee);
index 7a66989e98565fe56658c1db2d569d83d29bd82a..9b3aea843dc939166d908f7fab0854518ee72324 100644 (file)
@@ -26,7 +26,7 @@ pub trait PsbtUtils {
 
     /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in sats.
     /// If the PSBT is missing a TxOut for an input returns None.
-    fn fee_amount(&self) -> Option<u64>;
+    fn fee_amount(&self) -> Option<Amount>;
 
     /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
     /// `Psbt` is finalized and all witness/signature data is added to the
@@ -49,18 +49,13 @@ impl PsbtUtils for Psbt {
         }
     }
 
-    fn fee_amount(&self) -> Option<u64> {
+    fn fee_amount(&self) -> Option<Amount> {
         let tx = &self.unsigned_tx;
         let utxos: Option<Vec<TxOut>> = (0..tx.input.len()).map(|i| self.get_utxo_for(i)).collect();
 
         utxos.map(|inputs| {
-            let input_amount: u64 = inputs.iter().map(|i| i.value.to_sat()).sum();
-            let output_amount: u64 = self
-                .unsigned_tx
-                .output
-                .iter()
-                .map(|o| o.value.to_sat())
-                .sum();
+            let input_amount: Amount = inputs.iter().map(|i| i.value).sum();
+            let output_amount: Amount = self.unsigned_tx.output.iter().map(|o| o.value).sum();
             input_amount
                 .checked_sub(output_amount)
                 .expect("input amount must be greater than output amount")
@@ -70,6 +65,6 @@ impl PsbtUtils for Psbt {
     fn fee_rate(&self) -> Option<FeeRate> {
         let fee_amount = self.fee_amount();
         let weight = self.clone().extract_tx().ok()?.weight();
-        fee_amount.map(|fee| Amount::from_sat(fee) / weight)
+        fee_amount.map(|fee| fee / weight)
     }
 }
index eaf811d6fc06bdba89519499634554659ee10b9e..56612c54e046d0303f9e6386d4afc51de4222874 100644 (file)
@@ -16,7 +16,7 @@ use crate::descriptor::DescriptorError;
 use crate::wallet::coin_selection;
 use crate::{descriptor, KeychainKind};
 use alloc::string::String;
-use bitcoin::{absolute, psbt, OutPoint, Sequence, Txid};
+use bitcoin::{absolute, psbt, Amount, OutPoint, Sequence, Txid};
 use core::fmt;
 
 /// Errors returned by miniscript when updating inconsistent PSBTs
@@ -78,8 +78,8 @@ pub enum CreateTxError {
     },
     /// When bumping a tx the absolute fee requested is lower than replaced tx absolute fee
     FeeTooLow {
-        /// Required fee absolute value (satoshi)
-        required: u64,
+        /// Required fee absolute value [`Amount`]
+        required: Amount,
     },
     /// When bumping a tx the fee rate requested is lower than required
     FeeRateTooLow {
@@ -160,7 +160,7 @@ impl fmt::Display for CreateTxError {
                 )
             }
             CreateTxError::FeeTooLow { required } => {
-                write!(f, "Fee to low: required {} sat", required)
+                write!(f, "Fee to low: required {}", required.display_dynamic())
             }
             CreateTxError::FeeRateTooLow { required } => {
                 write!(
index e80584dc31064f3728e9b6f3fcc4ec006786f28e..42cf6d62ed5c06e8137e9e6f23eca3bfbd952d9c 100644 (file)
@@ -977,7 +977,7 @@ impl Wallet {
         self.persist.stage(ChangeSet::from(additions));
     }
 
-    /// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
+    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
     ///
     /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
     /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
@@ -1004,7 +1004,7 @@ impl Wallet {
     /// let fee = wallet.calculate_fee(tx).expect("fee");
     /// ```
     /// [`insert_txout`]: Self::insert_txout
-    pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
+    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
         self.indexed_graph.graph().calculate_fee(tx)
     }
 
@@ -1036,8 +1036,7 @@ impl Wallet {
     /// ```
     /// [`insert_txout`]: Self::insert_txout
     pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
-        self.calculate_fee(tx)
-            .map(|fee| Amount::from_sat(fee) / tx.weight())
+        self.calculate_fee(tx).map(|fee| fee / tx.weight())
     }
 
     /// Compute the `tx`'s sent and received [`Amount`]s.
@@ -1465,7 +1464,7 @@ impl Wallet {
                 if let Some(previous_fee) = params.bumping_fee {
                     if fee < previous_fee.absolute {
                         return Err(CreateTxError::FeeTooLow {
-                            required: previous_fee.absolute,
+                            required: Amount::from_sat(previous_fee.absolute),
                         });
                     }
                 }
@@ -1498,9 +1497,8 @@ impl Wallet {
             return Err(CreateTxError::NoUtxosSelected);
         }
 
-        // we keep it as a float while we accumulate it, and only round it at the end
-        let mut outgoing: u64 = 0;
-        let mut received: u64 = 0;
+        let mut outgoing = Amount::ZERO;
+        let mut received = Amount::ZERO;
 
         let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
 
@@ -1513,7 +1511,7 @@ impl Wallet {
             }
 
             if self.is_mine(script_pubkey) {
-                received += value;
+                received += Amount::from_sat(value);
             }
 
             let new_out = TxOut {
@@ -1523,7 +1521,7 @@ impl Wallet {
 
             tx.output.push(new_out);
 
-            outgoing += value;
+            outgoing += Amount::from_sat(value);
         }
 
         fee_amount += (fee_rate * tx.weight()).to_sat();
@@ -1565,7 +1563,7 @@ impl Wallet {
             required_utxos,
             optional_utxos,
             fee_rate,
-            outgoing + fee_amount,
+            outgoing.to_sat() + fee_amount,
             &drain_script,
         )?;
         fee_amount += coin_selection.fee_amount;
@@ -1613,7 +1611,7 @@ impl Wallet {
             } => fee_amount += remaining_amount,
             Change { amount, fee } => {
                 if self.is_mine(&drain_script) {
-                    received += amount;
+                    received += Amount::from_sat(*amount);
                 }
                 fee_amount += fee;
 
@@ -1797,7 +1795,7 @@ impl Wallet {
                 .collect(),
             utxos: original_utxos,
             bumping_fee: Some(tx_builder::PreviousFee {
-                absolute: fee,
+                absolute: fee.to_sat(),
                 rate: fee_rate,
             }),
             ..Default::default()
index 7b9522013d97beac1ff72bc2b715b1261f319d28..fbe5081f71052ba2bf6abe45be87ac847a398d8e 100644 (file)
@@ -186,10 +186,10 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
     }
 
     /// Set an absolute fee
-    /// The fee_absolute method refers to the absolute transaction fee in satoshis (sats).
-    /// If anyone sets both the fee_absolute method and the fee_rate method,
-    /// the FeePolicy enum will be set by whichever method was called last,
-    /// as the FeeRate and FeeAmount are mutually exclusive.
+    /// The fee_absolute method refers to the absolute transaction fee in [`Amount`].
+    /// If anyone sets both the `fee_absolute` method and the `fee_rate` method,
+    /// the `FeePolicy` enum will be set by whichever method was called last,
+    /// as the [`FeeRate`] and `FeeAmount` are mutually exclusive.
     ///
     /// Note that this is really a minimum absolute fee -- it's possible to
     /// overshoot it slightly since adding a change output to drain the remaining
index 9b27a2795d33d85241fd7620b5740f12db86823b..b513ffd81ea29132ccd40304c13580aa5edb1c35 100644 (file)
@@ -325,7 +325,7 @@ fn test_get_funded_wallet_tx_fees() {
     // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
     // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
     // sats are the transaction fee.
-    assert_eq!(tx_fee, 1000)
+    assert_eq!(tx_fee, Amount::from_sat(1000))
 }
 
 #[test]
@@ -390,16 +390,16 @@ macro_rules! assert_fee_rate {
             let fee_amount = psbt
             .inputs
             .iter()
-            .fold(0, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value.to_sat())
+            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
             - psbt
             .unsigned_tx
             .output
             .iter()
-            .fold(0, |acc, o| acc + o.value.to_sat());
+            .fold(Amount::ZERO, |acc, o| acc + o.value);
 
         assert_eq!(fee_amount, $fees);
 
-        let tx_fee_rate = (Amount::from_sat(fee_amount) / tx.weight())
+        let tx_fee_rate = (fee_amount / tx.weight())
             .to_sat_per_kwu();
         let fee_rate = $fee_rate.to_sat_per_kwu();
         let half_default = FeeRate::BROADCAST_MIN.checked_div(2)
@@ -681,8 +681,8 @@ fn test_create_tx_drain_wallet_and_drain_to() {
 
     assert_eq!(psbt.unsigned_tx.output.len(), 1);
     assert_eq!(
-        psbt.unsigned_tx.output[0].value.to_sat(),
-        50_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[0].value,
+        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
     );
 }
 
@@ -711,8 +711,11 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
         .iter()
         .find(|x| x.script_pubkey == drain_addr.script_pubkey())
         .unwrap();
-    assert_eq!(main_output.value.to_sat(), 20_000,);
-    assert_eq!(drain_output.value.to_sat(), 30_000 - fee.unwrap_or(0));
+    assert_eq!(main_output.value, Amount::from_sat(20_000));
+    assert_eq!(
+        drain_output.value,
+        Amount::from_sat(30_000) - fee.unwrap_or(Amount::ZERO)
+    );
 }
 
 #[test]
@@ -730,8 +733,8 @@ fn test_create_tx_drain_to_and_utxos() {
 
     assert_eq!(psbt.unsigned_tx.output.len(), 1);
     assert_eq!(
-        psbt.unsigned_tx.output[0].value.to_sat(),
-        50_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[0].value,
+        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
     );
 }
 
@@ -754,7 +757,7 @@ fn test_create_tx_default_fee_rate() {
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::BROADCAST_MIN, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::BROADCAST_MIN, @add_signature);
 }
 
 #[test]
@@ -768,7 +771,7 @@ fn test_create_tx_custom_fee_rate() {
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
 }
 
 #[test]
@@ -783,11 +786,11 @@ fn test_create_tx_absolute_fee() {
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_eq!(fee.unwrap_or(0), 100);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(100));
     assert_eq!(psbt.unsigned_tx.output.len(), 1);
     assert_eq!(
-        psbt.unsigned_tx.output[0].value.to_sat(),
-        50_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[0].value,
+        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
     );
 }
 
@@ -803,11 +806,11 @@ fn test_create_tx_absolute_zero_fee() {
     let psbt = builder.finish().unwrap();
     let fee = check_fee!(wallet, psbt);
 
-    assert_eq!(fee.unwrap_or(0), 0);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::ZERO);
     assert_eq!(psbt.unsigned_tx.output.len(), 1);
     assert_eq!(
-        psbt.unsigned_tx.output[0].value.to_sat(),
-        50_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[0].value,
+        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
     );
 }
 
@@ -838,10 +841,10 @@ fn test_create_tx_add_change() {
     let fee = check_fee!(wallet, psbt);
 
     assert_eq!(psbt.unsigned_tx.output.len(), 2);
-    assert_eq!(psbt.unsigned_tx.output[0].value.to_sat(), 25_000);
+    assert_eq!(psbt.unsigned_tx.output[0].value, Amount::from_sat(25_000));
     assert_eq!(
-        psbt.unsigned_tx.output[1].value.to_sat(),
-        25_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[1].value,
+        Amount::from_sat(25_000) - fee.unwrap_or(Amount::ZERO)
     );
 }
 
@@ -856,7 +859,7 @@ fn test_create_tx_skip_change_dust() {
 
     assert_eq!(psbt.unsigned_tx.output.len(), 1);
     assert_eq!(psbt.unsigned_tx.output[0].value.to_sat(), 49_800);
-    assert_eq!(fee.unwrap_or(0), 200);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
 }
 
 #[test]
@@ -887,11 +890,11 @@ fn test_create_tx_ordering_respected() {
 
     assert_eq!(psbt.unsigned_tx.output.len(), 3);
     assert_eq!(
-        psbt.unsigned_tx.output[0].value.to_sat(),
-        10_000 - fee.unwrap_or(0)
+        psbt.unsigned_tx.output[0].value,
+        Amount::from_sat(10_000) - fee.unwrap_or(Amount::ZERO)
     );
-    assert_eq!(psbt.unsigned_tx.output[1].value.to_sat(), 10_000);
-    assert_eq!(psbt.unsigned_tx.output[2].value.to_sat(), 30_000);
+    assert_eq!(psbt.unsigned_tx.output[1].value, Amount::from_sat(10_000));
+    assert_eq!(psbt.unsigned_tx.output[2].value, Amount::from_sat(30_000));
 }
 
 #[test]
@@ -1318,8 +1321,8 @@ fn test_add_foreign_utxo() {
         wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
 
     assert_eq!(
-        (sent_received.0 - sent_received.1).to_sat(),
-        10_000 + fee.unwrap_or(0),
+        (sent_received.0 - sent_received.1),
+        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
         "we should have only net spent ~10_000"
     );
 
@@ -1715,10 +1718,10 @@ fn test_bump_fee_reduce_change() {
 
     assert_eq!(sent_received.0, original_sent_received.0);
     assert_eq!(
-        sent_received.1 + Amount::from_sat(fee.unwrap_or(0)),
-        original_sent_received.1 + Amount::from_sat(original_fee.unwrap_or(0))
+        sent_received.1 + fee.unwrap_or(Amount::ZERO),
+        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
     );
-    assert!(fee.unwrap_or(0) > original_fee.unwrap_or(0));
+    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
 
     let tx = &psbt.unsigned_tx;
     assert_eq!(tx.output.len(), 2);
@@ -1739,7 +1742,7 @@ fn test_bump_fee_reduce_change() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), feerate, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_absolute(Amount::from_sat(200));
@@ -1751,14 +1754,14 @@ fn test_bump_fee_reduce_change() {
 
     assert_eq!(sent_received.0, original_sent_received.0);
     assert_eq!(
-        sent_received.1 + Amount::from_sat(fee.unwrap_or(0)),
-        original_sent_received.1 + Amount::from_sat(original_fee.unwrap_or(0))
+        sent_received.1 + fee.unwrap_or(Amount::ZERO),
+        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
     );
     assert!(
-        fee.unwrap_or(0) > original_fee.unwrap_or(0),
+        fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO),
         "{} > {}",
-        fee.unwrap_or(0),
-        original_fee.unwrap_or(0)
+        fee.unwrap_or(Amount::ZERO),
+        original_fee.unwrap_or(Amount::ZERO)
     );
 
     let tx = &psbt.unsigned_tx;
@@ -1780,7 +1783,7 @@ fn test_bump_fee_reduce_change() {
         sent_received.1
     );
 
-    assert_eq!(fee.unwrap_or(0), 200);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
 }
 
 #[test]
@@ -1819,16 +1822,16 @@ fn test_bump_fee_reduce_single_recipient() {
     let fee = check_fee!(wallet, psbt);
 
     assert_eq!(sent_received.0, original_sent_received.0);
-    assert!(fee.unwrap_or(0) > original_fee.unwrap_or(0));
+    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
 
     let tx = &psbt.unsigned_tx;
     assert_eq!(tx.output.len(), 1);
     assert_eq!(
-        tx.output[0].value + Amount::from_sat(fee.unwrap_or(0)),
+        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
         sent_received.0
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), feerate, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
 }
 
 #[test]
@@ -1866,15 +1869,15 @@ fn test_bump_fee_absolute_reduce_single_recipient() {
     let fee = check_fee!(wallet, psbt);
 
     assert_eq!(sent_received.0, original_sent_received.0);
-    assert!(fee.unwrap_or(0) > original_fee.unwrap_or(0));
+    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
 
     assert_eq!(tx.output.len(), 1);
     assert_eq!(
-        tx.output[0].value + Amount::from_sat(fee.unwrap_or(0)),
+        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
         sent_received.0
     );
 
-    assert_eq!(fee.unwrap_or(0), 300);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(300));
 }
 
 #[test]
@@ -2051,7 +2054,7 @@ fn test_bump_fee_add_input() {
         original_details.0 + Amount::from_sat(25_000)
     );
     assert_eq!(
-        Amount::from_sat(fee.unwrap_or(0)) + sent_received.1,
+        fee.unwrap_or(Amount::ZERO) + sent_received.1,
         Amount::from_sat(30_000)
     );
 
@@ -2075,7 +2078,7 @@ fn test_bump_fee_add_input() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
 }
 
 #[test]
@@ -2109,7 +2112,7 @@ fn test_bump_fee_absolute_add_input() {
         original_sent_received.0 + Amount::from_sat(25_000)
     );
     assert_eq!(
-        Amount::from_sat(fee.unwrap_or(0)) + sent_received.1,
+        fee.unwrap_or(Amount::ZERO) + sent_received.1,
         Amount::from_sat(30_000)
     );
 
@@ -2133,7 +2136,7 @@ fn test_bump_fee_absolute_add_input() {
         sent_received.1
     );
 
-    assert_eq!(fee.unwrap_or(0), 6_000);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(6_000));
 }
 
 #[test]
@@ -2172,15 +2175,14 @@ fn test_bump_fee_no_change_add_input_and_change() {
         wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
     let fee = check_fee!(wallet, psbt);
 
-    let original_send_all_amount =
-        original_sent_received.0 - Amount::from_sat(original_fee.unwrap_or(0));
+    let original_send_all_amount = original_sent_received.0 - original_fee.unwrap_or(Amount::ZERO);
     assert_eq!(
         sent_received.0,
         original_sent_received.0 + Amount::from_sat(50_000)
     );
     assert_eq!(
         sent_received.1,
-        Amount::from_sat(75_000) - original_send_all_amount - Amount::from_sat(fee.unwrap_or(0))
+        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
     );
 
     let tx = &psbt.unsigned_tx;
@@ -2200,10 +2202,10 @@ fn test_bump_fee_no_change_add_input_and_change() {
             .find(|txout| txout.script_pubkey != addr.script_pubkey())
             .unwrap()
             .value,
-        Amount::from_sat(75_000) - original_send_all_amount - Amount::from_sat(fee.unwrap_or(0))
+        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
 }
 
 #[test]
@@ -2257,14 +2259,14 @@ fn test_bump_fee_add_input_change_dust() {
 
     assert_eq!(
         original_sent_received.1,
-        Amount::from_sat(5_000 - original_fee.unwrap_or(0))
+        Amount::from_sat(5_000) - original_fee.unwrap_or(Amount::ZERO)
     );
 
     assert_eq!(
         sent_received.0,
         original_sent_received.0 + Amount::from_sat(25_000)
     );
-    assert_eq!(fee.unwrap_or(0), 30_000);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(30_000));
     assert_eq!(sent_received.1, Amount::ZERO);
 
     let tx = &psbt.unsigned_tx;
@@ -2279,7 +2281,7 @@ fn test_bump_fee_add_input_change_dust() {
         Amount::from_sat(45_000)
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(140), @dust_change, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(140), @dust_change, @add_signature);
 }
 
 #[test]
@@ -2321,7 +2323,7 @@ fn test_bump_fee_force_add_input() {
         original_sent_received.0 + Amount::from_sat(25_000)
     );
     assert_eq!(
-        Amount::from_sat(fee.unwrap_or(0)) + sent_received.1,
+        fee.unwrap_or(Amount::ZERO) + sent_received.1,
         Amount::from_sat(30_000)
     );
 
@@ -2345,7 +2347,7 @@ fn test_bump_fee_force_add_input() {
         sent_received.1
     );
 
-    assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
 }
 
 #[test]
@@ -2389,7 +2391,7 @@ fn test_bump_fee_absolute_force_add_input() {
         original_sent_received.0 + Amount::from_sat(25_000)
     );
     assert_eq!(
-        Amount::from_sat(fee.unwrap_or(0)) + sent_received.1,
+        fee.unwrap_or(Amount::ZERO) + sent_received.1,
         Amount::from_sat(30_000)
     );
 
@@ -2413,7 +2415,7 @@ fn test_bump_fee_absolute_force_add_input() {
         sent_received.1
     );
 
-    assert_eq!(fee.unwrap_or(0), 250);
+    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(250));
 }
 
 #[test]
@@ -2524,7 +2526,7 @@ fn test_fee_amount_negative_drain_val() {
     let fee = check_fee!(wallet, psbt);
 
     assert_eq!(psbt.inputs.len(), 1);
-    assert_fee_rate!(psbt, fee.unwrap_or(0), fee_rate, @add_signature);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate, @add_signature);
 }
 
 #[test]
@@ -3361,7 +3363,7 @@ fn test_taproot_foreign_utxo() {
 
     assert_eq!(
         sent_received.0 - sent_received.1,
-        Amount::from_sat(10_000 + fee.unwrap_or(0)),
+        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
         "we should have only net spent ~10_000"
     );
 
@@ -3865,7 +3867,7 @@ fn test_fee_rate_sign_no_grinding_high_r() {
         )
         .unwrap();
     // ...and checking that everything is fine
-    assert_fee_rate!(psbt, fee.unwrap_or(0), fee_rate);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
 }
 
 #[test]
@@ -3899,7 +3901,7 @@ fn test_fee_rate_sign_grinding_low_r() {
     let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
     let sig_len = psbt.inputs[0].partial_sigs[key].sig.serialize_der().len();
     assert_eq!(sig_len, 70);
-    assert_fee_rate!(psbt, fee.unwrap_or(0), fee_rate);
+    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
 }
 
 #[test]