3434 get_pubkey ,
3535 nip44_decrypt ,
3636)
37+ from .lnurl import (
38+ get_lnurl_data ,
39+ get_lnurl_invoice ,
40+ parse_lightning_invoice_amount ,
41+ LNURLError ,
42+ )
3743from .types import ProofDict , WalletError
3844from .events import EventManager
3945
@@ -583,6 +589,7 @@ async def melt(self, invoice: str, *, target_mint: str | None = None) -> None:
583589
584590 Args:
585591 invoice: BOLT-11 Lightning invoice to pay
592+ target_mint: Target mint URL (defaults to primary mint)
586593
587594 Raises:
588595 WalletError: If insufficient balance or payment fails
@@ -593,28 +600,61 @@ async def melt(self, invoice: str, *, target_mint: str | None = None) -> None:
593600 if target_mint is None :
594601 target_mint = self .mint_urls [0 ]
595602
596- invoice_amount = 0 # TODO: get_invoice_amount_with_fees(invoice)
603+ try :
604+ invoice_amount = parse_lightning_invoice_amount (invoice , self .currency )
605+ except LNURLError as e :
606+ raise WalletError (f"Invalid Lightning invoice: { e } " ) from e
597607
608+ # Get current state and check balance
598609 state = await self .fetch_wallet_state (check_proofs = True )
599- self .raise_if_insufficient_balance (state .balance , invoice_amount )
600610
611+ # Create melt quote to get fees
612+ mint = self ._get_mint (target_mint )
613+ melt_quote = await mint .create_melt_quote (unit = self .currency , request = invoice )
614+ fee_reserve = melt_quote .get ("fee_reserve" , 0 )
615+ total_needed = invoice_amount + fee_reserve
616+
617+ self .raise_if_insufficient_balance (state .balance , total_needed )
618+
619+ # Select proofs for the total amount needed (invoice + fees)
601620 selected_proofs , consumed_proofs = await self ._select_proofs (
602- state .proofs , invoice_amount , target_mint
621+ state .proofs , total_needed , target_mint
603622 )
604- print (selected_proofs )
605- # TODO: self.mark_pending_proofs(selected_proofs)
606623
607- # melt proofs and pay invoice
608- mint = self ._get_mint (target_mint )
609- melt_quote = await mint .create_melt_quote (unit = self .currency , request = invoice )
610- print (melt_quote )
611- # TODO: convert selected_proofs to mint format
612- # melt_resp = await mint.melt(quote=melt_quote["quote"], inputs=selected_proofs)
624+ # Convert selected proofs to mint format
625+ mint_proofs = [self ._proofdict_to_mint_proof (p ) for p in selected_proofs ]
613626
614- # TODO: check success and undo if failed or retry
627+ # Execute the melt operation
628+ melt_resp = await mint .melt (quote = melt_quote ["quote" ], inputs = mint_proofs )
615629
616- # TODO: publish spending history with fee information
617- pass
630+ # Check if payment was successful
631+ if not melt_resp .get ("paid" , False ):
632+ raise WalletError (
633+ f"Lightning payment failed. State: { melt_resp .get ('state' , 'unknown' )} "
634+ )
635+
636+ # Handle any change returned from the mint
637+ change_proofs : list [ProofDict ] = []
638+ if "change" in melt_resp and melt_resp ["change" ]:
639+ # Convert BlindedSignatures to ProofDict format
640+ # This would require unblinding logic, but for now we'll skip change handling
641+ # In practice, most melts shouldn't have change if amounts are selected properly
642+ pass
643+
644+ # Mark the consumed input proofs as spent
645+ await self ._mark_proofs_as_spent (consumed_proofs )
646+
647+ # Store any change proofs
648+ if change_proofs :
649+ await self .store_proofs (change_proofs )
650+
651+ # Publish spending history
652+ event_manager = await self ._ensure_event_manager ()
653+ await event_manager .publish_spending_history (
654+ direction = "out" ,
655+ amount = invoice_amount , # The actual invoice amount paid
656+ destroyed_token_ids = [], # Will be handled by _mark_proofs_as_spent
657+ )
618658
619659 async def send (
620660 self ,
@@ -700,7 +740,6 @@ async def send_to_lnurl(self, lnurl: str, amount: int) -> int:
700740 paid = await wallet.send_to_lnurl("user@getalby.com", 1000)
701741 print(f"Paid {paid} sats")
702742 """
703- from .lnurl import get_lnurl_data , get_lnurl_invoice
704743
705744 # Get current balance
706745 state = await self .fetch_wallet_state (check_proofs = True )
@@ -1777,13 +1816,17 @@ async def fetch_wallet_state(self, *, check_proofs: bool = True) -> WalletState:
17771816 decrypted = nip44_decrypt (wallet_event ["content" ], self ._privkey )
17781817 wallet_data = json .loads (decrypted )
17791818
1780- # Update mint URLs from wallet event
1781- self . mint_urls = []
1819+ # Update mint URLs from wallet event (only if event contains mint URLs)
1820+ event_mint_urls = []
17821821 for item in wallet_data :
17831822 if item [0 ] == "mint" :
1784- self . mint_urls .append (item [1 ])
1823+ event_mint_urls .append (item [1 ])
17851824 elif item [0 ] == "privkey" :
17861825 self .wallet_privkey = item [1 ]
1826+
1827+ # Only update mint URLs if the event actually contains some
1828+ if event_mint_urls :
1829+ self .mint_urls = event_mint_urls
17871830 except Exception as e :
17881831 # Skip wallet event if it can't be decrypted
17891832 print (f"Warning: Could not decrypt wallet event: { e } " )
0 commit comments