From ba670cc9dcd99b32f8a484b959af78eeab8b62c4 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sun, 29 Jun 2025 22:02:09 +0000 Subject: [PATCH] wallet: add struct PiechartBalance --- electrum/gui/qml/qewallet.py | 16 ++++++++-------- electrum/gui/qt/balance_dialog.py | 8 +++++++- electrum/gui/qt/main_window.py | 20 +++++++++---------- electrum/gui/stdio.py | 6 +++--- electrum/gui/text.py | 2 +- electrum/lnworker.py | 2 +- electrum/wallet.py | 32 ++++++++++++++++++++++++++++--- 7 files changed, 59 insertions(+), 27 deletions(-) diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 26f06dc49..5acdba0aa 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -832,15 +832,15 @@ class QEWallet(AuthMixin, QObject, QtEventListener): @pyqtSlot(result='QVariantMap') def getBalancesForPiechart(self): - confirmed, unconfirmed, unmatured, frozen, lightning, f_lightning = balances = self.wallet.get_balances_for_piechart() + p_bal = self.wallet.get_balances_for_piechart() return { - 'confirmed': confirmed, - 'unconfirmed': unconfirmed, - 'unmatured': unmatured, - 'frozen': frozen, - 'lightning': int(lightning), - 'f_lightning': int(f_lightning), - 'total': sum([int(x) for x in list(balances)]) + 'confirmed': p_bal.confirmed, + 'unconfirmed': p_bal.unconfirmed, + 'unmatured': p_bal.unmatured, + 'frozen': p_bal.frozen, + 'lightning': int(p_bal.lightning), + 'f_lightning': int(p_bal.lightning_frozen), + 'total': int(p_bal.total()) } @pyqtSlot(str, result=bool) diff --git a/electrum/gui/qt/balance_dialog.py b/electrum/gui/qt/balance_dialog.py index b7d2a54ff..f3aa118f4 100644 --- a/electrum/gui/qt/balance_dialog.py +++ b/electrum/gui/qt/balance_dialog.py @@ -168,7 +168,13 @@ class BalanceDialog(WindowModalDialog): self.config = parent.config self.fx = parent.fx - confirmed, unconfirmed, unmatured, frozen, lightning, f_lightning = self.wallet.get_balances_for_piechart() + p_bal = self.wallet.get_balances_for_piechart() + confirmed = p_bal.confirmed + unconfirmed = p_bal.unconfirmed + unmatured = p_bal.unmatured + frozen = p_bal.frozen + lightning = p_bal.lightning + f_lightning = p_bal.lightning_frozen frozen_str = self.config.format_amount_and_units(frozen) confirmed_str = self.config.format_amount_and_units(confirmed) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index d517150df..15a4fa91a 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1066,19 +1066,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): icon = read_QIcon("status_lagging%s.png"%fork_str) else: network_text = _("Connected") - confirmed, unconfirmed, unmatured, frozen, lightning, f_lightning = self.wallet.get_balances_for_piechart() + p_bal = self.wallet.get_balances_for_piechart() self.balance_label.update_list( [ - (_('Frozen'), COLOR_FROZEN, frozen), - (_('Unmatured'), COLOR_UNMATURED, unmatured), - (_('Unconfirmed'), COLOR_UNCONFIRMED, unconfirmed), - (_('On-chain'), COLOR_CONFIRMED, confirmed), - (_('Lightning'), COLOR_LIGHTNING, lightning), - (_('Lightning frozen'), COLOR_FROZEN_LIGHTNING, f_lightning), + (_('Frozen'), COLOR_FROZEN, p_bal.frozen), + (_('Unmatured'), COLOR_UNMATURED, p_bal.unmatured), + (_('Unconfirmed'), COLOR_UNCONFIRMED, p_bal.unconfirmed), + (_('On-chain'), COLOR_CONFIRMED, p_bal.confirmed), + (_('Lightning'), COLOR_LIGHTNING, p_bal.lightning), + (_('Lightning frozen'), COLOR_FROZEN_LIGHTNING, p_bal.lightning_frozen), ], warning = self.wallet.is_low_reserve(), ) - balance = confirmed + unconfirmed + unmatured + frozen + lightning + f_lightning + balance = p_bal.total() balance_text = _("Balance") + ": %s "%(self.format_amount_and_units(balance)) # append fiat balance and price if self.fx.is_enabled(): @@ -1787,7 +1787,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): console.updateNamespace(methods) def show_balance_dialog(self): - balance = sum(self.wallet.get_balances_for_piechart()) + balance = self.wallet.get_balances_for_piechart().total() if balance == 0: return from .balance_dialog import BalanceDialog @@ -1981,7 +1981,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): def new_channel_dialog(self, *, amount_sat=None, min_amount_sat=None): from electrum.lnutil import MIN_FUNDING_SAT from .new_channel_dialog import NewChannelDialog - confirmed, unconfirmed, unmatured, frozen, lightning, f_lightning = self.wallet.get_balances_for_piechart() + confirmed = self.wallet.get_spendable_balance_sat(confirmed_only=True) min_amount_sat = min_amount_sat or MIN_FUNDING_SAT if confirmed < min_amount_sat: msg = _('Not enough funds') + '\n\n' + _('You need at least {} to open a channel.').format(self.format_amount_and_units(min_amount_sat)) diff --git a/electrum/gui/stdio.py b/electrum/gui/stdio.py index 6b188e4d9..9d5794f35 100644 --- a/electrum/gui/stdio.py +++ b/electrum/gui/stdio.py @@ -133,11 +133,11 @@ class ElectrumGui(BaseElectrumGui, EventListener): msg = _("Synchronizing...") else: c, u, x = self.wallet.get_balance() - msg = _("Balance")+": %f "%(Decimal(c) / COIN) + msg = _("Balance")+": {} ".format(Decimal(c) / COIN) if u: - msg += " [%f unconfirmed]"%(Decimal(u) / COIN) + msg += " [{} unconfirmed]".format(Decimal(u) / COIN) if x: - msg += " [%f unmatured]"%(Decimal(x) / COIN) + msg += " [{} unmatured]".format(Decimal(x) / COIN) else: msg = _("Not connected") diff --git a/electrum/gui/text.py b/electrum/gui/text.py index 52b388970..64170346b 100644 --- a/electrum/gui/text.py +++ b/electrum/gui/text.py @@ -212,7 +212,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): if not self.wallet.is_up_to_date(): msg = _("Synchronizing...") else: - balance = sum(self.wallet.get_balances_for_piechart()) + balance = self.wallet.get_balances_for_piechart().total() msg = _("Balance") + ': ' + self.config.format_amount_and_units(balance) else: msg = _("Not connected") diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 2b55a3245..fb03f6742 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -2717,7 +2717,7 @@ class LNWallet(LNWorker): with self.lock: self.payment_info.pop(payment_hash_hex, None) - def get_balance(self, frozen=False): + def get_balance(self, *, frozen=False) -> Decimal: with self.lock: return Decimal(sum( chan.balance(LOCAL) if not chan.is_closed() and (chan.is_frozen_for_sending() if frozen else True) else 0 diff --git a/electrum/wallet.py b/electrum/wallet.py index 57dbe600a..6743f47cf 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -40,6 +40,7 @@ import itertools import threading import enum import asyncio +from dataclasses import dataclass import electrum_ecc as ecc from aiorpcx import ignore_after, run_in_thread @@ -370,6 +371,19 @@ class TxWalletDetails(NamedTuple): is_related_to_wallet: bool +@dataclass(kw_only=True, slots=True, frozen=True) +class PiechartBalance: + confirmed: int # confirmed and matured and NOT frozen + unconfirmed: int # unconfirmed and NOT frozen + unmatured: int # unmatured and NOT frozen + frozen: int # on-chain + lightning: Decimal + lightning_frozen: Decimal + + def total(self) -> Decimal: + return self.confirmed + self.unconfirmed + self.unmatured + self.frozen + self.lightning + self.lightning_frozen + + class Abstract_Wallet(ABC, Logger, EventListener): """ Wallet classes are created to handle various address generation methods. @@ -1063,6 +1077,9 @@ class Abstract_Wallet(ABC, Logger, EventListener): return result def get_balance(self, **kwargs): + """Note: intended for display-purposes. + Do not use for NotEnoughFunds checks. Use get_spendable_balance_sat() instead. + """ domain = self.get_addresses() return self.adb.get_balance(domain, **kwargs) @@ -1144,9 +1161,11 @@ class Abstract_Wallet(ABC, Logger, EventListener): ) return c1-c2, u1-u2, x1-x2 - def get_balances_for_piechart(self): + def get_balances_for_piechart(self) -> PiechartBalance: + """Note: intended for display-purposes. + Do not use for NotEnoughFunds checks. Use get_spendable_balance_sat() instead. + """ # return only positive values - # todo: add lightning frozen c, u, x = self.get_balance() fc, fu, fx = self.get_frozen_balance() lightning = self.lnworker.get_balance() if self.has_lightning() else 0 @@ -1156,7 +1175,14 @@ class Abstract_Wallet(ABC, Logger, EventListener): uu = u - fu xx = x - fx frozen = fc + fu + fx - return cc, uu, xx, frozen, lightning - f_lightning, f_lightning + return PiechartBalance( + confirmed=cc, + unconfirmed=uu, + unmatured=xx, + frozen=frozen, + lightning=lightning - f_lightning, + lightning_frozen=f_lightning, + ) def balance_at_timestamp(self, domain, target_timestamp): # we assume that get_history returns items ordered by block height