diff --git a/mutiny-core/src/error.rs b/mutiny-core/src/error.rs index 7ab49d9c8..ee6fcbc0d 100644 --- a/mutiny-core/src/error.rs +++ b/mutiny-core/src/error.rs @@ -282,6 +282,7 @@ impl From for MutinyError { match e { bdk::Error::Signer(_) => Self::WalletSigningFailed, bdk::Error::InsufficientFunds { .. } => Self::InsufficientBalance, + bdk::Error::TransactionNotFound => Self::NotFound, _ => Self::WalletOperationFailed, } } diff --git a/mutiny-core/src/nodemanager.rs b/mutiny-core/src/nodemanager.rs index b751788b5..cbbc70119 100644 --- a/mutiny-core/src/nodemanager.rs +++ b/mutiny-core/src/nodemanager.rs @@ -50,6 +50,7 @@ use payjoin::{PjUri, PjUriExt}; use reqwest::Client; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::cmp::max; use std::io::Cursor; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; @@ -935,6 +936,22 @@ impl NodeManager { self.wallet.estimate_sweep_tx_fee(script, fee_rate) } + /// Bumps the given transaction by replacing the given tx with a transaction at + /// the new given fee rate in sats/vbyte + pub async fn bump_fee(&self, txid: Txid, new_fee_rate: f32) -> Result { + // check that this is not a funding tx for any channels, + // bumping those can cause loss of funds + let channels = self.list_channels().await?; + if channels + .iter() + .any(|c| c.outpoint.is_some_and(|t| t.txid == txid)) + { + return Err(MutinyError::ChannelCreationFailed); + } + + self.wallet.bump_fee(txid, new_fee_rate).await + } + /// Checks if the given address has any transactions. /// If it does, it returns the details of the first transaction. /// @@ -1312,19 +1329,19 @@ impl NodeManager { /// Gets a fee estimate for a very low priority transaction. /// Value is in sat/vbyte. pub fn estimate_fee_low(&self) -> u32 { - self.fee_estimator.get_low_fee_rate() / 250 + max(self.fee_estimator.get_low_fee_rate() / 250, 1) } /// Gets a fee estimate for an average priority transaction. /// Value is in sat/vbyte. pub fn estimate_fee_normal(&self) -> u32 { - self.fee_estimator.get_normal_fee_rate() / 250 + max(self.fee_estimator.get_normal_fee_rate() / 250, 1) } /// Gets a fee estimate for an high priority transaction. /// Value is in sat/vbyte. pub fn estimate_fee_high(&self) -> u32 { - self.fee_estimator.get_high_fee_rate() / 250 + max(self.fee_estimator.get_high_fee_rate() / 250, 1) } /// Creates a new lightning node and adds it to the manager. diff --git a/mutiny-core/src/onchain.rs b/mutiny-core/src/onchain.rs index 9d7ce1f92..2bb637e44 100644 --- a/mutiny-core/src/onchain.rs +++ b/mutiny-core/src/onchain.rs @@ -628,6 +628,27 @@ impl OnChainWallet { psbt.fee_amount().ok_or(MutinyError::WalletOperationFailed) } + + /// Bumps the given transaction by replacing the given tx with a transaction at + /// the new given fee rate in sats/vbyte + pub async fn bump_fee(&self, txid: Txid, new_fee_rate: f32) -> Result { + let tx = { + let mut wallet = self.wallet.try_write()?; + // build RBF fee bump tx + let mut builder = wallet.build_fee_bump(txid)?; + builder.fee_rate(FeeRate::from_sat_per_vb(new_fee_rate)); + let (mut psbt, _) = builder.finish()?; + wallet.sign(&mut psbt, SignOptions::default())?; + + psbt.extract_tx() + }; + + let txid = tx.txid(); + + self.broadcast_transaction(tx).await?; + log_debug!(self.logger, "Fee bump Transaction broadcast! TXID: {txid}"); + Ok(txid) + } } fn get_tr_descriptors_for_extended_key( diff --git a/mutiny-wasm/src/lib.rs b/mutiny-wasm/src/lib.rs index 28576ec88..afb65866d 100644 --- a/mutiny-wasm/src/lib.rs +++ b/mutiny-wasm/src/lib.rs @@ -574,6 +574,15 @@ impl MutinyWallet { .estimate_sweep_channel_open_fee(fee_rate)?) } + /// Bumps the given transaction by replacing the given tx with a transaction at + /// the new given fee rate in sats/vbyte + pub async fn bump_fee(&self, txid: String, fee_rate: f32) -> Result { + let txid = Txid::from_str(&txid)?; + let result = self.inner.node_manager.bump_fee(txid, fee_rate).await?; + + Ok(result.to_string()) + } + /// Checks if the given address has any transactions. /// If it does, it returns the details of the first transaction. /// @@ -625,6 +634,13 @@ impl MutinyWallet { Ok(JsValue::from_serde(&self.inner.node_manager.list_utxos()?)?) } + /// Gets a fee estimate for an low priority transaction. + /// Value is in sat/vbyte. + #[wasm_bindgen] + pub fn estimate_fee_low(&self) -> u32 { + self.inner.node_manager.estimate_fee_low() + } + /// Gets a fee estimate for an average priority transaction. /// Value is in sat/vbyte. #[wasm_bindgen]