11//! An implementation of `TrustedWalletInterface` using the Cashu (CDK) SDK.
22
3+ use crate :: bitcoin:: hex:: DisplayHex ;
34use crate :: logging:: Logger ;
45use crate :: store:: { PaymentId , TxMetadataStore , TxStatus } ;
56use crate :: trusted_wallet:: { Payment , TrustedError , TrustedWalletInterface } ;
@@ -26,8 +27,11 @@ use cdk::wallet::Wallet;
2627use cdk:: wallet:: types:: { Transaction , TransactionDirection } ;
2728use cdk:: { Amount as CdkAmount , StreamExt } ;
2829
30+ use graduated_rebalancer:: ReceivedLightningPayment ;
31+
2932use tokio:: sync:: { mpsc, watch} ;
3033
34+ use std:: collections:: HashMap ;
3135use std:: future:: Future ;
3236use std:: pin:: Pin ;
3337use std:: str:: FromStr ;
@@ -55,6 +59,8 @@ pub struct Cashu {
5559 cashu_wallet : Arc < Wallet > ,
5660 unit : CurrencyUnit ,
5761 shutdown_sender : watch:: Sender < ( ) > ,
62+ payment_success_sender : watch:: Sender < ( ) > ,
63+ payment_success_flag : watch:: Receiver < ( ) > ,
5864 logger : Arc < Logger > ,
5965 supports_bolt12 : bool ,
6066 mint_quote_sender : mpsc:: Sender < MintQuote > ,
@@ -291,8 +297,14 @@ impl TrustedWalletInterface for Cashu {
291297 let event_queue = Arc :: clone ( & self . event_queue ) ;
292298 let tx_metadata = self . tx_metadata . clone ( ) ;
293299 let quote_id = quote. id . clone ( ) ;
300+ let payment_success_sender = self . payment_success_sender . clone ( ) ;
294301 self . runtime . spawn ( async move {
295- match cashu_wallet. melt ( & quote_id) . await {
302+ let mut metadata = HashMap :: new ( ) ;
303+ if let Some ( hash) = & payment_hash {
304+ metadata. insert ( PAYMENT_HASH_METADATA_KEY . to_string ( ) , hash. to_string ( ) ) ;
305+ }
306+
307+ match cashu_wallet. melt_with_metadata ( & quote_id, metadata) . await {
296308 Ok ( res) => {
297309 match res. state {
298310 MeltQuoteState :: Paid => {
@@ -304,6 +316,8 @@ impl TrustedWalletInterface for Cashu {
304316 map. get ( & payment_id) . is_some_and ( |m| m. ty . is_rebalance ( ) )
305317 } ;
306318 if is_rebalance {
319+ // make sure we still send payment success
320+ payment_success_sender. send ( ( ) ) . unwrap ( ) ;
307321 return ;
308322 }
309323
@@ -369,6 +383,8 @@ impl TrustedWalletInterface for Cashu {
369383 payment_preimage,
370384 fee_paid_msat : Some ( fee_paid_sat * 1_000 ) , // convert to msats
371385 } ) ;
386+
387+ payment_success_sender. send ( ( ) ) . unwrap ( ) ;
372388 } ,
373389 MeltQuoteState :: Failed => {
374390 log_error ! ( logger, "Melt failed for quote: {quote_id}" ) ;
@@ -418,6 +434,35 @@ impl TrustedWalletInterface for Cashu {
418434 } )
419435 }
420436
437+ fn await_payment_success (
438+ & self , payment_hash : [ u8 ; 32 ] ,
439+ ) -> Pin < Box < dyn Future < Output = Option < ReceivedLightningPayment > > + Send + ' _ > > {
440+ Box :: pin ( async move {
441+ loop {
442+ let txs = self
443+ . cashu_wallet
444+ . list_transactions ( Some ( TransactionDirection :: Outgoing ) )
445+ . await
446+ . ok ( ) ?;
447+
448+ let hex = payment_hash. to_lower_hex_string ( ) ;
449+ let tx = txs. iter ( ) . find ( |tx| {
450+ tx. metadata . get ( PAYMENT_HASH_METADATA_KEY ) . is_some_and ( |h| h == & hex)
451+ } ) ;
452+
453+ if let Some ( tx) = tx {
454+ let payment_id = Self :: id_to_32_byte_array ( tx. quote_id . as_ref ( ) . expect ( "safe" ) ) ;
455+ return Some ( ReceivedLightningPayment {
456+ id : payment_id,
457+ fee_paid_msat : Some ( convert_amount ( tx. fee , & self . unit ) . ok ( ) ?. milli_sats ( ) ) ,
458+ } ) ;
459+ }
460+
461+ self . await_payment_success ( ) . await ;
462+ }
463+ } )
464+ }
465+
421466 fn stop ( & self ) -> Pin < Box < dyn Future < Output = ( ) > + Send + ' _ > > {
422467 Box :: pin ( async move {
423468 log_info ! ( self . logger, "Stopping Cashu wallet" ) ;
@@ -426,6 +471,8 @@ impl TrustedWalletInterface for Cashu {
426471 }
427472}
428473
474+ const PAYMENT_HASH_METADATA_KEY : & str = "payment_hash" ;
475+
429476impl Cashu {
430477 pub ( crate ) async fn init (
431478 config : & WalletConfig , cashu_config : CashuConfig , store : Arc < dyn KVStore + Sync + Send > ,
@@ -485,6 +532,7 @@ impl Cashu {
485532 . unwrap_or ( false ) ;
486533
487534 let ( shutdown_sender, mut shutdown_receiver) = watch:: channel :: < ( ) > ( ( ) ) ;
535+ let ( payment_success_sender, payment_success_flag) = watch:: channel ( ( ) ) ;
488536
489537 // Create channel for mint quote monitoring with bounded capacity
490538 let ( mint_quote_sender, mut mint_quote_receiver) = mpsc:: channel :: < MintQuote > ( 32 ) ;
@@ -554,6 +602,8 @@ impl Cashu {
554602 cashu_wallet,
555603 unit : cashu_config. unit ,
556604 shutdown_sender,
605+ payment_success_sender,
606+ payment_success_flag,
557607 logger,
558608 supports_bolt12,
559609 mint_quote_sender,
@@ -644,6 +694,12 @@ impl Cashu {
644694 }
645695 Ok ( ( ) )
646696 }
697+
698+ pub ( crate ) async fn await_payment_success ( & self ) {
699+ let mut flag = self . payment_success_flag . clone ( ) ;
700+ flag. mark_unchanged ( ) ;
701+ let _ = flag. changed ( ) . await ;
702+ }
647703}
648704
649705fn convert_amount ( cdk_amount : CdkAmount , unit : & CurrencyUnit ) -> Result < Amount , TrustedError > {
0 commit comments