2 changed files with 191 additions and 1 deletions
@ -0,0 +1,153 @@
|
||||
from typing import TYPE_CHECKING, Optional |
||||
|
||||
from kivy.app import App |
||||
from kivy.factory import Factory |
||||
from kivy.properties import ObjectProperty |
||||
from kivy.lang import Builder |
||||
|
||||
from electrum.gui.kivy.i18n import _ |
||||
|
||||
if TYPE_CHECKING: |
||||
from ...main_window import ElectrumWindow |
||||
|
||||
from .fee_dialog import FeeSliderDialog |
||||
from electrum.gui.qt.amountedit import BTCAmountEdit |
||||
from electrum.util import format_satoshis_plain |
||||
|
||||
Builder.load_string(''' |
||||
<CPFPDialog@Popup> |
||||
title: _('Child Pays for Parent') |
||||
size_hint: 0.8, 0.8 |
||||
pos_hint: {'top':0.9} |
||||
method: 0 |
||||
BoxLayout: |
||||
orientation: 'vertical' |
||||
padding: '10dp' |
||||
GridLayout: |
||||
height: self.minimum_height |
||||
size_hint_y: None |
||||
cols: 1 |
||||
spacing: '10dp' |
||||
TopLabel: |
||||
text: |
||||
_(\ |
||||
"A CPFP is a transaction that sends an unconfirmed output back to "\ |
||||
"yourself, with a high fee. The goal is to have miners confirm "\ |
||||
"the parent transaction in order to get the fee attached to the "\ |
||||
"child transaction.") |
||||
BoxLabel: |
||||
id: total_size |
||||
text: _('Total Size') |
||||
value: '' |
||||
BoxLabel: |
||||
id: input_amount |
||||
text: _('Input amount') |
||||
value: '' |
||||
BoxLabel: |
||||
id: output_amount |
||||
text: _('Output amount') |
||||
value: '' |
||||
BoxLabel: |
||||
id: fee_for_child |
||||
text: _('Fee for child') |
||||
value: '' |
||||
BoxLayout: |
||||
orientation: 'horizontal' |
||||
size_hint: 1, 0.5 |
||||
Label: |
||||
text: _('Target') + ' (%s):' % (_('mempool') if root.method == 2 else _('ETA') if root.method == 1 else _('static')) |
||||
Button: |
||||
id: fee_target |
||||
text: '' |
||||
background_color: (0,0,0,0) |
||||
bold: True |
||||
on_release: |
||||
root.method = (root.method + 1) % 3 |
||||
root.update_slider() |
||||
root.on_slider(root.slider.value) |
||||
|
||||
Slider: |
||||
id: slider |
||||
range: 0, 4 |
||||
step: 1 |
||||
on_value: root.on_slider(self.value) |
||||
GridLayout: |
||||
height: self.minimum_height |
||||
size_hint_y: None |
||||
cols: 1 |
||||
spacing: '10dp' |
||||
BoxLabel: |
||||
id: total_fee |
||||
text: _('Total fee') |
||||
value: '' |
||||
BoxLabel: |
||||
id: total_feerate |
||||
text: _('Total feerate') |
||||
value: '' |
||||
Widget: |
||||
size_hint: 1, 1 |
||||
BoxLayout: |
||||
orientation: 'horizontal' |
||||
size_hint: 1, 0.5 |
||||
Button: |
||||
text: 'Cancel' |
||||
size_hint: 0.5, None |
||||
height: '48dp' |
||||
on_release: root.dismiss() |
||||
Button: |
||||
text: 'OK' |
||||
size_hint: 0.5, None |
||||
height: '48dp' |
||||
on_release: |
||||
root.dismiss() |
||||
root.on_ok() |
||||
''') |
||||
|
||||
class CPFPDialog(FeeSliderDialog, Factory.Popup): |
||||
|
||||
def __init__(self, app: 'ElectrumWindow', parent_fee, total_size, new_tx, callback): |
||||
self.app = app |
||||
self.parent_fee = parent_fee |
||||
self.total_size = total_size |
||||
self.new_tx = new_tx |
||||
self.max_fee = self.new_tx.output_value() |
||||
Factory.Popup.__init__(self) |
||||
FeeSliderDialog.__init__(self, app.electrum_config, self.ids.slider) |
||||
self.callback = callback |
||||
self.config = app.electrum_config |
||||
self.ids.total_size.value = ('%d bytes'% self.total_size) |
||||
self.ids.input_amount.value = self.app.format_amount(self.max_fee) + ' ' + self.app._get_bu() |
||||
self.update_slider() |
||||
self.update_text() |
||||
|
||||
def get_child_fee_from_total_feerate(self, fee_per_kb: Optional[int]) -> Optional[int]: |
||||
if fee_per_kb is None: |
||||
return None |
||||
fee = fee_per_kb * self.total_size / 1000 - self.parent_fee |
||||
fee = round(fee) |
||||
fee = min(self.max_fee, fee) |
||||
fee = max(self.total_size, fee) # pay at least 1 sat/byte for combined size |
||||
return fee |
||||
|
||||
def update_text(self): |
||||
target, tooltip, dyn = self.config.get_fee_target() |
||||
self.ids.fee_target.text = target |
||||
fee_per_kb = self.config.fee_per_kb() |
||||
self.fee = self.get_child_fee_from_total_feerate(fee_per_kb) |
||||
if self.fee is None: |
||||
self.ids.fee_for_child.value = "unknown" |
||||
else: |
||||
comb_fee = self.fee + self.parent_fee |
||||
comb_feerate = 1000 * comb_fee / self.total_size |
||||
self.ids.fee_for_child.value = self.app.format_amount_and_units(self.fee) |
||||
self.ids.output_amount.value = self.app.format_amount_and_units(self.max_fee-self.fee) if self.max_fee > self.fee else '' |
||||
self.ids.total_fee.value = self.app.format_amount_and_units(self.fee+self.parent_fee) |
||||
self.ids.total_feerate.value = self.app.format_fee_rate(comb_feerate) |
||||
|
||||
def on_ok(self): |
||||
fee = self.fee |
||||
self.callback(fee, self.max_fee) |
||||
|
||||
def on_slider(self, value): |
||||
self.save_config() |
||||
self.update_text() |
||||
Loading…
Reference in new issue