Browse Source

qml: initial RbF bump fee feature

patch-4
Sander van Grieken 2 years ago
parent
commit
902f16204c
  1. 264
      electrum/gui/qml/components/BumpFeeDialog.qml
  2. 35
      electrum/gui/qml/components/ConfirmTxDialog.qml
  3. 40
      electrum/gui/qml/components/TxDetails.qml
  4. 26
      electrum/gui/qml/components/controls/FeeMethodComboBox.qml
  5. 3
      electrum/gui/qml/qeapp.py
  6. 4
      electrum/gui/qml/qetxdetails.py
  7. 447
      electrum/gui/qml/qetxfinalizer.py

264
electrum/gui/qml/components/BumpFeeDialog.qml

@ -0,0 +1,264 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.14
import QtQuick.Controls.Material 2.0
import org.electrum 1.0
import "controls"
ElDialog {
id: dialog
required property string txid
required property QtObject txfeebumper
signal txaccepted
title: qsTr('Bump Fee')
width: parent.width
height: parent.height
padding: 0
standardButtons: Dialog.Cancel
modal: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: "#aa000000"
}
// function updateAmountText() {
// btcValue.text = Config.formatSats(finalizer.effectiveAmount, false)
// fiatValue.text = Daemon.fx.enabled
// ? '(' + Daemon.fx.fiatValue(finalizer.effectiveAmount, false) + ' ' + Daemon.fx.fiatCurrency + ')'
// : ''
// }
ColumnLayout {
width: parent.width
height: parent.height
spacing: 0
GridLayout {
Layout.preferredWidth: parent.width
Layout.leftMargin: constants.paddingLarge
Layout.rightMargin: constants.paddingLarge
columns: 2
Label {
text: qsTr('Old fee')
color: Material.accentColor
}
RowLayout {
Label {
id: oldfee
text: Config.formatSats(txfeebumper.oldfee)
}
Label {
text: Config.baseUnit
color: Material.accentColor
}
}
Label {
text: qsTr('Old fee rate')
color: Material.accentColor
}
RowLayout {
Label {
id: oldfeeRate
text: txfeebumper.oldfeeRate
}
Label {
text: 'sat/vB'
color: Material.accentColor
}
}
// Label {
// id: amountLabel
// text: qsTr('Amount to send')
// color: Material.accentColor
// }
//
// RowLayout {
// Layout.fillWidth: true
// Label {
// id: btcValue
// font.bold: true
// }
//
// Label {
// text: Config.baseUnit
// color: Material.accentColor
// }
//
// Label {
// id: fiatValue
// Layout.fillWidth: true
// font.pixelSize: constants.fontSizeMedium
// }
//
// Component.onCompleted: updateAmountText()
// Connections {
// target: finalizer
// function onEffectiveAmountChanged() {
// updateAmountText()
// }
// }
// }
Label {
text: qsTr('Mining fee')
color: Material.accentColor
}
RowLayout {
Label {
id: fee
text: txfeebumper.valid ? Config.formatSats(txfeebumper.fee) : ''
}
Label {
visible: txfeebumper.valid
text: Config.baseUnit
color: Material.accentColor
}
}
Label {
text: qsTr('Fee rate')
color: Material.accentColor
}
RowLayout {
Label {
id: feeRate
text: txfeebumper.valid ? txfeebumper.feeRate : ''
}
Label {
visible: txfeebumper.valid
text: 'sat/vB'
color: Material.accentColor
}
}
Label {
text: qsTr('Target')
color: Material.accentColor
}
Label {
id: targetdesc
text: txfeebumper.target
}
Slider {
id: feeslider
leftPadding: constants.paddingMedium
snapMode: Slider.SnapOnRelease
stepSize: 1
from: 0
to: txfeebumper.sliderSteps
onValueChanged: {
if (activeFocus)
txfeebumper.sliderPos = value
}
Component.onCompleted: {
value = txfeebumper.sliderPos
}
Connections {
target: txfeebumper
function onSliderPosChanged() {
feeslider.value = txfeebumper.sliderPos
}
}
}
FeeMethodComboBox {
id: target
feeslider: txfeebumper
}
CheckBox {
id: final_cb
text: qsTr('Replace-by-Fee')
Layout.columnSpan: 2
checked: txfeebumper.rbf
onCheckedChanged: {
if (activeFocus)
txfeebumper.rbf = checked
}
}
InfoTextArea {
Layout.columnSpan: 2
Layout.preferredWidth: parent.width * 3/4
Layout.alignment: Qt.AlignHCenter
visible: txfeebumper.warning != ''
text: txfeebumper.warning
iconStyle: InfoTextArea.IconStyle.Warn
}
Label {
visible: txfeebumper.valid
text: qsTr('Outputs')
Layout.columnSpan: 2
color: Material.accentColor
}
Repeater {
model: txfeebumper.valid ? txfeebumper.outputs : []
delegate: TextHighlightPane {
Layout.columnSpan: 2
Layout.fillWidth: true
padding: 0
leftPadding: constants.paddingSmall
RowLayout {
width: parent.width
Label {
text: modelData.address
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
color: modelData.is_mine ? constants.colorMine : Material.foreground
}
Label {
text: Config.formatSats(modelData.value_sats)
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
}
}
}
}
Item { Layout.fillHeight: true; Layout.preferredWidth: 1 }
FlatButton {
id: sendButton
Layout.fillWidth: true
text: qsTr('Ok')
icon.source: '../../icons/confirmed.png'
enabled: txfeebumper.valid
onClicked: {
txaccepted()
dialog.close()
}
}
}
}

35
electrum/gui/qml/components/ConfirmTxDialog.qml

@ -155,31 +155,9 @@ ElDialog {
}
}
ComboBox {
FeeMethodComboBox {
id: target
textRole: 'text'
valueRole: 'value'
model: [
{ text: qsTr('ETA'), value: 1 },
{ text: qsTr('Mempool'), value: 2 },
{ text: qsTr('Static'), value: 0 }
]
onCurrentValueChanged: {
if (activeFocus)
finalizer.method = currentValue
}
Component.onCompleted: {
currentIndex = indexOfValue(finalizer.method)
}
}
InfoTextArea {
Layout.columnSpan: 2
Layout.preferredWidth: parent.width * 3/4
Layout.alignment: Qt.AlignHCenter
visible: finalizer.warning != ''
text: finalizer.warning
iconStyle: InfoTextArea.IconStyle.Warn
feeslider: finalizer
}
CheckBox {
@ -194,6 +172,15 @@ ElDialog {
}
}
InfoTextArea {
Layout.columnSpan: 2
Layout.preferredWidth: parent.width * 3/4
Layout.alignment: Qt.AlignHCenter
visible: finalizer.warning != ''
text: finalizer.warning
iconStyle: InfoTextArea.IconStyle.Warn
}
Label {
text: qsTr('Outputs')
Layout.columnSpan: 2

40
electrum/gui/qml/components/TxDetails.qml

@ -28,7 +28,18 @@ Pane {
action: Action {
text: qsTr('Bump fee')
enabled: txdetails.canBump
//onTriggered:
onTriggered: {
var dialog = bumpFeeDialog.createObject(root, { txid: root.txid })
dialog.open()
}
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Child pays for parent')
enabled: txdetails.canCpfp
onTriggered: notificationPopup.show('Not implemented')
}
}
MenuItem {
@ -36,6 +47,15 @@ Pane {
action: Action {
text: qsTr('Cancel double-spend')
enabled: txdetails.canCancel
onTriggered: notificationPopup.show('Not implemented')
}
}
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Remove')
enabled: txdetails.canRemove
onTriggered: notificationPopup.show('Not implemented')
}
}
}
@ -349,4 +369,22 @@ Pane {
rawtx: root.rawtx
onLabelChanged: root.detailsChanged()
}
Component {
id: bumpFeeDialog
BumpFeeDialog {
id: dialog
txfeebumper: TxFeeBumper {
id: txfeebumper
wallet: Daemon.currentWallet
txid: dialog.txid
}
onTxaccepted: {
root.rawtx = txfeebumper.getNewTx()
}
onClosed: destroy()
}
}
}

26
electrum/gui/qml/components/controls/FeeMethodComboBox.qml

@ -0,0 +1,26 @@
import QtQuick 2.6
import QtQuick.Controls 2.0
import org.electrum 1.0
ComboBox {
id: control
required property QtObject feeslider
textRole: 'text'
valueRole: 'value'
model: [
{ text: qsTr('ETA'), value: 1 },
{ text: qsTr('Mempool'), value: 2 },
{ text: qsTr('Static'), value: 0 }
]
onCurrentValueChanged: {
if (activeFocus)
feeslider.method = currentValue
}
Component.onCompleted: {
currentIndex = indexOfValue(feeslider.method)
}
}

3
electrum/gui/qml/qeapp.py

@ -19,7 +19,7 @@ from .qeqr import QEQRParser, QEQRImageProvider, QEQRImageProviderHelper
from .qewalletdb import QEWalletDB
from .qebitcoin import QEBitcoin
from .qefx import QEFX
from .qetxfinalizer import QETxFinalizer
from .qetxfinalizer import QETxFinalizer, QETxFeeBumper
from .qeinvoice import QEInvoice, QEInvoiceParser, QEUserEnteredPayment
from .qerequestdetails import QERequestDetails
from .qetypes import QEAmount
@ -216,6 +216,7 @@ class ElectrumQmlApplication(QGuiApplication):
qmlRegisterType(QEChannelDetails, 'org.electrum', 1, 0, 'ChannelDetails')
qmlRegisterType(QESwapHelper, 'org.electrum', 1, 0, 'SwapHelper')
qmlRegisterType(QERequestDetails, 'org.electrum', 1, 0, 'RequestDetails')
qmlRegisterType(QETxFeeBumper, 'org.electrum', 1, 0, 'TxFeeBumper')
qmlRegisterUncreatableType(QEAmount, 'org.electrum', 1, 0, 'Amount', 'Amount can only be used as property')
qmlRegisterUncreatableType(QENewWalletWizard, 'org.electrum', 1, 0, 'NewWalletWizard', 'NewWalletWizard can only be used as property')

4
electrum/gui/qml/qetxdetails.py

@ -82,6 +82,8 @@ class QETxDetails(QObject):
if self._rawtx != rawtx:
self._logger.debug('rawtx set -> %s' % rawtx)
self._rawtx = rawtx
if not rawtx:
return
try:
self._tx = tx_from_any(rawtx, deserialize=True)
self._logger.debug('tx type is %s' % str(type(self._tx)))
@ -209,7 +211,7 @@ class QETxDetails(QObject):
txinfo = self._wallet.wallet.get_tx_info(self._tx)
#self._logger.debug(repr(txinfo))
self._logger.debug(repr(txinfo))
# can be None if outputs unrelated to wallet seed,
# e.g. to_local local_force_close commitment CSV-locked p2wsh script

447
electrum/gui/qml/qetxfinalizer.py

@ -4,42 +4,23 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger
from electrum.i18n import _
from electrum.transaction import PartialTxOutput
from electrum.transaction import PartialTxOutput, PartialTransaction
from electrum.util import NotEnoughFunds, profiler
from electrum.wallet import CannotBumpFee
from .qewallet import QEWallet
from .qetypes import QEAmount
class QETxFinalizer(QObject):
def __init__(self, parent=None, *, make_tx=None, accept=None):
super().__init__(parent)
self.f_make_tx = make_tx
self.f_accept = accept
self._tx = None
_logger = get_logger(__name__)
_address = ''
_amount = QEAmount()
_effectiveAmount = QEAmount()
_fee = QEAmount()
_feeRate = ''
class FeeSlider(QObject):
_wallet = None
_valid = False
_sliderSteps = 0
_sliderPos = 0
_method = -1
_warning = ''
_target = ''
_rbf = False
_canRbf = False
_outputs = []
config = None
_config = None
validChanged = pyqtSignal()
@pyqtProperty(bool, notify=validChanged)
def valid(self):
return self._valid
def __init__(self, parent=None):
super().__init__(parent)
walletChanged = pyqtSignal()
@pyqtProperty(QEWallet, notify=walletChanged)
@ -50,37 +31,106 @@ class QETxFinalizer(QObject):
def wallet(self, wallet: QEWallet):
if self._wallet != wallet:
self._wallet = wallet
self.config = self._wallet.wallet.config
self._config = self._wallet.wallet.config
self.read_config()
self.walletChanged.emit()
addressChanged = pyqtSignal()
@pyqtProperty(str, notify=addressChanged)
def address(self):
return self._address
sliderStepsChanged = pyqtSignal()
@pyqtProperty(int, notify=sliderStepsChanged)
def sliderSteps(self):
return self._sliderSteps
@address.setter
def address(self, address):
if self._address != address:
self._address = address
self.addressChanged.emit()
sliderPosChanged = pyqtSignal()
@pyqtProperty(int, notify=sliderPosChanged)
def sliderPos(self):
return self._sliderPos
amountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=amountChanged)
def amount(self):
return self._amount
@sliderPos.setter
def sliderPos(self, sliderPos):
if self._sliderPos != sliderPos:
self._sliderPos = sliderPos
self.save_config()
self.sliderPosChanged.emit()
@amount.setter
def amount(self, amount):
if self._amount != amount:
self._logger.debug(str(amount))
self._amount.copyFrom(amount)
self.amountChanged.emit()
methodChanged = pyqtSignal()
@pyqtProperty(int, notify=methodChanged)
def method(self):
return self._method
effectiveAmountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=effectiveAmountChanged)
def effectiveAmount(self):
return self._effectiveAmount
@method.setter
def method(self, method):
if self._method != method:
self._method = method
self.update_slider()
self.methodChanged.emit()
self.save_config()
def get_method(self):
dynfees = self._method > 0
mempool = self._method == 2
return dynfees, mempool
targetChanged = pyqtSignal()
@pyqtProperty(str, notify=targetChanged)
def target(self):
return self._target
@target.setter
def target(self, target):
if self._target != target:
self._target = target
self.targetChanged.emit()
def update_slider(self):
dynfees, mempool = self.get_method()
maxp, pos, fee_rate = self._config.get_fee_slider(dynfees, mempool)
self._sliderSteps = maxp
self._sliderPos = pos
self.sliderStepsChanged.emit()
self.sliderPosChanged.emit()
def update_target(self):
target, tooltip, dyn = self._config.get_fee_target()
self.target = target
def read_config(self):
mempool = self._config.use_mempool_fees()
dynfees = self._config.is_dynfee()
self._method = (2 if mempool else 1) if dynfees else 0
self.update_slider()
self.methodChanged.emit()
self.update_target()
self.update()
def save_config(self):
value = int(self._sliderPos)
dynfees, mempool = self.get_method()
self._config.set_key('dynamic_fees', dynfees, False)
self._config.set_key('mempool_fees', mempool, False)
if dynfees:
if mempool:
self._config.set_key('depth_level', value, True)
else:
self._config.set_key('fee_level', value, True)
else:
self._config.set_key('fee_per_kb', self._config.static_fee(value), True)
self.update_target()
self.update()
def update(self):
raise NotImplementedError()
class TxFeeSlider(FeeSlider):
_fee = QEAmount()
_feeRate = ''
_rbf = False
_tx = None
_outputs = []
_valid = False
_warning = ''
def __init__(self, parent=None):
super().__init__(parent)
feeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=feeChanged)
@ -104,17 +154,6 @@ class QETxFinalizer(QObject):
self._feeRate = feeRate
self.feeRateChanged.emit()
targetChanged = pyqtSignal()
@pyqtProperty(str, notify=targetChanged)
def target(self):
return self._target
@target.setter
def target(self, target):
if self._target != target:
self._target = target
self.targetChanged.emit()
rbfChanged = pyqtSignal()
@pyqtProperty(bool, notify=rbfChanged)
def rbf(self):
@ -127,19 +166,6 @@ class QETxFinalizer(QObject):
self.update()
self.rbfChanged.emit()
canRbfChanged = pyqtSignal()
@pyqtProperty(bool, notify=canRbfChanged)
def canRbf(self):
return self._canRbf
@canRbf.setter
def canRbf(self, canRbf):
if self._canRbf != canRbf:
self._canRbf = canRbf
self.canRbfChanged.emit()
if not canRbf and self.rbf:
self.rbf = False
outputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=outputsChanged)
def outputs(self):
@ -162,70 +188,81 @@ class QETxFinalizer(QObject):
self._warning = warning
self.warningChanged.emit()
sliderStepsChanged = pyqtSignal()
@pyqtProperty(int, notify=sliderStepsChanged)
def sliderSteps(self):
return self._sliderSteps
validChanged = pyqtSignal()
@pyqtProperty(bool, notify=validChanged)
def valid(self):
return self._valid
sliderPosChanged = pyqtSignal()
@pyqtProperty(int, notify=sliderPosChanged)
def sliderPos(self):
return self._sliderPos
def update_from_tx(self, tx):
tx_size = tx.estimated_size()
fee = tx.get_fee()
feerate = Decimal(fee) / tx_size # sat/byte
@sliderPos.setter
def sliderPos(self, sliderPos):
if self._sliderPos != sliderPos:
self._sliderPos = sliderPos
self.save_config()
self.sliderPosChanged.emit()
self.fee = QEAmount(amount_sat=int(fee))
self.feeRate = f'{feerate:.1f}'
methodChanged = pyqtSignal()
@pyqtProperty(int, notify=methodChanged)
def method(self):
return self._method
outputs = []
for o in tx.outputs():
outputs.append({
'address': o.get_ui_address_str(),
'value_sats': o.value,
'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str())
})
self.outputs = outputs
@method.setter
def method(self, method):
if self._method != method:
self._method = method
self.update_slider()
self.methodChanged.emit()
self.save_config()
class QETxFinalizer(TxFeeSlider):
def __init__(self, parent=None, *, make_tx=None, accept=None):
super().__init__(parent)
self.f_make_tx = make_tx
self.f_accept = accept
def get_method(self):
dynfees = self._method > 0
mempool = self._method == 2
return dynfees, mempool
_logger = get_logger(__name__)
def update_slider(self):
dynfees, mempool = self.get_method()
maxp, pos, fee_rate = self.config.get_fee_slider(dynfees, mempool)
self._sliderSteps = maxp
self._sliderPos = pos
self.sliderStepsChanged.emit()
self.sliderPosChanged.emit()
_address = ''
_amount = QEAmount()
_effectiveAmount = QEAmount()
_canRbf = False
def read_config(self):
mempool = self.config.use_mempool_fees()
dynfees = self.config.is_dynfee()
self._method = (2 if mempool else 1) if dynfees else 0
self.update_slider()
self.methodChanged.emit()
self.update()
addressChanged = pyqtSignal()
@pyqtProperty(str, notify=addressChanged)
def address(self):
return self._address
def save_config(self):
value = int(self._sliderPos)
dynfees, mempool = self.get_method()
self.config.set_key('dynamic_fees', dynfees, False)
self.config.set_key('mempool_fees', mempool, False)
if dynfees:
if mempool:
self.config.set_key('depth_level', value, True)
else:
self.config.set_key('fee_level', value, True)
else:
self.config.set_key('fee_per_kb', self.config.static_fee(value), True)
self.update()
@address.setter
def address(self, address):
if self._address != address:
self._address = address
self.addressChanged.emit()
amountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=amountChanged)
def amount(self):
return self._amount
@amount.setter
def amount(self, amount):
if self._amount != amount:
self._logger.debug(str(amount))
self._amount.copyFrom(amount)
self.amountChanged.emit()
effectiveAmountChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=effectiveAmountChanged)
def effectiveAmount(self):
return self._effectiveAmount
canRbfChanged = pyqtSignal()
@pyqtProperty(bool, notify=canRbfChanged)
def canRbf(self):
return self._canRbf
@canRbf.setter
def canRbf(self, canRbf):
if self._canRbf != canRbf:
self._canRbf = canRbf
self.canRbfChanged.emit()
if not canRbf and self.rbf:
self.rbf = False
@profiler
def make_tx(self, amount):
@ -241,18 +278,8 @@ class QETxFinalizer(QObject):
self._logger.debug('fee: %d, inputs: %d, outputs: %d' % (tx.get_fee(), len(tx.inputs()), len(tx.outputs())))
outputs = []
for o in tx.outputs():
outputs.append({
'address': o.get_ui_address_str(),
'value_sats': o.value,
'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str())
})
self.outputs = outputs
return tx
@pyqtSlot()
def update(self):
try:
# make unsigned transaction
@ -276,26 +303,18 @@ class QETxFinalizer(QObject):
self._effectiveAmount.satsInt = amount
self.effectiveAmountChanged.emit()
tx_size = tx.estimated_size()
fee = tx.get_fee()
feerate = Decimal(fee) / tx_size # sat/byte
self._fee.satsInt = int(fee)
self.feeRate = f'{feerate:.1f}'
self.update_from_tx(tx)
#TODO
#x_fee = run_hook('get_tx_extra_fee', self._wallet.wallet, tx)
fee_warning_tuple = self._wallet.wallet.get_tx_fee_warning(
invoice_amt=amount, tx_size=tx_size, fee=fee)
invoice_amt=amount, tx_size=tx.estimated_size(), fee=tx.get_fee())
if fee_warning_tuple:
allow_send, long_warning, short_warning = fee_warning_tuple
self.warning = long_warning
else:
self.warning = ''
target, tooltip, dyn = self.config.get_fee_target()
self.target = target
self._valid = True
self.validChanged.emit()
@ -318,3 +337,133 @@ class QETxFinalizer(QObject):
return self._tx.to_qr_data()
else:
return str(self._tx)
class QETxFeeBumper(TxFeeSlider):
_logger = get_logger(__name__)
_oldfee = QEAmount()
_oldfee_rate = 0
_orig_tx = None
_txid = ''
_rbf = True
def __init__(self, parent=None):
super().__init__(parent)
txidChanged = pyqtSignal()
@pyqtProperty(str, notify=txidChanged)
def txid(self):
return self._txid
@txid.setter
def txid(self, txid):
if self._txid != txid:
self._txid = txid
self.get_tx()
self.txidChanged.emit()
oldfeeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=oldfeeChanged)
def oldfee(self):
return self._oldfee
@oldfee.setter
def oldfee(self, oldfee):
if self._oldfee != oldfee:
self._oldfee.copyFrom(oldfee)
self.oldfeeChanged.emit()
oldfeeRateChanged = pyqtSignal()
@pyqtProperty(str, notify=oldfeeRateChanged)
def oldfeeRate(self):
return self._oldfee_rate
@oldfeeRate.setter
def oldfeeRate(self, oldfeerate):
if self._oldfee_rate != oldfeerate:
self._oldfee_rate = oldfeerate
self.oldfeeRateChanged.emit()
def get_tx(self):
assert self._txid
self._orig_tx = self._wallet.wallet.get_input_tx(self._txid)
assert self._orig_tx
if not isinstance(self._orig_tx, PartialTransaction):
self._orig_tx = PartialTransaction.from_tx(self._orig_tx)
if not self._add_info_to_tx_from_wallet_and_network(self._orig_tx):
return
self.update_from_tx(self._orig_tx)
self.oldfee = self.fee
self.oldfeeRate = self.feeRate
self.update()
# TODO: duplicated from kivy gui, candidate for moving into backend wallet
def _add_info_to_tx_from_wallet_and_network(self, tx: PartialTransaction) -> bool:
"""Returns whether successful."""
# note side-effect: tx is being mutated
assert isinstance(tx, PartialTransaction)
try:
# note: this might download input utxos over network
# FIXME network code in gui thread...
tx.add_info_from_wallet(self._wallet.wallet, ignore_network_issues=False)
except NetworkException as e:
# self.app.show_error(repr(e))
self._logger.error(repr(e))
return False
return True
def update(self):
if not self._txid:
# not initialized yet
return
fee_per_kb = self._config.fee_per_kb()
if fee_per_kb is None:
# dynamic method and no network
self._logger.debug('no fee_per_kb')
self.warning = _('Cannot determine dynamic fees, not connected')
return
new_fee_rate = fee_per_kb / 1000
try:
self._tx = self._wallet.wallet.bump_fee(
tx=self._orig_tx,
txid=self._txid,
new_fee_rate=new_fee_rate,
)
except CannotBumpFee as e:
self._valid = False
self.validChanged.emit()
self._logger.error(str(e))
self.warning = str(e)
return
else:
self.warning = ''
self._tx.set_rbf(self.rbf)
self.update_from_tx(self._tx)
# TODO: deduce amount sent?
# TODO: we don't handle send-max txs correctly yet
# fee_warning_tuple = self._wallet.wallet.get_tx_fee_warning(
# invoice_amt=amount, tx_size=tx.estimated_size(), fee=tx.get_fee())
# if fee_warning_tuple:
# allow_send, long_warning, short_warning = fee_warning_tuple
# self.warning = long_warning
# else:
# self.warning = ''
self._valid = True
self.validChanged.emit()
@pyqtSlot(result=str)
def getNewTx(self):
return str(self._tx)

Loading…
Cancel
Save