diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index b0437cec1..915c659d3 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -63,6 +63,29 @@ Item { dialog.open() } + function payOnchain() { + var dialog = confirmPaymentDialog.createObject(mainView, { + address: invoice.address, + satoshis: invoice.amountOverride.isEmpty + ? invoice.amount + : invoice.amountOverride, + message: invoice.message + }) + var canComplete = !Daemon.currentWallet.isWatchOnly && Daemon.currentWallet.canSignWithoutCosigner + dialog.accepted.connect(function() { + if (!canComplete) { + if (Daemon.currentWallet.isWatchOnly) { + dialog.finalizer.saveOrShow() + } else { + dialog.finalizer.sign() + } + } else { + dialog.finalizer.signAndSend() + } + }) + dialog.open() + } + property QtObject menu: Menu { id: menu @@ -327,29 +350,23 @@ Item { height: parent.height onDoPay: { - if (invoice.invoiceType == Invoice.OnchainInvoice - || (invoice.invoiceType == Invoice.LightningInvoice - && invoice.amountOverride.isEmpty - ? invoice.amount.satsInt > Daemon.currentWallet.lightningCanSend - : invoice.amountOverride.satsInt > Daemon.currentWallet.lightningCanSend - )) - { - var dialog = confirmPaymentDialog.createObject(mainView, { - address: invoice.address, - satoshis: invoice.amountOverride.isEmpty ? invoice.amount : invoice.amountOverride, - message: invoice.message + var lninvoiceButPayOnchain = false + if (invoice.invoiceType == Invoice.LightningInvoice && invoice.address) { + // ln invoice with fallback + var amountToSend = invoice.amountOverride.isEmpty + ? invoice.amount.satsInt + : invoice.amountOverride.satsInt + if (amountToSend > Daemon.currentWallet.lightningCanSend.satsInt) { + lninvoiceButPayOnchain = true + } + } + if (invoice.invoiceType == Invoice.OnchainInvoice || lninvoiceButPayOnchain) { + var dialog = app.messageDialog.createObject(mainView, { + title: qsTr('Insufficient balance to pay over Lightning. Pay on-chain instead?'), + yesno: true }) - var canComplete = !Daemon.currentWallet.isWatchOnly && Daemon.currentWallet.canSignWithoutCosigner dialog.accepted.connect(function() { - if (!canComplete) { - if (Daemon.currentWallet.isWatchOnly) { - dialog.finalizer.saveOrShow() - } else { - dialog.finalizer.sign() - } - } else { - dialog.finalizer.signAndSend() - } + payOnchain() }) dialog.open() } else if (invoice.invoiceType == Invoice.LightningInvoice) { diff --git a/electrum/gui/qml/qeinvoice.py b/electrum/gui/qml/qeinvoice.py index 3dde33612..0230622ec 100644 --- a/electrum/gui/qml/qeinvoice.py +++ b/electrum/gui/qml/qeinvoice.py @@ -1,6 +1,4 @@ -import threading -from typing import TYPE_CHECKING, Optional, Dict, Any -import asyncio +from typing import Optional, Dict, Any from urllib.parse import urlparse from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Q_ENUMS, QTimer @@ -9,16 +7,11 @@ from electrum.i18n import _ from electrum.logging import get_logger from electrum.invoices import (Invoice, PR_UNPAID, PR_EXPIRED, PR_UNKNOWN, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST, LN_EXPIRY_NEVER) -from electrum.lnaddr import LnInvoiceException from electrum.transaction import PartialTxOutput, TxOutput -from electrum.util import InvoiceError, get_asyncio_loop -from electrum.lnutil import format_short_channel_id, IncompatibleOrInsaneFeatures -from electrum.lnurl import decode_lnurl, request_lnurl, callback_lnurl +from electrum.lnutil import format_short_channel_id from electrum.bitcoin import COIN from electrum.paymentrequest import PaymentRequest -from electrum.payment_identifier import (maybe_extract_lightning_payment_identifier, - PaymentIdentifier, PaymentIdentifierState, PaymentIdentifierType) -from electrum.bip21 import parse_bip21_URI, InvalidBitcoinURI +from electrum.payment_identifier import (PaymentIdentifier, PaymentIdentifierState, PaymentIdentifierType) from .qetypes import QEAmount from .qewallet import QEWallet from .util import status_update_timer_interval, QtEventListener, event_listener @@ -392,6 +385,7 @@ class QEInvoice(QObject, QtEventListener): def get_max_spendable_lightning(self): return self._wallet.wallet.lnworker.num_sats_can_send() if self._wallet.wallet.lnworker else 0 + class QEInvoiceParser(QEInvoice): _logger = get_logger(__name__) @@ -530,10 +524,9 @@ class QEInvoiceParser(QEInvoice): self.validationError.emit('no_lightning', _('Detected valid Lightning invoice, but Lightning not enabled for wallet and no fallback address found.')) return - if self._wallet.wallet.lnworker and not self._wallet.wallet.lnworker.channels: + if self._wallet.wallet.lnworker and not self._wallet.wallet.lnworker.channels and not lninvoice.get_address(): self.validationWarning.emit('no_channels', _('Detected valid Lightning invoice, but there are no open channels')) - self.setValidLightningInvoice(lninvoice) self.validationSuccess.emit() elif self._pi.type == PaymentIdentifierType.BIP21: @@ -544,7 +537,6 @@ class QEInvoiceParser(QEInvoice): else: self._validateRecipient_bip21_onchain(self._pi.bip21) - def _validateRecipient_bip21_onchain(self, bip21: Dict[str, Any]) -> None: if 'amount' not in bip21: amount = 0