| 
					
				 | 
			
			
				@@ -0,0 +1,177 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use std::io::{self, Write}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use anyhow::{anyhow, Result}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use cdk::{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    amount::SplitTarget, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    nuts::{nut18::TransportType, PaymentRequest, PaymentRequestPayload}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    wallet::{MultiMintWallet, SendKind}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use clap::Args; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use nostr_sdk::{nips::nip19::Nip19Profile, Client as NostrClient, EventBuilder, FromBech32, Keys}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use reqwest::Client; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#[derive(Args)] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pub struct PayRequestSubCommand { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    payment_request: PaymentRequest, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pub async fn pay_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    multi_mint_wallet: &MultiMintWallet, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sub_command_args: &PayRequestSubCommand, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) -> Result<()> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let payment_request = &sub_command_args.payment_request; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let unit = payment_request.unit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let amount = match payment_request.amount { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Some(amount) => amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        None => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            println!("Enter the amount you would like to pay"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut user_input = String::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let stdin = io::stdin(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            io::stdout().flush().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stdin.read_line(&mut user_input)?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let amount: u64 = user_input.trim().parse()?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            amount.into() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let request_mints = &payment_request.mints; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let wallet_mints = multi_mint_wallet.get_wallets().await; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Wallets where unit, balance and mint match request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let mut matching_wallets = vec![]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for wallet in wallet_mints.iter() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let balance = wallet.total_balance().await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if let Some(request_mints) = request_mints { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if !request_mints.contains(&wallet.mint_url) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if let Some(unit) = unit { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if wallet.unit != unit { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if balance >= amount { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            matching_wallets.push(wallet); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let matching_wallet = matching_wallets.first().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // We prefer nostr transport if it is available to hide ip. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let transport = payment_request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .transports 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .find(|t| t._type == TransportType::Nostr) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .or_else(|| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            payment_request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .transports 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .find(|t| t._type == TransportType::HttpPost) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .ok_or(anyhow!("No supported transport method found"))?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let proofs = matching_wallet 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .send( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            &SplitTarget::default(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            &SendKind::default(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .proofs() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .get(&matching_wallet.mint_url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .unwrap() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .clone(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let payload = PaymentRequestPayload { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        id: payment_request.payment_id.clone(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        memo: None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        mint: matching_wallet.mint_url.clone(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        unit: matching_wallet.unit, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        proofs, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    match transport._type { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        TransportType::Nostr => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let keys = Keys::generate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let client = NostrClient::new(keys); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let nprofile = Nip19Profile::from_bech32(&transport.target)?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            println!("{:?}", nprofile.relays); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let rumor = EventBuilder::new( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nostr_sdk::Kind::from_u16(14), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                serde_json::to_string(&payload)?, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                [], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let relays = nprofile.relays; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for relay in relays.iter() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                client.add_write_relay(relay).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            client.connect().await; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let gift_wrap = client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .gift_wrap_to(relays, &nprofile.public_key, rumor, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            println!( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "Published event {} succufully to {}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                gift_wrap.val, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                gift_wrap 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .success 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .map(|s| s.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .collect::<Vec<_>>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    .join(", ") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if !gift_wrap.failed.is_empty() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                println!( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    "Could not publish to {:?}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    gift_wrap 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .failed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .keys() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .map(|relay| relay.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .collect::<Vec<_>>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        .join(", ") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        TransportType::HttpPost => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let client = Client::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let res = client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .post(transport.target.clone()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .json(&payload) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .send() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let status = res.status(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if status.is_success() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                println!("Successfully posted payment"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                println!("{:?}", res); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                println!("Error posting payment"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Ok(()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |