qml: initial RbF bump fee feature

This commit is contained in:
Sander van Grieken
2022-10-25 15:13:57 +02:00
parent 1a7fc2cff7
commit 902f16204c
7 changed files with 656 additions and 189 deletions

View File

@@ -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()
}
}
}
}

View File

@@ -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

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}

View File

@@ -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')

View File

@@ -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

View File

@@ -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,118 +31,10 @@ 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
@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
feeChanged = pyqtSignal()
@pyqtProperty(QEAmount, notify=feeChanged)
def fee(self):
return self._fee
@fee.setter
def fee(self, fee):
if self._fee != fee:
self._fee.copyFrom(fee)
self.feeChanged.emit()
feeRateChanged = pyqtSignal()
@pyqtProperty(str, notify=feeRateChanged)
def feeRate(self):
return self._feeRate
@feeRate.setter
def feeRate(self, feeRate):
if self._feeRate != feeRate:
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):
return self._rbf
@rbf.setter
def rbf(self, rbf):
if self._rbf != rbf:
self._rbf = rbf
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):
return self._outputs
@outputs.setter
def outputs(self, outputs):
if self._outputs != outputs:
self._outputs = outputs
self.outputsChanged.emit()
warningChanged = pyqtSignal()
@pyqtProperty(str, notify=warningChanged)
def warning(self):
return self._warning
@warning.setter
def warning(self, warning):
if self._warning != warning:
self._warning = warning
self.warningChanged.emit()
sliderStepsChanged = pyqtSignal()
@pyqtProperty(int, notify=sliderStepsChanged)
def sliderSteps(self):
@@ -197,36 +70,200 @@ class QETxFinalizer(QObject):
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)
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()
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)
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)
self._config.set_key('depth_level', value, True)
else:
self.config.set_key('fee_level', value, True)
self._config.set_key('fee_level', value, True)
else:
self.config.set_key('fee_per_kb', self.config.static_fee(value), True)
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)
def fee(self):
return self._fee
@fee.setter
def fee(self, fee):
if self._fee != fee:
self._fee.copyFrom(fee)
self.feeChanged.emit()
feeRateChanged = pyqtSignal()
@pyqtProperty(str, notify=feeRateChanged)
def feeRate(self):
return self._feeRate
@feeRate.setter
def feeRate(self, feeRate):
if self._feeRate != feeRate:
self._feeRate = feeRate
self.feeRateChanged.emit()
rbfChanged = pyqtSignal()
@pyqtProperty(bool, notify=rbfChanged)
def rbf(self):
return self._rbf
@rbf.setter
def rbf(self, rbf):
if self._rbf != rbf:
self._rbf = rbf
self.update()
self.rbfChanged.emit()
outputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=outputsChanged)
def outputs(self):
return self._outputs
@outputs.setter
def outputs(self, outputs):
if self._outputs != outputs:
self._outputs = outputs
self.outputsChanged.emit()
warningChanged = pyqtSignal()
@pyqtProperty(str, notify=warningChanged)
def warning(self):
return self._warning
@warning.setter
def warning(self, warning):
if self._warning != warning:
self._warning = warning
self.warningChanged.emit()
validChanged = pyqtSignal()
@pyqtProperty(bool, notify=validChanged)
def valid(self):
return self._valid
def update_from_tx(self, tx):
tx_size = tx.estimated_size()
fee = tx.get_fee()
feerate = Decimal(fee) / tx_size # sat/byte
self.fee = QEAmount(amount_sat=int(fee))
self.feeRate = f'{feerate:.1f}'
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
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
_logger = get_logger(__name__)
_address = ''
_amount = QEAmount()
_effectiveAmount = QEAmount()
_canRbf = False
addressChanged = pyqtSignal()
@pyqtProperty(str, notify=addressChanged)
def address(self):
return self._address
@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):
self._logger.debug('make_tx amount = %s' % str(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)