electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

confirm_tx_dialog.py (5800B)


      1 from decimal import Decimal
      2 from typing import TYPE_CHECKING
      3 
      4 from kivy.app import App
      5 from kivy.factory import Factory
      6 from kivy.properties import ObjectProperty
      7 from kivy.lang import Builder
      8 from kivy.uix.checkbox import CheckBox
      9 from kivy.uix.label import Label
     10 from kivy.uix.widget import Widget
     11 from kivy.clock import Clock
     12 
     13 from electrum.simple_config import FEERATE_WARNING_HIGH_FEE, FEE_RATIO_HIGH_WARNING
     14 from electrum.gui.kivy.i18n import _
     15 from electrum.plugin import run_hook
     16 from electrum.util import NotEnoughFunds
     17 
     18 from .fee_dialog import FeeSliderDialog, FeeDialog
     19 
     20 if TYPE_CHECKING:
     21     from electrum.gui.kivy.main_window import ElectrumWindow
     22 
     23 Builder.load_string('''
     24 <ConfirmTxDialog@Popup>
     25     id: popup
     26     title: _('Confirm Payment')
     27     message: ''
     28     warning: ''
     29     extra_fee: ''
     30     show_final: False
     31     size_hint: 0.8, 0.8
     32     pos_hint: {'top':0.9}
     33     BoxLayout:
     34         orientation: 'vertical'
     35         BoxLayout:
     36             orientation: 'horizontal'
     37             size_hint: 1, 0.5
     38             Label:
     39                 text: _('Amount to be sent:')
     40             Label:
     41                 id: amount_label
     42                 text: ''
     43         BoxLayout:
     44             orientation: 'horizontal'
     45             size_hint: 1, 0.5
     46             Label:
     47                 text: _('Mining fee:')
     48             Label:
     49                 id: fee_label
     50                 text: ''
     51         BoxLayout:
     52             orientation: 'horizontal'
     53             size_hint: 1, (0.5 if root.extra_fee else 0.01)
     54             Label:
     55                 text: _('Additional fees') if root.extra_fee else ''
     56             Label:
     57                 text: root.extra_fee
     58         BoxLayout:
     59             orientation: 'horizontal'
     60             size_hint: 1, 0.5
     61             Label:
     62                 text: _('Fee target:')
     63             Button:
     64                 id: fee_button
     65                 text: ''
     66                 background_color: (0,0,0,0)
     67                 bold: True
     68                 on_release:
     69                     root.on_fee_button()
     70         Slider:
     71             id: slider
     72             range: 0, 4
     73             step: 1
     74             on_value: root.on_slider(self.value)
     75         BoxLayout:
     76             orientation: 'horizontal'
     77             size_hint: 1, 0.2
     78             Label:
     79                 text: _('Final')
     80                 opacity: int(root.show_final)
     81             CheckBox:
     82                 id: final_cb
     83                 opacity: int(root.show_final)
     84                 disabled: not root.show_final
     85         Label:
     86             text: root.warning
     87             text_size: self.width, None
     88         Widget:
     89             size_hint: 1, 0.5
     90         BoxLayout:
     91             orientation: 'horizontal'
     92             size_hint: 1, 0.5
     93             Button:
     94                 text: _('Cancel')
     95                 size_hint: 0.5, None
     96                 height: '48dp'
     97                 on_release:
     98                     popup.dismiss()
     99             Button:
    100                 text: _('OK')
    101                 size_hint: 0.5, None
    102                 height: '48dp'
    103                 on_release:
    104                     root.pay()
    105                     popup.dismiss()
    106 ''')
    107 
    108 
    109 
    110 
    111 class ConfirmTxDialog(FeeSliderDialog, Factory.Popup):
    112 
    113     def __init__(self, app: 'ElectrumWindow', invoice):
    114 
    115         Factory.Popup.__init__(self)
    116         FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider)
    117         self.app = app
    118         self.show_final = bool(self.config.get('use_rbf'))
    119         self.invoice = invoice
    120         self.update_slider()
    121         self.update_text()
    122         self.update_tx()
    123 
    124     def update_tx(self):
    125         outputs = self.invoice.outputs
    126         try:
    127             # make unsigned transaction
    128             coins = self.app.wallet.get_spendable_coins(None)
    129             tx = self.app.wallet.make_unsigned_transaction(coins=coins, outputs=outputs)
    130         except NotEnoughFunds:
    131             self.warning = _("Not enough funds")
    132             return
    133         except Exception as e:
    134             self.logger.exception('')
    135             self.app.show_error(repr(e))
    136             return
    137         rbf = not bool(self.ids.final_cb.active) if self.show_final else False
    138         tx.set_rbf(rbf)
    139         amount = sum(map(lambda x: x.value, outputs)) if '!' not in [x.value for x in outputs] else tx.output_value()
    140         tx_size = tx.estimated_size()
    141         fee = tx.get_fee()
    142         feerate = Decimal(fee) / tx_size  # sat/byte
    143         self.ids.fee_label.text = self.app.format_amount_and_units(fee) + f' ({feerate:.1f} sat/B)'
    144         self.ids.amount_label.text = self.app.format_amount_and_units(amount)
    145         x_fee = run_hook('get_tx_extra_fee', self.app.wallet, tx)
    146         if x_fee:
    147             x_fee_address, x_fee_amount = x_fee
    148             self.extra_fee = self.app.format_amount_and_units(x_fee_amount)
    149         else:
    150             self.extra_fee = ''
    151         fee_warning_tuple = self.app.wallet.get_tx_fee_warning(
    152             invoice_amt=amount, tx_size=tx_size, fee=fee)
    153         if fee_warning_tuple:
    154             allow_send, long_warning, short_warning = fee_warning_tuple
    155             self.warning = long_warning
    156         else:
    157             self.warning = ''
    158         self.tx = tx
    159 
    160     def on_slider(self, value):
    161         self.save_config()
    162         self.update_text()
    163         Clock.schedule_once(lambda dt: self.update_tx())
    164 
    165     def update_text(self):
    166         target, tooltip, dyn = self.config.get_fee_target()
    167         self.ids.fee_button.text = target
    168 
    169     def pay(self):
    170         self.app.protected(_('Send payment?'), self.app.send_screen.send_tx, (self.tx, self.invoice))
    171 
    172     def on_fee_button(self):
    173         fee_dialog = FeeDialog(self, self.config, self.after_fee_changed)
    174         fee_dialog.open()
    175 
    176     def after_fee_changed(self):
    177         self.read_config()
    178         self.update_slider()
    179         self.update_text()
    180         Clock.schedule_once(lambda dt: self.update_tx())