1414)
1515from electrum .transaction import PartialTxOutput , TxOutput
1616from electrum .lnutil import format_short_channel_id
17+ from electrum .lnurl import LNURLData , LNURL3Data , LNURL6Data , request_lnurl_withdraw_callback , LNURLError
1718from electrum .bitcoin import COIN , address_to_script
1819from electrum .paymentrequest import PaymentRequest
1920from electrum .payment_identifier import PaymentIdentifier , PaymentIdentifierState , PaymentIdentifierType
@@ -32,6 +33,7 @@ class Type(IntEnum):
3233 OnchainInvoice = 0
3334 LightningInvoice = 1
3435 LNURLPayRequest = 2
36+ LNURLWithdrawRequest = 3
3537
3638 @pyqtEnum
3739 class Status (IntEnum ):
@@ -477,7 +479,7 @@ def lnurlData(self):
477479
478480 @pyqtProperty (bool , notify = lnurlRetrieved )
479481 def isLnurlPay (self ):
480- return self ._lnurlData is not None
482+ return self ._lnurlData is not None and self . invoiceType == QEInvoice . Type . LNURLPayRequest
481483
482484 @pyqtProperty (bool , notify = busyChanged )
483485 def busy (self ):
@@ -512,6 +514,12 @@ def setValidLNURLPayRequest(self):
512514 self ._effectiveInvoice = None
513515 self .invoiceChanged .emit ()
514516
517+ def setValidLNURLWithdrawRequest (self ):
518+ self ._logger .debug ('setValidLNURLWithdrawRequest' )
519+ self .setInvoiceType (QEInvoice .Type .LNURLWithdrawRequest )
520+ self ._effectiveInvoice = None
521+ self .invoiceChanged .emit ()
522+
515523 def create_onchain_invoice (self , outputs , message , payment_request , uri ):
516524 return self ._wallet .wallet .create_invoice (
517525 outputs = outputs ,
@@ -542,7 +550,7 @@ def validateRecipient(self, recipient):
542550 self ._pi = PaymentIdentifier (self ._wallet .wallet , recipient )
543551 if not self ._pi .is_valid () or self ._pi .type not in [PaymentIdentifierType .SPK , PaymentIdentifierType .BIP21 ,
544552 PaymentIdentifierType .BIP70 , PaymentIdentifierType .BOLT11 ,
545- PaymentIdentifierType .LNURLP ,
553+ PaymentIdentifierType .LNURL ,
546554 PaymentIdentifierType .EMAILLIKE ,
547555 PaymentIdentifierType .DOMAINLIKE ]:
548556 self .validationError .emit ('unknown' , _ ('Unknown invoice' ))
@@ -561,7 +569,11 @@ def _update_from_payment_identifier(self):
561569 self .resolve_pi ()
562570 return
563571
564- if self ._pi .type in [PaymentIdentifierType .LNURLP , PaymentIdentifierType .LNADDR ]:
572+ if self ._pi .type in [
573+ PaymentIdentifierType .LNURLP ,
574+ PaymentIdentifierType .LNURLW ,
575+ PaymentIdentifierType .LNADDR ,
576+ ]:
565577 self .on_lnurl (self ._pi .lnurl_data )
566578 return
567579
@@ -621,7 +633,7 @@ def on_finished(pi: PaymentIdentifier):
621633 if pi .is_error ():
622634 if pi .type in [PaymentIdentifierType .EMAILLIKE , PaymentIdentifierType .DOMAINLIKE ]:
623635 msg = _ ('Could not resolve address' )
624- elif pi .type == PaymentIdentifierType .LNURLP :
636+ elif pi .type == PaymentIdentifierType .LNURL :
625637 msg = _ ('Could not resolve LNURL' ) + "\n \n " + pi .get_error ()
626638 elif pi .type == PaymentIdentifierType .BIP70 :
627639 msg = _ ('Could not resolve BIP70 payment request: {}' ).format (pi .error )
@@ -636,26 +648,40 @@ def on_finished(pi: PaymentIdentifier):
636648
637649 self ._pi .resolve (on_finished = on_finished )
638650
639- def on_lnurl (self , lnurldata ):
651+ def on_lnurl (self , lnurldata : LNURLData ):
640652 self ._logger .debug ('on_lnurl' )
641653 self ._logger .debug (f'{ repr (lnurldata )} ' )
642654
643- self ._lnurlData = {
644- 'domain' : urlparse (lnurldata .callback_url ).netloc ,
645- 'callback_url' : lnurldata .callback_url ,
646- 'min_sendable_sat' : lnurldata .min_sendable_sat ,
647- 'max_sendable_sat' : lnurldata .max_sendable_sat ,
648- 'metadata_plaintext' : lnurldata .metadata_plaintext ,
649- 'comment_allowed' : lnurldata .comment_allowed
650- }
651- self .setValidLNURLPayRequest ()
655+ if isinstance (lnurldata , LNURL6Data ):
656+ self ._lnurlData = {
657+ 'domain' : urlparse (lnurldata .callback_url ).netloc ,
658+ 'callback_url' : lnurldata .callback_url ,
659+ 'min_sendable_sat' : lnurldata .min_sendable_sat ,
660+ 'max_sendable_sat' : lnurldata .max_sendable_sat ,
661+ 'metadata_plaintext' : lnurldata .metadata_plaintext ,
662+ 'comment_allowed' : lnurldata .comment_allowed ,
663+ }
664+ self .setValidLNURLPayRequest ()
665+ elif isinstance (lnurldata , LNURL3Data ):
666+ self ._lnurlData = {
667+ 'domain' : urlparse (lnurldata .callback_url ).netloc ,
668+ 'callback_url' : lnurldata .callback_url ,
669+ 'min_withdrawable_sat' : lnurldata .min_withdrawable_sat ,
670+ 'max_withdrawable_sat' : lnurldata .max_withdrawable_sat ,
671+ 'default_description' : lnurldata .default_description ,
672+ 'k1' : lnurldata .k1 ,
673+ }
674+ self .setValidLNURLWithdrawRequest ()
675+ else :
676+ raise NotImplementedError (f"Invalid lnurl type in on_lnurl { lnurldata = } " )
652677 self .lnurlRetrieved .emit ()
653678
654679 @pyqtSlot ()
655680 @pyqtSlot (str )
656681 def lnurlGetInvoice (self , comment = None ):
657682 assert self ._lnurlData
658683 assert self ._pi .need_finalize ()
684+ assert self .invoiceType == QEInvoice .Type .LNURLPayRequest
659685 self ._logger .debug (f'{ repr (self ._lnurlData )} ' )
660686
661687 amount = self .amountOverride .satsInt
@@ -680,6 +706,54 @@ def on_finished(pi):
680706
681707 self ._pi .finalize (amount_sat = amount , comment = comment , on_finished = on_finished )
682708
709+ @pyqtSlot ()
710+ def lnurlRequestWithdrawal (self ):
711+ assert self ._lnurlData
712+ assert self .invoiceType == QEInvoice .Type .LNURLWithdrawRequest
713+ self ._logger .debug (f'{ repr (self ._lnurlData )} ' )
714+
715+ amount_sat = self .amountOverride .satsInt
716+
717+ try :
718+ key = self .wallet .wallet .create_request (
719+ amount_sat = amount_sat ,
720+ message = self ._lnurlData .get ('default_description' , '' ),
721+ exp_delay = 120 ,
722+ address = None ,
723+ )
724+ req = self .wallet .wallet .get_request (key )
725+ _lnaddr , b11_invoice = self .wallet .wallet .lnworker .get_bolt11_invoice (
726+ payment_hash = req .payment_hash ,
727+ amount_msat = req .get_amount_msat (),
728+ message = req .get_message (),
729+ expiry = req .exp ,
730+ fallback_address = None
731+ )
732+ except Exception as e :
733+ self ._logger .exception ('' )
734+ self .lnurlError .emit (
735+ 'lnurl' ,
736+ _ ("Failed to create payment request for withdrawal: {}" ).format (str (e ))
737+ )
738+ return
739+
740+ self ._busy = True
741+ self .busyChanged .emit ()
742+
743+ coro = request_lnurl_withdraw_callback (
744+ callback_url = self ._lnurlData ['callback_url' ],
745+ k1 = self ._lnurlData ['k1' ],
746+ bolt_11 = b11_invoice ,
747+ )
748+ try :
749+ Network .run_from_another_thread (coro )
750+ except LNURLError as e :
751+ self .lnurlError .emit ('lnurl' , str (e ))
752+
753+ self ._busy = False
754+ self .busyChanged .emit ()
755+
756+
683757 def on_lnurl_invoice (self , orig_amount , invoice ):
684758 self ._logger .debug ('on_lnurl_invoice' )
685759 self ._logger .debug (f'{ repr (invoice )} ' )
0 commit comments