revoke_send.rs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #![allow(missing_docs)]
  2. use std::str::FromStr;
  3. use std::sync::Arc;
  4. use std::time::Duration;
  5. use bip39::Mnemonic;
  6. use cdk::amount::SplitTarget;
  7. use cdk::mint_url::MintUrl;
  8. use cdk::nuts::nut00::KnownMethod;
  9. use cdk::nuts::{CurrencyUnit, PaymentMethod};
  10. use cdk::wallet::{ReceiveOptions, SendOptions, WalletRepositoryBuilder};
  11. use cdk::Amount;
  12. use cdk_sqlite::wallet::memory;
  13. /// This example demonstrates the ability to revoke a send operation.
  14. ///
  15. /// It shows:
  16. /// - Funding a wallet
  17. /// - Creating a send (generating a token)
  18. /// - Viewing pending sends
  19. /// - Checking send status
  20. /// - Revoking the send (reclaiming funds)
  21. #[tokio::main]
  22. async fn main() -> Result<(), Box<dyn std::error::Error>> {
  23. // Configuration
  24. let mint_url = MintUrl::from_str("https://fake.thesimplekid.dev")?;
  25. let unit = CurrencyUnit::Sat;
  26. // Generate a seed
  27. let mnemonic = Mnemonic::generate(12)?;
  28. let seed = mnemonic.to_seed_normalized("");
  29. println!("Generated mnemonic: {}", mnemonic);
  30. // Create the WalletRepository
  31. let localstore = Arc::new(memory::empty().await?);
  32. let wallet = WalletRepositoryBuilder::new()
  33. .localstore(localstore)
  34. .seed(seed)
  35. .build()
  36. .await?;
  37. println!("Created WalletRepository");
  38. // Add a mint to the wallet
  39. wallet.add_wallet(mint_url.clone()).await?;
  40. println!("Added mint: {}", mint_url);
  41. // Get the wallet for this mint
  42. let mint_wallet = wallet
  43. .create_wallet(mint_url.clone(), unit.clone(), None)
  44. .await?;
  45. // ========================================
  46. // 1. FUND: Mint some tokens to start
  47. // ========================================
  48. let mint_amount = Amount::from(100);
  49. println!("\n--- 1. FUNDING WALLET ---");
  50. println!("Minting {} sats...", mint_amount);
  51. let mint_quote = mint_wallet
  52. .mint_quote(
  53. PaymentMethod::Known(KnownMethod::Bolt11),
  54. Some(mint_amount),
  55. None,
  56. None,
  57. )
  58. .await?;
  59. // Wait for quote to be paid (automatic with fake mint)
  60. let _proofs = mint_wallet
  61. .wait_and_mint_quote(
  62. mint_quote.clone(),
  63. SplitTarget::default(),
  64. None,
  65. Duration::from_secs(60),
  66. )
  67. .await?;
  68. let balances = wallet.total_balance().await?;
  69. let balance = balances
  70. .get(&CurrencyUnit::Sat)
  71. .copied()
  72. .unwrap_or(Amount::ZERO);
  73. println!("Wallet funded. Balance: {} sats", balance);
  74. // ========================================
  75. // 2. SEND: Create a token
  76. // ========================================
  77. let send_amount = Amount::from(50);
  78. println!("\n--- 2. CREATING SEND ---");
  79. println!("Preparing to send {} sats...", send_amount);
  80. // Prepare and confirm the send
  81. let prepared_send = mint_wallet
  82. .prepare_send(send_amount, SendOptions::default())
  83. .await?;
  84. let operation_id = prepared_send.operation_id();
  85. let token = prepared_send.confirm(None).await?;
  86. println!("Token created (Send Operation ID: {})", operation_id);
  87. println!("Token: {}", token);
  88. let balances_after_send = wallet.total_balance().await?;
  89. let balance_after_send = balances_after_send
  90. .get(&CurrencyUnit::Sat)
  91. .copied()
  92. .unwrap_or(Amount::ZERO);
  93. println!("Balance after send: {} sats", balance_after_send);
  94. // ========================================
  95. // 3. INSPECT: Check pending status
  96. // ========================================
  97. println!("\n--- 3. INSPECTING STATUS ---");
  98. // Get all pending sends
  99. let pending_sends = mint_wallet.get_pending_sends().await?;
  100. println!("Pending sends count: {}", pending_sends.len());
  101. for id in &pending_sends {
  102. println!("- ID: {}", id);
  103. }
  104. // Check specific status
  105. let claimed = mint_wallet.check_send_status(operation_id).await?;
  106. println!("Is token claimed? {}", claimed);
  107. if !claimed {
  108. println!("Token is unclaimed. Revocation possible.");
  109. } else {
  110. println!("Token already claimed. Cannot revoke.");
  111. return Ok(());
  112. }
  113. // ========================================
  114. // 4. REVOKE: Reclaim the funds
  115. // ========================================
  116. println!("\n--- 4. REVOKING SEND ---");
  117. println!("Revoking operation {}...", operation_id);
  118. let reclaimed_amount = mint_wallet.revoke_send(operation_id).await?;
  119. println!("Reclaimed {} sats", reclaimed_amount);
  120. // ========================================
  121. // 5. VERIFY: Check final state
  122. // ========================================
  123. println!("\n--- 5. VERIFYING STATE ---");
  124. // Check pending sends again
  125. let pending_after = mint_wallet.get_pending_sends().await?;
  126. println!("Pending sends after revocation: {}", pending_after.len());
  127. // Check final balance
  128. let final_balances = wallet.total_balance().await?;
  129. let final_balance = final_balances
  130. .get(&CurrencyUnit::Sat)
  131. .copied()
  132. .unwrap_or(Amount::ZERO);
  133. println!("Final balance: {} sats", final_balance);
  134. if final_balance > balance_after_send {
  135. println!("SUCCESS: Funds restored!");
  136. } else {
  137. println!("WARNING: Balance did not increase.");
  138. }
  139. // Note on fees
  140. if final_balance < mint_amount {
  141. println!("(Note: Final balance may be slightly less than original due to mint fees)");
  142. }
  143. // ========================================
  144. // 6. FINALIZE: Send and Claim (Happy Path)
  145. // ========================================
  146. println!("\n--- 6. SEND AND FINALIZE (Happy Path) ---");
  147. let send_amount_2 = Amount::from(20);
  148. println!("Sending {} sats to be claimed...", send_amount_2);
  149. // Create a new send
  150. let prepared_send_2 = mint_wallet
  151. .prepare_send(send_amount_2, SendOptions::default())
  152. .await?;
  153. let operation_id_2 = prepared_send_2.operation_id();
  154. let token_2 = prepared_send_2.confirm(None).await?;
  155. println!("Token created: {}", token_2);
  156. // Create a receiver wallet
  157. println!("Creating receiver wallet...");
  158. let receiver_seed = Mnemonic::generate(12)?.to_seed_normalized("");
  159. let receiver_store = Arc::new(memory::empty().await?);
  160. let receiver_wallet = WalletRepositoryBuilder::new()
  161. .localstore(receiver_store)
  162. .seed(receiver_seed)
  163. .build()
  164. .await?;
  165. receiver_wallet.add_wallet(mint_url.clone()).await?;
  166. let receiver_mint_wallet = receiver_wallet
  167. .create_wallet(mint_url.clone(), unit, None)
  168. .await?;
  169. // Receiver claims the token
  170. println!("Receiver claiming token...");
  171. let received_amount = receiver_mint_wallet
  172. .receive(&token_2.to_string(), ReceiveOptions::default())
  173. .await?;
  174. println!("Receiver got {} sats", received_amount);
  175. // Check status from sender side
  176. println!("Checking status from sender...");
  177. let claimed_2 = mint_wallet.check_send_status(operation_id_2).await?;
  178. println!("Is token claimed? {}", claimed_2);
  179. if claimed_2 {
  180. println!("Token confirmed as claimed.");
  181. } else {
  182. println!("WARNING: Token should be claimed but status says false.");
  183. }
  184. // Verify pending sends is empty
  185. let pending_final = mint_wallet.get_pending_sends().await?;
  186. println!("Pending sends count: {}", pending_final.len());
  187. if pending_final.is_empty() {
  188. println!("SUCCESS: Saga finalized and removed from pending.");
  189. } else {
  190. println!("WARNING: Pending sends not empty.");
  191. }
  192. Ok(())
  193. }