Skip to content

Commit 8595781

Browse files
committed
qml: let user finalize forward swap onchain tx before initiating swap
1 parent ce64dea commit 8595781

File tree

3 files changed

+72
-14
lines changed

3 files changed

+72
-14
lines changed

electrum/gui/qml/components/SwapDialog.qml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,19 @@ ElDialog {
293293
enabled: swaphelper.valid && (swaphelper.state == SwapHelper.ServiceReady || swaphelper.state == SwapHelper.Failed)
294294

295295
onClicked: {
296-
swaphelper.executeSwap()
296+
if (swaphelper.isReverse) {
297+
swaphelper.executeSwap()
298+
} else {
299+
swaphelper.prepNormalSwap()
300+
var dialog = forwardSwapTxDialog.createObject(app, {
301+
finalizer: swaphelper.finalizer,
302+
satoshis: swaphelper.finalizer.amount
303+
})
304+
dialog.accepted.connect(function() {
305+
swaphelper.executeSwap()
306+
})
307+
dialog.open()
308+
}
297309
}
298310
}
299311
FlatButton {
@@ -331,6 +343,15 @@ ElDialog {
331343
}
332344
}
333345

346+
Component {
347+
id: forwardSwapTxDialog
348+
ConfirmTxDialog {
349+
amountLabelText: qsTr('Amount to swap')
350+
sendButtonText: qsTr('Swap')
351+
finalizer: swaphelper.finalizer
352+
}
353+
}
354+
334355
Component.onCompleted: {
335356
swapslider.value = swaphelper.sliderPos
336357
}

electrum/gui/qml/qeswaphelper.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from PyQt6.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, pyqtEnum, QAbstractListModel, Qt,
77
QModelIndex)
88

9+
from electrum.gui.qml.qetxfinalizer import QETxFinalizer
910
from electrum.i18n import _
1011
from electrum.bitcoin import DummyAddress
1112
from electrum.logging import get_logger
@@ -156,6 +157,7 @@ def __init__(self, parent=None):
156157
super().__init__(parent)
157158

158159
self._wallet = None # type: Optional[QEWallet]
160+
self._finalizer = None # type: Optional[QETxFinalizer]
159161
self._sliderPos = 0
160162
self._rangeMin = -1
161163
self._rangeMax = 1
@@ -213,6 +215,11 @@ def wallet(self, wallet: QEWallet):
213215
self.run_swap_manager()
214216
self.walletChanged.emit()
215217

218+
finalizerChanged = pyqtSignal()
219+
@pyqtProperty(QETxFinalizer, notify=finalizerChanged)
220+
def finalizer(self):
221+
return self._finalizer
222+
216223
sliderPosChanged = pyqtSignal()
217224
@pyqtProperty(float, notify=sliderPosChanged)
218225
def sliderPos(self):
@@ -545,24 +552,26 @@ def initSwapSliderRange(self):
545552
self.swap_slider_moved()
546553

547554
@profiler
548-
def update_tx(self, onchain_amount: Union[int, str]):
555+
def update_tx(self, onchain_amount: Union[int, str], fee_policy: Optional[FeePolicy] = None):
549556
"""Updates the transaction associated with a forward swap."""
550557
if onchain_amount is None:
551558
self._tx = None
552559
self.valid = False
553560
return
554-
outputs = [PartialTxOutput.from_address_and_value(DummyAddress.SWAP, onchain_amount)]
555-
coins = self._wallet.wallet.get_spendable_coins(None)
556-
fee_policy = FeePolicy('eta:2')
557561
try:
558-
self._tx = self._wallet.wallet.make_unsigned_transaction(
559-
coins=coins,
560-
outputs=outputs,
561-
fee_policy=fee_policy)
562+
self._tx = self._create_swap_tx(onchain_amount, fee_policy)
562563
except (NotEnoughFunds, NoDynamicFeeEstimates):
563564
self._tx = None
564565
self.valid = False
565566

567+
def _create_swap_tx(self, onchain_amount: int | str, fee_policy: Optional[FeePolicy] = None):
568+
outputs = [PartialTxOutput.from_address_and_value(DummyAddress.SWAP, onchain_amount)]
569+
coins = self._wallet.wallet.get_spendable_coins(None)
570+
fee_policy = fee_policy if fee_policy else FeePolicy('eta:2')
571+
return self._wallet.wallet.make_unsigned_transaction(
572+
coins=coins, outputs=outputs, fee_policy=fee_policy
573+
)
574+
566575
@qt_event_listener
567576
def on_event_fee_histogram(self, *args):
568577
self.swap_slider_moved()
@@ -627,7 +636,8 @@ def do_normal_swap(self, lightning_amount, onchain_amount):
627636
async def swap_task():
628637
assert self.swap_transport is not None, "Swap transport not available"
629638
try:
630-
dummy_tx = self._create_tx(onchain_amount)
639+
# dummy_tx = self._create_tx(onchain_amount)
640+
dummy_tx = self._finalized_tx
631641
self.userinfo = _('Performing swap...')
632642
self.state = QESwapHelper.State.Started
633643
self._swap, invoice = await self._wallet.wallet.lnworker.swap_manager.request_normal_swap(
@@ -673,6 +683,27 @@ async def swap_task():
673683

674684
asyncio.run_coroutine_threadsafe(swap_task(), get_asyncio_loop())
675685

686+
@pyqtSlot()
687+
def prepNormalSwap(self):
688+
def mktx(amt, fee_policy: FeePolicy):
689+
try:
690+
self._finalized_tx = self._create_swap_tx(amt, fee_policy)
691+
except (NotEnoughFunds, NoDynamicFeeEstimates):
692+
self._finalized_tx = None
693+
return self._finalized_tx
694+
695+
# def acpt(tx):
696+
# raise Exception('EXPECTED!')
697+
# self._logger.info(f'acpt {tx=}')
698+
# assert tx == self._tx, 'acpt tx is not self._tx'
699+
# assert tx != self._tx, 'acpt tx is self._tx'
700+
701+
self._finalizer = QETxFinalizer(self, make_tx=mktx) #, accept=acpt)
702+
self._finalizer.canRbf = False
703+
self._finalizer.amount = QEAmount(amount_sat=self._send_amount)
704+
self._finalizer.wallet = self._wallet
705+
self.finalizerChanged.emit()
706+
676707
def _create_tx(self, onchain_amount: Union[int, str, None]) -> PartialTransaction:
677708
# TODO: func taken from qt GUI, this should be common code
678709
assert not self.isReverse

electrum/gui/qml/qetxfinalizer.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from enum import IntEnum
33
import threading
44
from decimal import Decimal
5-
from typing import Optional, TYPE_CHECKING
5+
from typing import Optional, TYPE_CHECKING, Callable
66
from functools import partial
77

88
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, pyqtEnum
@@ -54,7 +54,7 @@ def __init__(self, parent=None):
5454
self._wallet = None # type: Optional[QEWallet]
5555
self._sliderSteps = 0
5656
self._sliderPos = 0
57-
self._fee_policy = None
57+
self._fee_policy: Optional[FeePolicy] = None
5858
self._target = ''
5959
self._config = None # type: Optional[SimpleConfig]
6060

@@ -314,7 +314,13 @@ class QETxFinalizer(TxFeeSlider):
314314
finished = pyqtSignal([bool, bool, bool], arguments=['signed', 'saved', 'complete'])
315315
signError = pyqtSignal([str], arguments=['message'])
316316

317-
def __init__(self, parent=None, *, make_tx=None, accept=None):
317+
def __init__(
318+
self,
319+
parent=None,
320+
*,
321+
make_tx: Optional[Callable[[int | str, Optional[FeePolicy]], PartialTransaction]] = None,
322+
accept: Optional[Callable[[PartialTransaction], None]] = None
323+
):
318324
super().__init__(parent)
319325
self.f_make_tx = make_tx
320326
self.f_accept = accept
@@ -377,7 +383,7 @@ def canRbf(self, canRbf):
377383
self.rbf = self._canRbf # if we can RbF, we do RbF
378384

379385
@profiler
380-
def make_tx(self, amount):
386+
def make_tx(self, amount: int | str) -> PartialTransaction:
381387
self._logger.debug(f'make_tx amount={amount}')
382388

383389
if self.f_make_tx:

0 commit comments

Comments
 (0)