diff --git a/electrum/fee_policy.py b/electrum/fee_policy.py index 90bd8d44f..dd42237b0 100644 --- a/electrum/fee_policy.py +++ b/electrum/fee_policy.py @@ -5,8 +5,7 @@ from enum import IntEnum from .i18n import _ from .util import NoDynamicFeeEstimates, quantize_feerate, format_fee_satoshis -from . import util -from . import constants +from . import util, constants from .logging import Logger if TYPE_CHECKING: @@ -132,7 +131,7 @@ class FeePolicy(Logger): return self.method in [FeeMethod.ETA, FeeMethod.MEMPOOL] @classmethod - def depth_target(self, slider_pos: int) -> int: + def depth_target(cls, slider_pos: int) -> int: """Returns mempool depth target in bytes for a fee slider position.""" slider_pos = max(slider_pos, 0) slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1) @@ -143,7 +142,7 @@ class FeePolicy(Logger): return FEE_ETA_TARGETS[slider_pos] @classmethod - def eta_tooltip(self, x): + def eta_tooltip(cls, x): if x < 0: return _('Low fee') elif x == 1: @@ -191,15 +190,15 @@ class FeePolicy(Logger): return _('Fixed rate') + ': ' + target + '\n' + _('Estimate') + ': ' + estimate @classmethod - def depth_tooltip(self, depth: Optional[int]) -> str: + def depth_tooltip(cls, depth: Optional[int]) -> str: """Returns text tooltip for given mempool depth (in vbytes).""" if depth is None: return "unknown from tip" - depth_mb = self.get_depth_mb_str(depth) + depth_mb = cls.get_depth_mb_str(depth) return _("{} from tip").format(depth_mb) @classmethod - def get_depth_mb_str(self, depth: int) -> str: + def get_depth_mb_str(cls, depth: int) -> str: # e.g. 500_000 -> "0.50 MB" depth_mb = "{:.2f}".format(depth / 1_000_000) # maybe .rstrip("0") ? return f"{depth_mb} {util.UI_UNIT_NAME_MEMPOOL_MB}" @@ -264,9 +263,10 @@ class FeePolicy(Logger): fee_per_byte = quantize_feerate(fee_per_byte) return round(fee_per_byte * size) + class FixedFeePolicy(FeePolicy): def __init__(self, fee): - FeePolicy.__init__(self, 'fixed:%d'%fee) + FeePolicy.__init__(self, 'fixed:%d' % fee) def impose_hard_limits_on_fee(func): diff --git a/electrum/gui/qml/qechannelopener.py b/electrum/gui/qml/qechannelopener.py index f75f42207..6f147442f 100644 --- a/electrum/gui/qml/qechannelopener.py +++ b/electrum/gui/qml/qechannelopener.py @@ -8,11 +8,12 @@ from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.i18n import _ from electrum.gui import messages -from electrum.util import bfh, NotEnoughFunds, NoDynamicFeeEstimates +from electrum.util import bfh from electrum.lntransport import extract_nodeid, ConnStringFormatError from electrum.bitcoin import DummyAddress from electrum.lnworker import hardcoded_trampoline_nodes from electrum.logging import get_logger +from electrum.fee_policy import FeePolicy from .auth import AuthMixin, auth_protect from .qetxfinalizer import QETxFinalizer @@ -169,12 +170,13 @@ class QEChannelOpener(QObject, AuthMixin): self._logger.debug('amount = %s' % str(amount)) coins = self._wallet.wallet.get_spendable_coins(None, nonlocal_only=True) + fee_policy = FeePolicy(self._wallet.wallet.config.FEE_POLICY) mktx = lambda amt: lnworker.mktx_for_open_channel( coins=coins, funding_sat=amt, node_id=self._node_pubkey, - fee_est=None) + fee_policy=fee_policy) acpt = lambda tx: self.do_open_channel(tx, self._connect_str_resolved, self._wallet.password) @@ -244,11 +246,11 @@ class QEChannelOpener(QObject, AuthMixin): try: coins = self._wallet.wallet.get_spendable_coins(None, nonlocal_only=True) dummy_nodeid = ecc.GENERATOR.get_public_key_bytes(compressed=True) - make_tx = lambda amt: self._wallet.wallet.lnworker.mktx_for_open_channel( + make_tx = lambda fee_policy: self._wallet.wallet.lnworker.mktx_for_open_channel( coins=coins, funding_sat='!', node_id=dummy_nodeid, - fee_est=None) + fee_policy=fee_policy) amount, message = self._wallet.determine_max(mktx=make_tx) if amount is None: diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py index 68b6759c4..aa1f267e1 100644 --- a/electrum/gui/qml/qeinvoice.py +++ b/electrum/gui/qml/qeinvoice.py @@ -19,6 +19,7 @@ from electrum.payment_identifier import (PaymentIdentifier, PaymentIdentifierSta from .qetypes import QEAmount from .qewallet import QEWallet from .util import status_update_timer_interval, QtEventListener, event_listener +from ...fee_policy import FeePolicy class QEInvoice(QObject, QtEventListener): @@ -413,10 +414,10 @@ class QEInvoice(QObject, QtEventListener): def calc_max(address): try: outputs = [PartialTxOutput(scriptpubkey=address_to_script(address), value='!')] - make_tx = lambda fee_est, *, confirmed_only=False: self._wallet.wallet.make_unsigned_transaction( + make_tx = lambda fee_policy, *, confirmed_only=False: self._wallet.wallet.make_unsigned_transaction( coins=self._wallet.wallet.get_spendable_coins(None), outputs=outputs, - fee=fee_est, + fee_policy=fee_policy, is_sweep=False) amount, message = self._wallet.determine_max(mktx=make_tx) if amount is None: diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index f79532670..9953c5455 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -26,6 +26,7 @@ from .qeinvoicelistmodel import QEInvoiceListModel, QERequestListModel from .qetransactionlistmodel import QETransactionListModel from .qetypes import QEAmount from .util import QtEventListener, qt_event_listener +from ...fee_policy import FeePolicy if TYPE_CHECKING: from electrum.wallet import Abstract_Wallet @@ -820,16 +821,18 @@ class QEWallet(AuthMixin, QObject, QtEventListener): sig = self.wallet.sign_message(address, message, self.password) return base64.b64encode(sig).decode('ascii') - def determine_max(self, *, mktx: Callable[[Optional[int]], PartialTransaction]) -> Tuple[Optional[int], Optional[str]]: + def determine_max(self, *, mktx: Callable[[FeePolicy], PartialTransaction]) -> Tuple[Optional[int], Optional[str]]: # TODO: merge with SendTab.spend_max() and move to backend wallet amount = message = None try: try: - tx = mktx(None) + fee_policy = FeePolicy(self.wallet.config.FEE_POLICY) + tx = mktx(fee_policy) except (NotEnoughFunds, NoDynamicFeeEstimates) as e: # Check if we had enough funds excluding fees, # if so, still provide opportunity to set lower fees. - tx = mktx(0) + fee_policy = FeePolicy('fixed:0') + tx = mktx(fee_policy) amount = tx.output_value() except NotEnoughFunds as e: self._logger.debug(str(e)) diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 153b86726..951a0835c 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1388,7 +1388,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): def make_tx(fee_policy, *, confirmed_only=False, base_tx=None): assert base_tx is None return self.wallet.lnworker.mktx_for_open_channel( - coins = self.get_coins(nonlocal_only=True, confirmed_only=confirmed_only), + coins=self.get_coins(nonlocal_only=True, confirmed_only=confirmed_only), funding_sat=funding_sat, node_id=node_id, fee_policy=fee_policy) diff --git a/electrum/gui/qt/new_channel_dialog.py b/electrum/gui/qt/new_channel_dialog.py index 9622fc755..db8fd7809 100644 --- a/electrum/gui/qt/new_channel_dialog.py +++ b/electrum/gui/qt/new_channel_dialog.py @@ -1,5 +1,5 @@ from typing import TYPE_CHECKING, Optional -from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton, QComboBox, QLineEdit, QSpacerItem, QWidget, QHBoxLayout +from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton, QComboBox, QLineEdit, QHBoxLayout import electrum_ecc as ecc @@ -7,6 +7,7 @@ from electrum.i18n import _ from electrum.lnutil import MIN_FUNDING_SAT from electrum.lnworker import hardcoded_trampoline_nodes from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates +from electrum.fee_policy import FeePolicy from .util import (WindowModalDialog, Buttons, OkButton, CancelButton, EnterButton, WWLabel, char_width_in_lineedit) @@ -119,7 +120,7 @@ class NewChannelDialog(WindowModalDialog): dummy_nodeid = ecc.GENERATOR.get_public_key_bytes(compressed=True) make_tx = self.window.mktx_for_open_channel(funding_sat='!', node_id=dummy_nodeid) try: - tx = make_tx(None) + tx = make_tx(FeePolicy(self.config.FEE_POLICY)) except (NotEnoughFunds, NoDynamicFeeEstimates) as e: self.max_button.setChecked(False) self.amount_e.setFrozen(False) diff --git a/electrum/gui/qt/send_tab.py b/electrum/gui/qt/send_tab.py index 4b6064ffc..1fe77717f 100644 --- a/electrum/gui/qt/send_tab.py +++ b/electrum/gui/qt/send_tab.py @@ -5,10 +5,9 @@ from decimal import Decimal from typing import Optional, TYPE_CHECKING, Sequence, List, Callable, Union, Mapping -from PyQt6.QtCore import pyqtSignal, QPoint, QSize, Qt +from PyQt6.QtCore import pyqtSignal, QPoint, Qt from PyQt6.QtWidgets import (QLabel, QVBoxLayout, QGridLayout, QHBoxLayout, QWidget, QToolTip, QPushButton, QApplication) -from PyQt6.QtGui import QMovie, QColor from electrum.i18n import _ from electrum.logging import Logger @@ -21,11 +20,12 @@ from electrum.network import TxBroadcastError, BestEffortRequestFailed from electrum.payment_identifier import (PaymentIdentifierType, PaymentIdentifier, invoice_from_payment_identifier, payment_identifier_from_invoice) from electrum.submarine_swaps import SwapServerError +from electrum.fee_policy import FeePolicy from .amountedit import AmountEdit, BTCAmountEdit, SizedFreezableLineEdit from .paytoedit import InvalidPaymentIdentifier from .util import (WaitingDialog, HelpLabel, MessageBoxMixin, EnterButton, char_width_in_lineedit, - get_iconname_camera, read_QIcon, ColorScheme, icon_path, IconLabel, Spinner) + get_iconname_camera, read_QIcon, ColorScheme, IconLabel, Spinner) from .invoice_list import InvoiceList if TYPE_CHECKING: @@ -260,11 +260,11 @@ class SendTab(QWidget, MessageBoxMixin, Logger): is_sweep=False) try: try: - tx = make_tx(None) + tx = make_tx(FeePolicy(self.config.FEE_POLICY)) except (NotEnoughFunds, NoDynamicFeeEstimates) as e: # Check if we had enough funds excluding fees, # if so, still provide opportunity to set lower fees. - tx = make_tx(0) + tx = make_tx(FeePolicy('fixed:0')) except NotEnoughFunds as e: self.max_button.setChecked(False) text = self.wallet.get_text_not_enough_funds_mentioning_frozen()