11use crate :: error:: MutinyError ;
22use crate :: nodemanager:: NodeManager ;
33use crate :: nostr:: nwc:: {
4- NostrWalletConnect , NwcProfile , PendingNwcInvoice , Profile , PENDING_NWC_EVENTS_KEY ,
4+ NostrWalletConnect , NwcProfile , PendingNwcInvoice , Profile , SingleUseSpendingConditions ,
5+ SpendingConditions , PENDING_NWC_EVENTS_KEY ,
56} ;
67use crate :: storage:: MutinyStorage ;
78use bitcoin:: hashes:: sha256;
@@ -110,6 +111,7 @@ impl<S: MutinyStorage> NostrManager<S> {
110111 . read ( )
111112 . unwrap ( )
112113 . iter ( )
114+ . filter ( |x| !x. profile . archived )
113115 . map ( |x| x. nwc_profile ( ) )
114116 . collect ( )
115117 }
@@ -143,7 +145,7 @@ impl<S: MutinyStorage> NostrManager<S> {
143145 pub ( crate ) fn create_new_profile (
144146 & self ,
145147 profile_type : ProfileType ,
146- max_single_amt_sats : u64 ,
148+ spending_conditions : SpendingConditions ,
147149 ) -> Result < NwcProfile , MutinyError > {
148150 let mut profiles = self . nwc . write ( ) . unwrap ( ) ;
149151
@@ -166,10 +168,10 @@ impl<S: MutinyStorage> NostrManager<S> {
166168 let profile = Profile {
167169 name,
168170 index,
169- max_single_amt_sats,
170171 relay : "wss://nostr.mutinywallet.com" . to_string ( ) ,
171172 enabled : true ,
172- require_approval : true ,
173+ archived : false ,
174+ spending_conditions,
173175 } ;
174176 let nwc = NostrWalletConnect :: new ( & Secp256k1 :: new ( ) , self . xprivkey , profile) ?;
175177
@@ -193,9 +195,9 @@ impl<S: MutinyStorage> NostrManager<S> {
193195 pub async fn create_new_nwc_profile (
194196 & self ,
195197 profile_type : ProfileType ,
196- max_single_amt_sats : u64 ,
198+ spending_conditions : SpendingConditions ,
197199 ) -> Result < NwcProfile , MutinyError > {
198- let profile = self . create_new_profile ( profile_type, max_single_amt_sats ) ?;
200+ let profile = self . create_new_profile ( profile_type, spending_conditions ) ?;
199201
200202 let info_event = self . nwc . read ( ) . unwrap ( ) . iter ( ) . find_map ( |nwc| {
201203 if nwc. profile . index == profile. index {
@@ -227,6 +229,21 @@ impl<S: MutinyStorage> NostrManager<S> {
227229 Ok ( profile)
228230 }
229231
232+ pub async fn create_single_use_nwc (
233+ & self ,
234+ name : String ,
235+ amount_sats : u64 ,
236+ ) -> Result < NwcProfile , MutinyError > {
237+ let profile = ProfileType :: Normal { name } ;
238+
239+ let spending_conditions = SpendingConditions :: SingleUse ( SingleUseSpendingConditions {
240+ amount_sats,
241+ spent : false ,
242+ } ) ;
243+ self . create_new_nwc_profile ( profile, spending_conditions)
244+ . await
245+ }
246+
230247 /// Lists all pending NWC invoices
231248 pub fn get_pending_nwc_invoices ( & self ) -> Result < Vec < PendingNwcInvoice > , MutinyError > {
232249 Ok ( self
@@ -405,21 +422,86 @@ impl<S: MutinyStorage> NostrManager<S> {
405422 . cloned ( )
406423 } ;
407424
408- if let Some ( nwc) = nwc {
409- let event = nwc
425+ if let Some ( mut nwc) = nwc {
426+ let ( event, needs_save ) = nwc
410427 . handle_nwc_request (
411428 event,
412429 node_manager,
413430 from_node,
414431 self . pending_nwc_lock . deref ( ) ,
415432 )
416433 . await ?;
434+
435+ // update the profile if needed
436+ if needs_save {
437+ let mut vec = self . nwc . write ( ) . unwrap ( ) ;
438+
439+ // update the profile
440+ for item in vec. iter_mut ( ) {
441+ if item. profile . index == nwc. profile . index {
442+ item. profile = nwc. profile ;
443+ break ;
444+ }
445+ }
446+
447+ let profiles = vec. iter ( ) . map ( |x| x. profile . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
448+ drop ( vec) ; // drop the lock, no longer needed
449+
450+ self . storage . set_data ( NWC_STORAGE_KEY , profiles, None ) ?;
451+ }
452+
417453 Ok ( event)
418454 } else {
419455 Ok ( None )
420456 }
421457 }
422458
459+ pub async fn claim_single_use_nwc (
460+ & self ,
461+ amount_sats : u64 ,
462+ nwc_uri : & str ,
463+ node_manager : & NodeManager < S > ,
464+ ) -> anyhow:: Result < EventId > {
465+ let nwc = NostrWalletConnectURI :: from_str ( nwc_uri) ?;
466+ let secret = Keys :: new ( nwc. secret ) ;
467+ let client = Client :: new ( & secret) ;
468+
469+ #[ cfg( target_arch = "wasm32" ) ]
470+ let add_relay_res = client. add_relay ( nwc. relay_url . as_str ( ) ) . await ;
471+
472+ #[ cfg( not( target_arch = "wasm32" ) ) ]
473+ let add_relay_res = client. add_relay ( nwc. relay_url . as_str ( ) , None ) . await ;
474+
475+ add_relay_res. expect ( "Failed to add relays" ) ;
476+ client. connect ( ) . await ;
477+
478+ let invoice = node_manager
479+ . create_invoice ( Some ( amount_sats) , vec ! [ "Gift" . to_string( ) ] )
480+ . await ?;
481+
482+ let req = Request {
483+ method : Method :: PayInvoice ,
484+ params : RequestParams {
485+ invoice : invoice. bolt11 . unwrap ( ) . to_string ( ) ,
486+ } ,
487+ } ;
488+ let encrypted = encrypt ( & nwc. secret , & nwc. public_key , req. as_json ( ) ) ?;
489+ let p_tag = Tag :: PubKey ( nwc. public_key , None ) ;
490+ let request_event =
491+ EventBuilder :: new ( Kind :: WalletConnectRequest , encrypted, & [ p_tag] ) . to_event ( & secret) ?;
492+
493+ client
494+ . send_event ( request_event. clone ( ) )
495+ . await
496+ . map_err ( |e| {
497+ MutinyError :: Other ( anyhow:: anyhow!( "Failed to send request event: {e:?}" ) )
498+ } ) ?;
499+
500+ client. disconnect ( ) . await ?;
501+
502+ Ok ( request_event. id )
503+ }
504+
423505 /// Derives the client and server keys for Nostr Wallet Connect given a profile index
424506 /// The left key is the client key and the right key is the server key
425507 pub ( crate ) fn derive_nwc_keys < C : Signing > (
@@ -524,24 +606,21 @@ mod test {
524606 let nostr_manager = create_nostr_manager ( ) ;
525607
526608 let name = "test" . to_string ( ) ;
527- let max_single_amt_sats = 1_000 ;
528609
529610 let profile = nostr_manager
530611 . create_new_profile (
531612 ProfileType :: Normal { name : name. clone ( ) } ,
532- max_single_amt_sats ,
613+ SpendingConditions :: default ( ) ,
533614 )
534615 . unwrap ( ) ;
535616
536617 assert_eq ! ( profile. name, name) ;
537618 assert_eq ! ( profile. index, 1000 ) ;
538- assert_eq ! ( profile. max_single_amt_sats, max_single_amt_sats) ;
539619
540620 let profiles = nostr_manager. profiles ( ) ;
541621 assert_eq ! ( profiles. len( ) , 1 ) ;
542622 assert_eq ! ( profiles[ 0 ] . name, name) ;
543623 assert_eq ! ( profiles[ 0 ] . index, 1000 ) ;
544- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
545624
546625 let profiles: Vec < Profile > = nostr_manager
547626 . storage
@@ -552,32 +631,28 @@ mod test {
552631 assert_eq ! ( profiles. len( ) , 1 ) ;
553632 assert_eq ! ( profiles[ 0 ] . name, name) ;
554633 assert_eq ! ( profiles[ 0 ] . index, 1000 ) ;
555- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
556634 }
557635
558636 #[ test]
559637 fn test_create_reserve_profile ( ) {
560638 let nostr_manager = create_nostr_manager ( ) ;
561639
562640 let name = "Mutiny+ Subscription" . to_string ( ) ;
563- let max_single_amt_sats = 1_000 ;
564641
565642 let profile = nostr_manager
566643 . create_new_profile (
567644 ProfileType :: Reserved ( ReservedProfile :: MutinySubscription ) ,
568- max_single_amt_sats ,
645+ SpendingConditions :: default ( ) ,
569646 )
570647 . unwrap ( ) ;
571648
572649 assert_eq ! ( profile. name, name) ;
573650 assert_eq ! ( profile. index, 0 ) ;
574- assert_eq ! ( profile. max_single_amt_sats, max_single_amt_sats) ;
575651
576652 let profiles = nostr_manager. profiles ( ) ;
577653 assert_eq ! ( profiles. len( ) , 1 ) ;
578654 assert_eq ! ( profiles[ 0 ] . name, name) ;
579655 assert_eq ! ( profiles[ 0 ] . index, 0 ) ;
580- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
581656
582657 let profiles: Vec < Profile > = nostr_manager
583658 . storage
@@ -588,42 +663,37 @@ mod test {
588663 assert_eq ! ( profiles. len( ) , 1 ) ;
589664 assert_eq ! ( profiles[ 0 ] . name, name) ;
590665 assert_eq ! ( profiles[ 0 ] . index, 0 ) ;
591- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
592666
593667 // now create normal profile
594668 let name = "test" . to_string ( ) ;
595- let max_single_amt_sats = 1_000 ;
596669
597670 let profile = nostr_manager
598671 . create_new_profile (
599672 ProfileType :: Normal { name : name. clone ( ) } ,
600- max_single_amt_sats ,
673+ SpendingConditions :: default ( ) ,
601674 )
602675 . unwrap ( ) ;
603676
604677 assert_eq ! ( profile. name, name) ;
605678 assert_eq ! ( profile. index, 1000 ) ;
606- assert_eq ! ( profile. max_single_amt_sats, max_single_amt_sats) ;
607679 }
608680
609681 #[ test]
610682 fn test_edit_profile ( ) {
611683 let nostr_manager = create_nostr_manager ( ) ;
612684
613685 let name = "test" . to_string ( ) ;
614- let max_single_amt_sats = 1_000 ;
615686
616687 let mut profile = nostr_manager
617688 . create_new_profile (
618689 ProfileType :: Normal { name : name. clone ( ) } ,
619- max_single_amt_sats ,
690+ SpendingConditions :: default ( ) ,
620691 )
621692 . unwrap ( ) ;
622693
623694 assert_eq ! ( profile. name, name) ;
624695 assert_eq ! ( profile. index, 1000 ) ;
625696 assert ! ( profile. enabled) ;
626- assert_eq ! ( profile. max_single_amt_sats, max_single_amt_sats) ;
627697
628698 profile. enabled = false ;
629699
@@ -634,7 +704,6 @@ mod test {
634704 assert_eq ! ( profiles[ 0 ] . name, name) ;
635705 assert_eq ! ( profiles[ 0 ] . index, 1000 ) ;
636706 assert ! ( !profiles[ 0 ] . enabled) ;
637- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
638707
639708 let profiles: Vec < Profile > = nostr_manager
640709 . storage
@@ -646,18 +715,16 @@ mod test {
646715 assert_eq ! ( profiles[ 0 ] . name, name) ;
647716 assert_eq ! ( profiles[ 0 ] . index, 1000 ) ;
648717 assert ! ( !profiles[ 0 ] . enabled) ;
649- assert_eq ! ( profiles[ 0 ] . max_single_amt_sats, max_single_amt_sats) ;
650718 }
651719
652720 #[ test]
653721 fn test_deny_invoice ( ) {
654722 let nostr_manager = create_nostr_manager ( ) ;
655723
656724 let name = "test" . to_string ( ) ;
657- let max_single_amt_sats = 1_000 ;
658725
659726 let profile = nostr_manager
660- . create_new_profile ( ProfileType :: Normal { name } , max_single_amt_sats )
727+ . create_new_profile ( ProfileType :: Normal { name } , SpendingConditions :: default ( ) )
661728 . unwrap ( ) ;
662729
663730 let inv = PendingNwcInvoice {
0 commit comments