Files
pallectrum/electrum/gui/qml/components/LnurlWithdrawRequestDialog.qml
f321x fdeada3f51 lnurl: implement LNURL-withdraw
adds handling of lnurl-withdraw payment identifiers which allow users to
withdraw bitcoin from a service by scanning a qr code or pasting the
lnurl-w code as "sending" address.
2025-08-27 15:31:43 +02:00

162 lines
6.2 KiB
QML

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import org.electrum 1.0
import "controls"
ElDialog {
id: dialog
title: qsTr('LNURL Withdraw request')
iconSource: '../../../icons/link.png'
property InvoiceParser invoiceParser
padding: 0
property int walletCanReceive: invoiceParser.wallet.lightningCanReceive.satsInt
property int providerMinWithdrawable: parseInt(invoiceParser.lnurlData['min_withdrawable_sat'])
property int providerMaxWithdrawable: parseInt(invoiceParser.lnurlData['max_withdrawable_sat'])
property int effectiveMinWithdrawable: Math.max(providerMinWithdrawable, 1)
property int effectiveMaxWithdrawable: Math.min(providerMaxWithdrawable, walletCanReceive)
property bool insufficientLiquidity: effectiveMinWithdrawable > walletCanReceive
property bool liquidityWarning: providerMaxWithdrawable > walletCanReceive
property bool amountValid: !dialog.insufficientLiquidity &&
amountBtc.textAsSats.satsInt >= dialog.effectiveMinWithdrawable &&
amountBtc.textAsSats.satsInt <= dialog.effectiveMaxWithdrawable
property bool valid: amountValid
ColumnLayout {
width: parent.width
GridLayout {
id: rootLayout
columns: 2
Layout.fillWidth: true
Layout.leftMargin: constants.paddingLarge
Layout.rightMargin: constants.paddingLarge
Layout.bottomMargin: constants.paddingLarge
InfoTextArea {
Layout.columnSpan: 2
Layout.fillWidth: true
compact: true
visible: dialog.insufficientLiquidity
text: qsTr('Too little incoming liquidity to satisfy this withdrawal request.')
+ '\n\n'
+ qsTr('Can receive: %1')
.arg(Config.formatSats(dialog.walletCanReceive) + ' ' + Config.baseUnit)
+ '\n'
+ qsTr('Minimum withdrawal amount: %1')
.arg(Config.formatSats(dialog.providerMinWithdrawable) + ' ' + Config.baseUnit)
+ '\n\n'
+ qsTr('Do a submarine swap in the \'Channels\' tab to get more incoming liquidity.')
iconStyle: InfoTextArea.IconStyle.Error
}
InfoTextArea {
Layout.columnSpan: 2
Layout.fillWidth: true
compact: true
visible: !dialog.insufficientLiquidity && dialog.providerMinWithdrawable != dialog.providerMaxWithdrawable
text: qsTr('Amount must be between %1 and %2 %3')
.arg(Config.formatSats(dialog.effectiveMinWithdrawable))
.arg(Config.formatSats(dialog.effectiveMaxWithdrawable))
.arg(Config.baseUnit)
}
InfoTextArea {
Layout.columnSpan: 2
Layout.fillWidth: true
compact: true
visible: dialog.liquidityWarning && !dialog.insufficientLiquidity
text: qsTr('The maximum withdrawable amount (%1) is larger than what your channels can receive (%2).')
.arg(Config.formatSats(dialog.providerMaxWithdrawable) + ' ' + Config.baseUnit)
.arg(Config.formatSats(dialog.walletCanReceive) + ' ' + Config.baseUnit)
+ ' '
+ qsTr('You may need to do a submarine swap to increase your incoming liquidity.')
iconStyle: InfoTextArea.IconStyle.Warn
}
Label {
text: qsTr('Provider')
color: Material.accentColor
}
Label {
Layout.fillWidth: true
text: invoiceParser.lnurlData['domain']
}
Label {
text: qsTr('Description')
color: Material.accentColor
visible: invoiceParser.lnurlData['default_description']
}
Label {
Layout.fillWidth: true
text: invoiceParser.lnurlData['default_description']
visible: invoiceParser.lnurlData['default_description']
wrapMode: Text.Wrap
}
Label {
text: qsTr('Amount')
color: Material.accentColor
}
RowLayout {
Layout.fillWidth: true
BtcField {
id: amountBtc
Layout.preferredWidth: rootLayout.width / 3
text: Config.formatSatsForEditing(dialog.effectiveMaxWithdrawable)
enabled: !dialog.insufficientLiquidity && (dialog.providerMinWithdrawable != dialog.providerMaxWithdrawable)
color: Material.foreground // override gray-out on disabled
fiatfield: amountFiat
onTextAsSatsChanged: {
invoiceParser.amountOverride = textAsSats
}
}
Label {
text: Config.baseUnit
color: Material.accentColor
}
}
Item { visible: Daemon.fx.enabled; Layout.preferredWidth: 1; Layout.preferredHeight: 1 }
RowLayout {
visible: Daemon.fx.enabled
FiatField {
id: amountFiat
Layout.preferredWidth: rootLayout.width / 3
btcfield: amountBtc
enabled: !dialog.insufficientLiquidity && (dialog.providerMinWithdrawable != dialog.providerMaxWithdrawable)
color: Material.foreground
}
Label {
text: Daemon.fx.fiatCurrency
color: Material.accentColor
}
}
}
FlatButton {
Layout.topMargin: constants.paddingLarge
Layout.fillWidth: true
text: qsTr('Withdraw...')
icon.source: '../../icons/confirmed.png'
enabled: valid
onClicked: {
invoiceParser.lnurlRequestWithdrawal()
dialog.close()
}
}
}
}