URI: 
       ttx dialog: uniform high fee warnings between GUIs - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f9f49daad7a9eee80abccd49ce43bee07258fd99
   DIR parent 84326cf1f78f60201e5bac44ff306c240147adde
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Fri, 26 Feb 2021 19:02:24 +0100
       
       ttx dialog: uniform high fee warnings between GUIs
       
       Diffstat:
         M electrum/gui/kivy/uix/dialogs/conf… |      23 ++++++++++++++---------
         M electrum/gui/qt/confirm_tx_dialog.… |      23 +++++++----------------
         M electrum/gui/qt/main_window.py      |      29 ++++++++++++++++-------------
         M electrum/gui/qt/transaction_dialog… |      41 ++++++++++++++++++++++++-------
         M electrum/wallet.py                  |      36 ++++++++++++++++++++++++++++++-
       
       5 files changed, 104 insertions(+), 48 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py b/electrum/gui/kivy/uix/dialogs/confirm_tx_dialog.py
       t@@ -1,3 +1,6 @@
       +from decimal import Decimal
       +from typing import TYPE_CHECKING
       +
        from kivy.app import App
        from kivy.factory import Factory
        from kivy.properties import ObjectProperty
       t@@ -7,8 +10,6 @@ from kivy.uix.label import Label
        from kivy.uix.widget import Widget
        from kivy.clock import Clock
        
       -from decimal import Decimal
       -
        from electrum.simple_config import FEERATE_WARNING_HIGH_FEE, FEE_RATIO_HIGH_WARNING
        from electrum.gui.kivy.i18n import _
        from electrum.plugin import run_hook
       t@@ -16,6 +17,9 @@ from electrum.util import NotEnoughFunds
        
        from .fee_dialog import FeeSliderDialog, FeeDialog
        
       +if TYPE_CHECKING:
       +    from electrum.gui.kivy.main_window import ElectrumWindow
       +
        Builder.load_string('''
        <ConfirmTxDialog@Popup>
            id: popup
       t@@ -106,7 +110,7 @@ Builder.load_string('''
        
        class ConfirmTxDialog(FeeSliderDialog, Factory.Popup):
        
       -    def __init__(self, app, invoice):
       +    def __init__(self, app: 'ElectrumWindow', invoice):
        
                Factory.Popup.__init__(self)
                FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider)
       t@@ -133,8 +137,9 @@ class ConfirmTxDialog(FeeSliderDialog, Factory.Popup):
                rbf = not bool(self.ids.final_cb.active) if self.show_final else False
                tx.set_rbf(rbf)
                amount = sum(map(lambda x: x.value, outputs)) if '!' not in [x.value for x in outputs] else tx.output_value()
       +        tx_size = tx.estimated_size()
                fee = tx.get_fee()
       -        feerate = Decimal(fee) / tx.estimated_size()  # sat/byte
       +        feerate = Decimal(fee) / tx_size  # sat/byte
                self.ids.fee_label.text = self.app.format_amount_and_units(fee) + f' ({feerate:.1f} sat/B)'
                self.ids.amount_label.text = self.app.format_amount_and_units(amount)
                x_fee = run_hook('get_tx_extra_fee', self.app.wallet, tx)
       t@@ -143,11 +148,11 @@ class ConfirmTxDialog(FeeSliderDialog, Factory.Popup):
                    self.extra_fee = self.app.format_amount_and_units(x_fee_amount)
                else:
                    self.extra_fee = ''
       -        fee_ratio = Decimal(fee) / amount if amount else 1
       -        if fee_ratio >= FEE_RATIO_HIGH_WARNING:
       -            self.warning = _('Warning') + ': ' + _("The fee for this transaction seems unusually high.") + f' ({fee_ratio*100:.2f}% of amount)'
       -        elif feerate > FEERATE_WARNING_HIGH_FEE / 1000:
       -            self.warning = _('Warning') + ': ' + _("The fee for this transaction seems unusually high.") + f' (feerate: {feerate:.2f} sat/byte)'
       +        fee_warning_tuple = self.app.wallet.get_tx_fee_warning(
       +            invoice_amt=amount, tx_size=tx_size, fee=fee)
       +        if fee_warning_tuple:
       +            allow_send, long_warning, short_warning = fee_warning_tuple
       +            self.warning = long_warning
                else:
                    self.warning = ''
                self.tx = tx
   DIR diff --git a/electrum/gui/qt/confirm_tx_dialog.py b/electrum/gui/qt/confirm_tx_dialog.py
       t@@ -239,6 +239,7 @@ class ConfirmTxDialog(TxEditor, WindowModalDialog):
                    return
        
                fee = tx.get_fee()
       +        assert fee is not None
                self.fee_label.setText(self.main_window.format_amount_and_units(fee))
                x_fee = run_hook('get_tx_extra_fee', self.wallet, tx)
                if x_fee:
       t@@ -248,21 +249,11 @@ class ConfirmTxDialog(TxEditor, WindowModalDialog):
                    self.extra_fee_value.setText(self.main_window.format_amount_and_units(x_fee_amount))
        
                amount = tx.output_value() if self.output_value == '!' else self.output_value
       -        feerate = Decimal(fee) / tx.estimated_size()  # sat/byte
       -        fee_ratio = Decimal(fee) / amount if amount else 1
       -        if feerate < self.wallet.relayfee() / 1000:
       -            msg = '\n'.join([
       -                _("This transaction requires a higher fee, or it will not be propagated by your current server"),
       -                _("Try to raise your transaction fee, or use a server with a lower relay fee.")
       -            ])
       -            self.toggle_send_button(False, message=msg)
       -        elif fee_ratio >= FEE_RATIO_HIGH_WARNING:
       -            self.toggle_send_button(True,
       -                                    message=_('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
       -                                            + f'\n({fee_ratio*100:.2f}% of amount)')
       -        elif feerate > FEERATE_WARNING_HIGH_FEE / 1000:
       -            self.toggle_send_button(True,
       -                                    message=_('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
       -                                            + f'\n(feerate: {feerate:.2f} sat/byte)')
       +        tx_size = tx.estimated_size()
       +        fee_warning_tuple = self.wallet.get_tx_fee_warning(
       +            invoice_amt=amount, tx_size=tx_size, fee=fee)
       +        if fee_warning_tuple:
       +            allow_send, long_warning, short_warning = fee_warning_tuple
       +            self.toggle_send_button(allow_send, message=long_warning)
                else:
                    self.toggle_send_button(True)
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -1670,22 +1670,26 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                    return
        
                output_value = '!' if '!' in output_values else sum(output_values)
       -        d = ConfirmTxDialog(window=self, make_tx=make_tx, output_value=output_value, is_sweep=is_sweep)
       -        if d.not_enough_funds:
       +        conf_dlg = ConfirmTxDialog(window=self, make_tx=make_tx, output_value=output_value, is_sweep=is_sweep)
       +        if conf_dlg.not_enough_funds:
                    # Check if we had enough funds excluding fees,
                    # if so, still provide opportunity to set lower fees.
       -            if not d.have_enough_funds_assuming_zero_fees():
       +            if not conf_dlg.have_enough_funds_assuming_zero_fees():
                        text = self.get_text_not_enough_funds_mentioning_frozen()
                        self.show_message(text)
                        return
        
                # shortcut to advanced preview (after "enough funds" check!)
                if self.config.get('advanced_preview'):
       -            self.preview_tx_dialog(make_tx=make_tx,
       -                                   external_keypairs=external_keypairs)
       +            preview_dlg = PreviewTxDialog(
       +                window=self,
       +                make_tx=make_tx,
       +                external_keypairs=external_keypairs,
       +                output_value=output_value)
       +            preview_dlg.show()
                    return
        
       -        cancelled, is_send, password, tx = d.run()
       +        cancelled, is_send, password, tx = conf_dlg.run()
                if cancelled:
                    return
                if is_send:
       t@@ -1696,13 +1700,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                    self.sign_tx_with_password(tx, callback=sign_done, password=password,
                                               external_keypairs=external_keypairs)
                else:
       -            self.preview_tx_dialog(make_tx=make_tx,
       -                                   external_keypairs=external_keypairs)
       -
       -    def preview_tx_dialog(self, *, make_tx, external_keypairs=None):
       -        d = PreviewTxDialog(make_tx=make_tx, external_keypairs=external_keypairs,
       -                            window=self)
       -        d.show()
       +            preview_dlg = PreviewTxDialog(
       +                window=self,
       +                make_tx=make_tx,
       +                external_keypairs=external_keypairs,
       +                output_value=output_value)
       +            preview_dlg.show()
        
            def broadcast_or_show(self, tx: Transaction):
                if not tx.is_complete():
   DIR diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py
       t@@ -28,7 +28,7 @@ import copy
        import datetime
        import traceback
        import time
       -from typing import TYPE_CHECKING, Callable, Optional, List
       +from typing import TYPE_CHECKING, Callable, Optional, List, Union
        from functools import partial
        from decimal import Decimal
        
       t@@ -502,11 +502,22 @@ class BaseTxDialog(QDialog, MessageBoxMixin):
                size_str = _("Size:") + ' %d bytes'% size
                fee_str = _("Fee") + ': %s' % (format_amount(fee) + ' ' + base_unit if fee is not None else _('unknown'))
                if fee is not None:
       -            fee_rate = fee/size*1000
       -            fee_str += '  ( %s ) ' % self.main_window.format_fee_rate(fee_rate)
       -            feerate_warning = simple_config.FEERATE_WARNING_HIGH_FEE
       -            if fee_rate > feerate_warning:
       -                fee_str += ' - ' + _('Warning') + ': ' + _("high fee") + '!'
       +            fee_rate = Decimal(fee) / size  # sat/byte
       +            fee_str += '  ( %s ) ' % self.main_window.format_fee_rate(fee_rate * 1000)
       +            if isinstance(self.tx, PartialTransaction):
       +                if isinstance(self, PreviewTxDialog):
       +                    invoice_amt = self.tx.output_value() if self.output_value == '!' else self.output_value
       +                else:
       +                    invoice_amt = amount
       +                fee_warning_tuple = self.wallet.get_tx_fee_warning(
       +                    invoice_amt=invoice_amt, tx_size=size, fee=fee)
       +                if fee_warning_tuple:
       +                    allow_send, long_warning, short_warning = fee_warning_tuple
       +                    fee_str += " - <font color={color}>{header}: {body}</font>".format(
       +                        header=_('Warning'),
       +                        body=short_warning,
       +                        color=ColorScheme.RED.as_color().name(),
       +                    )
                if isinstance(self.tx, PartialTransaction):
                    risk_of_burning_coins = (can_sign and fee is not None
                                             and self.wallet.get_warning_for_risk_of_burning_coins_as_fees(self.tx))
       t@@ -742,11 +753,23 @@ class TxDialog(BaseTxDialog):
                self.update()
        
        
       -
        class PreviewTxDialog(BaseTxDialog, TxEditor):
        
       -    def __init__(self, *, make_tx, external_keypairs, window: 'ElectrumWindow'):
       -        TxEditor.__init__(self, window=window, make_tx=make_tx, is_sweep=bool(external_keypairs))
       +    def __init__(
       +            self,
       +            *,
       +            make_tx,
       +            external_keypairs,
       +            window: 'ElectrumWindow',
       +            output_value: Union[int, str],
       +    ):
       +        TxEditor.__init__(
       +            self,
       +            window=window,
       +            make_tx=make_tx,
       +            is_sweep=bool(external_keypairs),
       +            output_value=output_value,
       +        )
                BaseTxDialog.__init__(self, parent=window, desc='', prompt_if_unsaved=False,
                                      finalized=False, external_keypairs=external_keypairs)
                BlockingWaitingDialog(window, _("Preparing transaction..."),
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -58,7 +58,7 @@ from .util import (NotEnoughFunds, UserCancelled, profiler,
                           InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
                           Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex)
        from .util import get_backup_dir
       -from .simple_config import SimpleConfig
       +from .simple_config import SimpleConfig, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
        from .bitcoin import COIN, TYPE_ADDRESS
        from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold
        from .crypto import sha256d
       t@@ -2451,6 +2451,40 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                            "do not accept to sign it more than once,\n"
                            "otherwise you could end up paying a different fee."))
        
       +    def get_tx_fee_warning(
       +            self,
       +            *,
       +            invoice_amt: int,
       +            tx_size: int,
       +            fee: int,
       +    ) -> Optional[Tuple[bool, str, str]]:
       +        feerate = Decimal(fee) / tx_size  # sat/byte
       +        fee_ratio = Decimal(fee) / invoice_amt if invoice_amt else 1
       +        long_warning = None
       +        short_warning = None
       +        allow_send = True
       +        if feerate < self.relayfee() / 1000:
       +            long_warning = (
       +                    _("This transaction requires a higher fee, or it will not be propagated by your current server") + "\n"
       +                    + _("Try to raise your transaction fee, or use a server with a lower relay fee.")
       +            )
       +            short_warning = _("below relay fee") + "!"
       +            allow_send = False
       +        elif fee_ratio >= FEE_RATIO_HIGH_WARNING:
       +            long_warning = (
       +                    _('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
       +                    + f'\n({fee_ratio*100:.2f}% of amount)')
       +            short_warning = _("high fee ratio") + "!"
       +        elif feerate > FEERATE_WARNING_HIGH_FEE / 1000:
       +            long_warning = (
       +                    _('Warning') + ': ' + _("The fee for this transaction seems unusually high.")
       +                    + f'\n(feerate: {feerate:.2f} sat/byte)')
       +            short_warning = _("high fee rate") + "!"
       +        if long_warning is None:
       +            return None
       +        else:
       +            return allow_send, long_warning, short_warning
       +
        
        class Simple_Wallet(Abstract_Wallet):
            # wallet with a single keystore