electrum

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

commit 6b36c59ab062ea01fcdc67194c9e6aeb5c504038
parent ac3ec19d2d2f7f1b89ff36ba4d44b3c7da695061
Author: ThomasV <thomasv@electrum.org>
Date:   Mon, 25 May 2020 12:47:08 +0200

submarine_swaps: add fee slider, improve gui

Diffstat:
Melectrum/gui/qt/swap_dialog.py | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Melectrum/submarine_swaps.py | 8+++++---
2 files changed, 71 insertions(+), 20 deletions(-)

diff --git a/electrum/gui/qt/swap_dialog.py b/electrum/gui/qt/swap_dialog.py @@ -14,7 +14,8 @@ from electrum.lnworker import LNWallet from .util import (MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButton, EnterButton, WaitingDialog, MONOSPACE_FONT, ColorScheme) from .amountedit import BTCAmountEdit, FreezableLineEdit - +from .util import WWLabel +from .fee_slider import FeeSlider import asyncio from .util import read_QIcon @@ -25,19 +26,22 @@ class SwapDialog(WindowModalDialog): def __init__(self, window): WindowModalDialog.__init__(self, window, _('Submarine Swap')) self.window = window + self.config = window.config self.swap_manager = self.window.wallet.lnworker.swap_manager self.network = window.network self.normal_fee = 0 self.lockup_fee = 0 - self.claim_fee = 0 + self.claim_fee = self.swap_manager.get_tx_fee() self.percentage = 0 + self.min_amount = 0 + self.max_amount = 0 vbox = QVBoxLayout(self) + vbox.addWidget(WWLabel('Swap lightning funds for on-chain funds if you need to increase your receiving capacity. This service is powered by the Boltz backend.')) self.send_amount_e = BTCAmountEdit(self.window.get_decimal_point) self.recv_amount_e = BTCAmountEdit(self.window.get_decimal_point) self.send_button = QPushButton('') self.recv_button = QPushButton('') - self.is_reverse = False - self.toggle_direction() + self.is_reverse = True self.send_amount_e.follows = False self.recv_amount_e.follows = False self.send_button.clicked.connect(self.toggle_direction) @@ -51,22 +55,47 @@ class SwapDialog(WindowModalDialog): h.addWidget(QLabel(_('You receive')+':'), 3, 0) h.addWidget(self.recv_amount_e, 3, 1) h.addWidget(self.recv_button, 3, 2) - self.normal_fee_label = QLabel() - self.lockup_fee_label = QLabel() - self.claim_fee_label = QLabel() - h.addWidget(self.normal_fee_label, 4, 0, 1, 2) - h.addWidget(self.lockup_fee_label, 5, 0, 1, 2) - h.addWidget(self.claim_fee_label, 6, 0, 1, 2) + self.min_label = QLabel('') + self.max_label = QLabel('') + self.fee_label = QLabel() + self.percentage_label = QLabel() + h.addWidget(QLabel(_('Min amount')+':'), 4, 0) + h.addWidget(self.min_label, 4, 1) + h.addWidget(QLabel(_('Max amount')+':'), 5, 0) + h.addWidget(self.max_label, 5, 1) + h.addWidget(QLabel(_('Mining fee')+':'), 4, 2) + h.addWidget(self.fee_label, 4, 3) + h.addWidget(QLabel(_('Swap fee')+':'), 5, 2) + h.addWidget(self.percentage_label, 5, 3) vbox.addLayout(h) + fee_slider = FeeSlider(self.window, self.config, self.fee_slider_callback) + fee_slider.update() + h.addWidget(fee_slider, 6, 1) + vbox.addStretch(1) ok_button = OkButton(self) ok_button.setDefault(True) vbox.addLayout(Buttons(CancelButton(self), ok_button)) - # todo: add a fee slider for the claim tx + self.update() + + def fee_slider_callback(self, dyn, pos, fee_rate): + if dyn: + if self.config.use_mempool_fees(): + self.config.set_key('depth_level', pos, False) + else: + self.config.set_key('fee_level', pos, False) + else: + self.config.set_key('fee_per_kb', fee_rate, False) + # read claim_fee from config + self.claim_fee = self.swap_manager.get_tx_fee() + self.on_send_edited() + #self.on_recv_edited() + self.update() def toggle_direction(self): self.is_reverse = not self.is_reverse - self.send_button.setIcon(read_QIcon("lightning.png" if self.is_reverse else "bitcoin.png")) - self.recv_button.setIcon(read_QIcon("lightning.png" if not self.is_reverse else "bitcoin.png")) + self.send_amount_e.setAmount(None) + self.recv_amount_e.setAmount(None) + self.update() def on_send_edited(self): if self.send_amount_e.follows: @@ -92,20 +121,40 @@ class SwapDialog(WindowModalDialog): self.percentage = fees['percentage'] self.normal_fee = fees['minerFees']['baseAsset']['normal'] self.lockup_fee = fees['minerFees']['baseAsset']['reverse']['lockup'] - self.claim_fee = fees['minerFees']['baseAsset']['reverse']['claim'] - self.normal_fee_label.setText(f'normal fee: {self.normal_fee}') - self.lockup_fee_label.setText(f'lockup fee: {self.lockup_fee}') - self.claim_fee_label.setText(f'claim fee: {self.claim_fee}') + #self.claim_fee = fees['minerFees']['baseAsset']['reverse']['claim'] + limits = pairs['pairs']['BTC/BTC']['limits'] + self.min_amount = limits['minimal'] + self.max_amount = limits['maximal'] + self.update() + + def update(self): + self.send_button.setIcon(read_QIcon("lightning.png" if self.is_reverse else "bitcoin.png")) + self.recv_button.setIcon(read_QIcon("lightning.png" if not self.is_reverse else "bitcoin.png")) + fee = self.lockup_fee + self.claim_fee if self.is_reverse else self.normal_fee + self.fee_label.setText(self.window.format_amount(fee) + ' ' + self.window.base_unit()) + self.percentage_label.setText('%.2f'%self.percentage + '%') + self.min_label.setText(self.window.format_amount(self.min_amount) + ' ' + self.window.base_unit()) + self.max_label.setText(self.window.format_amount(self.max_amount) + ' ' + self.window.base_unit()) + + def set_minimum(self): + self.send_amount_e.setAmount(self.min_amount) + + def set_maximum(self): + self.send_amount_e.setAmount(self.max_amount) def get_recv_amount(self, send_amount): if send_amount is None: return + if send_amount < self.min_amount or send_amount > self.max_amount: + return x = send_amount * (100 - self.percentage) / 100 if self.is_reverse: x -= self.lockup_fee x -= self.claim_fee else: x -= self.normal_fee + if x < 0: + return return x def get_send_amount(self, recv_amount): diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py @@ -65,7 +65,7 @@ def create_claim_tx(txin, witness_script, preimage, privkey:bytes, address, amou txin.script_sig = bytes.fromhex(push_script(txin.redeem_script.hex())) txin.witness_script = witness_script txout = PartialTxOutput(scriptpubkey=bytes.fromhex(address_to_script(address)), value=amount_sat) - tx = PartialTransaction.from_io([txin], [txout], version=2, locktime=locktime) + tx = PartialTransaction.from_io([txin], [txout], version=2, locktime=(None if preimage else locktime)) tx.set_rbf(True) sig = bytes.fromhex(tx.sign_txin(0, privkey)) witness = [sig, preimage, witness_script] @@ -87,8 +87,7 @@ class SwapManager(Logger): self.logger.info(f'height not reached for refund {lockup_address} {delta}, {locktime}') return for txin in list(utxos.values()): - fee = self.lnwatcher.config.estimate_fee(136, allow_fallback_to_static_rates=True) - amount_sat = txin._trusted_value_sats - fee + amount_sat = txin._trusted_value_sats - self.get_tx_fee() if amount_sat < dust_threshold(): self.logger.info('utxo value below dust threshold') continue @@ -96,6 +95,9 @@ class SwapManager(Logger): tx = create_claim_tx(txin, redeem_script, preimage, privkey, address, amount_sat, locktime) await self.network.broadcast_transaction(tx) + def get_tx_fee(self): + return self.lnwatcher.config.estimate_fee(136, allow_fallback_to_static_rates=True) + def __init__(self, wallet: 'Abstract_Wallet', network:'Network'): Logger.__init__(self) self.network = network