From 30b146033d273223ca13ba653f040ce856052205 Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 29 Jul 2025 14:36:55 +0200 Subject: [PATCH] gui: detect if reserve inputs have been removed from tx Adapts the gui(s) to detect if an existing reserve input has not been used as input for the transaction even though the user tried to spend max. This allows to show the lightning reserve warning not only for reserve change outputs but also for existing reserve inputs that have been ignored for this max spend transaction. Still keeps the `is_utxo_reserve` flag on `PartialTxOutput` as it is used in the qml gui. --- electrum/gui/qml/qetxfinalizer.py | 5 ++++- electrum/gui/qt/confirm_tx_dialog.py | 2 +- electrum/wallet.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/electrum/gui/qml/qetxfinalizer.py b/electrum/gui/qml/qetxfinalizer.py index bafe4a6c4..1b378d608 100644 --- a/electrum/gui/qml/qetxfinalizer.py +++ b/electrum/gui/qml/qetxfinalizer.py @@ -434,7 +434,10 @@ class QETxFinalizer(TxFeeSlider): self.update_fee_warning_from_tx(tx=tx, invoice_amt=amount) if self._amount.isMax and not self.warning: - if reserve_sats := sum(txo.value for txo in tx.outputs() if txo.is_utxo_reserve): + if reserve_sats := self._wallet.wallet.tx_keeps_ln_utxo_reserve( + tx, + gui_spend_max=self._amount.isMax + ): reserve_str = self._config.format_amount_and_units(reserve_sats) self.warning = ' '.join([ _('Warning') + ':', diff --git a/electrum/gui/qt/confirm_tx_dialog.py b/electrum/gui/qt/confirm_tx_dialog.py index b18f8332b..6ab873331 100644 --- a/electrum/gui/qt/confirm_tx_dialog.py +++ b/electrum/gui/qt/confirm_tx_dialog.py @@ -544,7 +544,7 @@ class TxEditor(WindowModalDialog): if any((txin.block_height is not None and txin.block_height<=0) for txin in self.tx.inputs()): messages.append(_('This transaction will spend unconfirmed coins.')) # warn if a reserve utxo was added - if reserve_sats := sum(txo.value for txo in self.tx.outputs() if txo.is_utxo_reserve): + if reserve_sats := self.wallet.tx_keeps_ln_utxo_reserve(self.tx, gui_spend_max=bool(self.output_value == '!')): reserve_str = self.main_window.config.format_amount_and_units(reserve_sats) messages.append(_('Could not spend max: a security reserve of {} was kept for your Lightning channels.').format(reserve_str)) # warn if we merge from mempool diff --git a/electrum/wallet.py b/electrum/wallet.py index 37c05e742..efdcf03e9 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -1911,6 +1911,19 @@ class Abstract_Wallet(ABC, Logger, EventListener): def is_low_reserve(self) -> bool: return self.should_keep_reserve_utxo([], [], False) + def tx_keeps_ln_utxo_reserve(self, tx, *, gui_spend_max: bool) -> Optional[int]: + if reserve_output_amount := sum(txo.value for txo in tx.outputs() if txo.is_utxo_reserve): + # tx has a reserve change output + return reserve_output_amount + if gui_spend_max: # user tried to spend max amount + coins_in_wallet = self.get_spendable_coins(nonlocal_only=False, confirmed_only=False) + amount_in_wallet = sum(c.value_sats() for c in coins_in_wallet) + tx_spend_amount = tx.output_value() + tx.get_fee() + if amount_in_wallet - tx_spend_amount == self.config.LN_UTXO_RESERVE: + # tx keeps exactly LN_UTXO_RESERVE amount sats in the wallet + return self.config.LN_UTXO_RESERVE + return None + @profiler(min_threshold=0.1) def make_unsigned_transaction( self, *,