nutshell_wallet.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. use std::time::Duration;
  2. use cdk_fake_wallet::create_fake_invoice;
  3. use serde::{Deserialize, Serialize};
  4. use serde_json::Value;
  5. use tokio::time::sleep;
  6. /// Response from the invoice creation endpoint
  7. #[derive(Debug, Serialize, Deserialize)]
  8. struct InvoiceResponse {
  9. payment_request: String,
  10. checking_id: Option<String>,
  11. }
  12. /// Maximum number of attempts to check invoice payment status
  13. const MAX_PAYMENT_CHECK_ATTEMPTS: u8 = 20;
  14. /// Delay between payment status checks in milliseconds
  15. const PAYMENT_CHECK_DELAY_MS: u64 = 500;
  16. /// Default test amount in satoshis
  17. const DEFAULT_TEST_AMOUNT: u64 = 10000;
  18. /// Helper function to mint tokens via Lightning invoice
  19. async fn mint_tokens(base_url: &str, amount: u64) -> String {
  20. // Create an invoice for the specified amount
  21. let invoice_url = format!("{}/lightning/create_invoice?amount={}", base_url, amount);
  22. let invoice_response: InvoiceResponse = bitreq::post(&invoice_url)
  23. .send_async()
  24. .await
  25. .expect("Failed to send invoice creation request")
  26. .json()
  27. .expect("Failed to parse invoice response");
  28. println!("Created invoice: {}", invoice_response.payment_request);
  29. invoice_response.payment_request
  30. }
  31. /// Helper function to wait for payment confirmation
  32. async fn wait_for_payment_confirmation(base_url: &str, payment_request: &str) {
  33. let check_url = format!(
  34. "{}/lightning/invoice_state?payment_request={}",
  35. base_url, payment_request
  36. );
  37. let mut payment_confirmed = false;
  38. for attempt in 1..=MAX_PAYMENT_CHECK_ATTEMPTS {
  39. println!(
  40. "Checking invoice state (attempt {}/{})...",
  41. attempt, MAX_PAYMENT_CHECK_ATTEMPTS
  42. );
  43. let response = bitreq::get(&check_url)
  44. .send_async()
  45. .await
  46. .expect("Failed to send payment check request");
  47. if response.status_code == 200 {
  48. let state: Value = response
  49. .json()
  50. .expect("Failed to parse payment state response");
  51. println!("Payment state: {:?}", state);
  52. if let Some(result) = state.get("result") {
  53. if result == 1 {
  54. payment_confirmed = true;
  55. break;
  56. }
  57. }
  58. } else {
  59. println!("Failed to check payment state: {}", response.status_code);
  60. }
  61. sleep(Duration::from_millis(PAYMENT_CHECK_DELAY_MS)).await;
  62. }
  63. if !payment_confirmed {
  64. panic!("Payment not confirmed after maximum attempts");
  65. }
  66. }
  67. /// Helper function to get the current wallet balance
  68. async fn get_wallet_balance(base_url: &str) -> u64 {
  69. let balance_url = format!("{}/balance", base_url);
  70. let balance: Value = bitreq::get(&balance_url)
  71. .send_async()
  72. .await
  73. .expect("Failed to send balance request")
  74. .json()
  75. .expect("Failed to parse balance response");
  76. balance["balance"]
  77. .as_u64()
  78. .expect("Could not parse balance as u64")
  79. }
  80. /// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
  81. #[tokio::test]
  82. async fn test_nutshell_wallet_mint() {
  83. // Get the wallet URL from environment variable
  84. let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
  85. // Step 1: Create an invoice and mint tokens
  86. let amount = DEFAULT_TEST_AMOUNT;
  87. let payment_request = mint_tokens(&base_url, amount).await;
  88. // Step 2: Wait for the invoice to be paid
  89. wait_for_payment_confirmation(&base_url, &payment_request).await;
  90. // Step 3: Check the wallet balance
  91. let available_balance = get_wallet_balance(&base_url).await;
  92. // Verify the balance is at least the amount we minted
  93. assert!(
  94. available_balance >= amount,
  95. "Balance should be at least {} but was {}",
  96. amount,
  97. available_balance
  98. );
  99. }
  100. /// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
  101. #[tokio::test]
  102. async fn test_nutshell_wallet_swap() {
  103. // Get the wallet URL from environment variable
  104. let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
  105. // Step 1: Create an invoice and mint tokens
  106. let amount = DEFAULT_TEST_AMOUNT;
  107. let payment_request = mint_tokens(&base_url, amount).await;
  108. // Step 2: Wait for the invoice to be paid
  109. wait_for_payment_confirmation(&base_url, &payment_request).await;
  110. let send_amount = 100;
  111. let send_url = format!("{}/send?amount={}", base_url, send_amount);
  112. let response: Value = bitreq::post(&send_url)
  113. .send_async()
  114. .await
  115. .expect("Failed to send payment check request")
  116. .json()
  117. .expect("Valid json");
  118. // Extract the token and remove the surrounding quotes
  119. let token_with_quotes = response
  120. .get("token")
  121. .expect("Missing token")
  122. .as_str()
  123. .expect("Token is not a string");
  124. let token = token_with_quotes.trim_matches('"');
  125. let receive_url = format!("{}/receive?token={}", base_url, token);
  126. let response: Value = bitreq::post(&receive_url)
  127. .send_async()
  128. .await
  129. .expect("Failed to receive request")
  130. .json()
  131. .expect("Valid json");
  132. let balance = response
  133. .get("balance")
  134. .expect("Bal in response")
  135. .as_u64()
  136. .expect("Valid num");
  137. let initial_balance = response
  138. .get("initial_balance")
  139. .expect("Bal in response")
  140. .as_u64()
  141. .expect("Valid num");
  142. let token_received = balance - initial_balance;
  143. let fee = 1;
  144. assert_eq!(token_received, send_amount - fee);
  145. }
  146. /// Test the Nutshell wallet's ability to melt tokens to pay a Lightning invoice
  147. #[tokio::test]
  148. async fn test_nutshell_wallet_melt() {
  149. // Get the wallet URL from environment variable
  150. let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
  151. // Step 1: Create an invoice and mint tokens
  152. let amount = DEFAULT_TEST_AMOUNT;
  153. let payment_request = mint_tokens(&base_url, amount).await;
  154. // Step 2: Wait for the invoice to be paid
  155. wait_for_payment_confirmation(&base_url, &payment_request).await;
  156. // Get initial balance
  157. let initial_balance = get_wallet_balance(&base_url).await;
  158. println!("Initial balance: {}", initial_balance);
  159. // Step 3: Create a fake invoice to pay
  160. let payment_amount = 1000; // 1000 sats
  161. let fake_invoice = create_fake_invoice(payment_amount, "Test payment".to_string());
  162. let pay_url = format!("{}/lightning/pay_invoice?bolt11={}", base_url, fake_invoice);
  163. // Step 4: Pay the invoice
  164. let _response: Value = bitreq::post(&pay_url)
  165. .send_async()
  166. .await
  167. .expect("Failed to send pay request")
  168. .json()
  169. .expect("Failed to parse pay response");
  170. let final_balance = get_wallet_balance(&base_url).await;
  171. println!("Final balance: {}", final_balance);
  172. assert!(
  173. initial_balance > final_balance,
  174. "Balance should decrease after payment"
  175. );
  176. let balance_difference = initial_balance - final_balance;
  177. println!("Balance decreased by: {}", balance_difference);
  178. // The balance difference should be at least the payment amount
  179. assert!(
  180. balance_difference >= (payment_amount / 1000),
  181. "Balance should decrease by at least the payment amount ({}) but decreased by {}",
  182. payment_amount,
  183. balance_difference
  184. );
  185. }