Skip to content

Add basic fee estimation to on-chain wallet #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/payment/onchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,63 @@ impl OnchainPayment {
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
}

/// Estimates the fee for sending an on-chain payment to the given address.
///
/// This will respect any on-chain reserve we need to keep, i.e., won't allow to cut into
/// [`BalanceDetails::total_anchor_channels_reserve_sats`].
///
/// If `fee_rate` is set it will be used for estimating the resulting transaction. Otherwise we'll retrieve
/// a reasonable estimate from the configured chain source.
///
/// [`BalanceDetails::total_anchor_channels_reserve_sats`]: crate::BalanceDetails::total_anchor_channels_reserve_sats
pub fn estimate_send_to_address(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, as discussed elsewhere, this is kind of an odd API as it is inherently TOCTOU race-y (based on fee rate updates, but also wallet state changes), and we don't offer any API that would allow to set exactly that net. fee returned here. I think generally the 'staging' approach as described over at #578 would be preferable, but that of course also interacts with UTXO locking, etc.

&self, address: &Address, amount_sats: u64, fee_rate: Option<FeeRate>,
) -> Result<bitcoin::Amount, Error> {
let rt_lock = self.runtime.read().unwrap();
if rt_lock.is_none() {
return Err(Error::NotRunning);
}

let cur_anchor_reserve_sats =
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
let send_amount =
OnchainSendAmount::ExactRetainingReserve { amount_sats, cur_anchor_reserve_sats };
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
self.wallet.estimate_fee(address, send_amount, fee_rate_opt)
}

/// Estimates the fee for sending an on-chain payment to the given address, draining the available funds.
///
/// This is useful if you have closed all channels and want to migrate funds to another
/// on-chain wallet.
///
/// Please note that if `retain_reserves` is set to `false` this will **not** retain any on-chain reserves, which might be potentially
/// dangerous if you have open Anchor channels for which you can't trust the counterparty to
/// spend the Anchor output after channel closure. If `retain_reserves` is set to `true`, this
/// will try to send all spendable onchain funds, i.e.,
/// [`BalanceDetails::spendable_onchain_balance_sats`].
///
/// If `fee_rate` is set it will be used on the resulting transaction. Otherwise a reasonable
/// we'll retrieve an estimate from the configured chain source.
///
/// [`BalanceDetails::spendable_onchain_balance_sats`]: crate::balance::BalanceDetails::spendable_onchain_balance_sats
pub fn estimate_send_all_to_address(
&self, address: &Address, retain_reserves: bool, fee_rate: Option<FeeRate>,
) -> Result<bitcoin::Amount, Error> {
let rt_lock = self.runtime.read().unwrap();
if rt_lock.is_none() {
return Err(Error::NotRunning);
}

let send_amount = if retain_reserves {
let cur_anchor_reserve_sats =
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats }
} else {
OnchainSendAmount::AllDrainingReserve
};
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
self.wallet.estimate_fee(address, send_amount, fee_rate_opt)
}
}