melt-token.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #![allow(missing_docs)]
  2. use std::sync::Arc;
  3. use std::time::Duration;
  4. use bitcoin::hashes::{sha256, Hash};
  5. use bitcoin::hex::prelude::FromHex;
  6. use bitcoin::secp256k1::Secp256k1;
  7. use cdk::error::Error;
  8. use cdk::nuts::{CurrencyUnit, PaymentMethod, SecretKey};
  9. use cdk::wallet::{MeltOutcome, Wallet, WalletTrait};
  10. use cdk::Amount;
  11. use cdk_sqlite::wallet::memory;
  12. use lightning_invoice::{Currency, InvoiceBuilder, PaymentSecret};
  13. use rand::Rng;
  14. #[tokio::main]
  15. async fn main() -> Result<(), Error> {
  16. // Initialize the memory store for the wallet
  17. let localstore = memory::empty().await?;
  18. // Generate a random seed for the wallet
  19. let seed = rand::rng().random::<[u8; 64]>();
  20. // Define the mint URL and currency unit
  21. let mint_url = "https://fake.thesimplekid.dev";
  22. let unit = CurrencyUnit::Sat;
  23. let amount = Amount::from(20);
  24. // Create a new wallet
  25. let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None)?;
  26. // Mint enough tokens for both examples
  27. let quote = wallet
  28. .mint_quote(PaymentMethod::BOLT11, Some(amount), None, None)
  29. .await?;
  30. let _proofs = wallet
  31. .wait_and_mint_quote(
  32. quote,
  33. Default::default(),
  34. Default::default(),
  35. Duration::from_secs(10),
  36. )
  37. .await?;
  38. let balance = wallet.total_balance().await?;
  39. println!("Minted {} sats from {}", balance, mint_url);
  40. // Helper to create a test invoice
  41. let create_test_invoice = |amount_msats: u64, description: &str| {
  42. let private_key = SecretKey::from_slice(
  43. &<[u8; 32]>::from_hex(
  44. "e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734",
  45. )
  46. .unwrap(),
  47. )
  48. .unwrap();
  49. let random_bytes = rand::rng().random::<[u8; 32]>();
  50. let payment_hash = sha256::Hash::from_slice(&random_bytes).unwrap();
  51. let payment_secret = PaymentSecret([42u8; 32]);
  52. InvoiceBuilder::new(Currency::Bitcoin)
  53. .amount_milli_satoshis(amount_msats)
  54. .description(description.into())
  55. .payment_hash(payment_hash)
  56. .payment_secret(payment_secret)
  57. .current_timestamp()
  58. .min_final_cltv_expiry_delta(144)
  59. .build_signed(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))
  60. .unwrap()
  61. .to_string()
  62. };
  63. println!("\n=== Example 1: Synchronous Confirm ===");
  64. println!("This approach blocks until the payment completes.");
  65. println!("Use this when you need to wait for completion before continuing.");
  66. // Create first melt quote
  67. let invoice1 = create_test_invoice(5 * 1000, "Sync melt example");
  68. let melt_quote1 = wallet
  69. .melt_quote(PaymentMethod::BOLT11, invoice1, None, None)
  70. .await?;
  71. println!(
  72. "Melt quote 1: {} sats, fee reserve: {:?}",
  73. melt_quote1.amount, melt_quote1.fee_reserve
  74. );
  75. // Prepare and confirm synchronously
  76. let prepared1 = wallet
  77. .prepare_melt(&melt_quote1.id, std::collections::HashMap::new())
  78. .await?;
  79. println!(
  80. "Prepared melt - Amount: {}, Total Fee: {}",
  81. prepared1.amount(),
  82. prepared1.total_fee()
  83. );
  84. let confirmed1 = prepared1.confirm().await?;
  85. println!(
  86. "Sync melt completed: state={:?}, amount={}, fee_paid={}",
  87. confirmed1.state(),
  88. confirmed1.amount(),
  89. confirmed1.fee_paid()
  90. );
  91. println!("\n=== Example 2: Async Confirm ===");
  92. println!(
  93. "This approach sends the request with async preference and waits for the mint's response."
  94. );
  95. println!(
  96. "If the mint supports async payments, it may return Pending quickly without waiting for"
  97. );
  98. println!("the payment to complete. If not, it may block until the payment completes.");
  99. // Create second melt quote
  100. let invoice2 = create_test_invoice(5 * 1000, "Async melt example");
  101. let melt_quote2 = wallet
  102. .melt_quote(PaymentMethod::BOLT11, invoice2, None, None)
  103. .await?;
  104. println!(
  105. "Melt quote 2: {} sats, fee reserve: {:?}",
  106. melt_quote2.amount, melt_quote2.fee_reserve
  107. );
  108. // Prepare and confirm asynchronously
  109. let prepared2 = wallet
  110. .prepare_melt(&melt_quote2.id, std::collections::HashMap::new())
  111. .await?;
  112. println!(
  113. "Prepared melt - Amount: {}, Total Fee: {}",
  114. prepared2.amount(),
  115. prepared2.total_fee()
  116. );
  117. // confirm_prefer_async waits for the mint's response, which may be quick if async is supported
  118. let result = prepared2.confirm_prefer_async().await?;
  119. match result {
  120. MeltOutcome::Paid(finalized) => {
  121. println!(
  122. "Async melt completed immediately: state={:?}, amount={}, fee_paid={}",
  123. finalized.state(),
  124. finalized.amount(),
  125. finalized.fee_paid()
  126. );
  127. }
  128. MeltOutcome::Pending(pending) => {
  129. println!("Melt is pending, waiting for completion via WebSocket...");
  130. // You can either await the pending melt directly:
  131. let finalized = pending.await?;
  132. println!(
  133. "Async melt completed after waiting: state={:?}, amount={}, fee_paid={}",
  134. finalized.state(),
  135. finalized.amount(),
  136. finalized.fee_paid()
  137. );
  138. // Alternative: Instead of awaiting, you could:
  139. // 1. Store the quote ID and check status later with:
  140. // wallet.check_melt_quote_status(&melt_quote2.id).await?
  141. // 2. Let the wallet's background task handle it via:
  142. // wallet.finalize_pending_melts().await?
  143. }
  144. }
  145. let final_balance = wallet.total_balance().await?;
  146. println!("\nFinal balance: {} sats", final_balance);
  147. Ok(())
  148. }