Просмотр исходного кода

feat(multi-mint-wallet): add human-readable address melt quote support (#1325)

Add three new methods to MultiMintWallet for creating melt quotes from human-readable addresses:
- melt_bip353_quote: resolves BIP353 addresses to Lightning offers
- melt_lightning_address_quote: resolves Lightning addresses to invoices
- melt_human_readable_quote: intelligently tries BIP353 or Lightning address based on mint Bolt12 support
tsk 1 месяц назад
Родитель
Сommit
4cfc29532a
2 измененных файлов с 174 добавлено и 0 удалено
  1. 82 0
      crates/cdk-ffi/src/multi_mint_wallet.rs
  2. 92 0
      crates/cdk/src/wallet/multi_mint_wallet.rs

+ 82 - 0
crates/cdk-ffi/src/multi_mint_wallet.rs

@@ -349,6 +349,88 @@ impl MultiMintWallet {
         Ok(quote.into())
     }
 
+    /// Get a melt quote for a BIP353 human-readable address
+    ///
+    /// This method resolves a BIP353 address (e.g., "alice@example.com") to a Lightning offer
+    /// and then creates a melt quote for that offer at the specified mint.
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `bip353_address` - Human-readable address in the format "user@domain.com"
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn melt_bip353_quote(
+        &self,
+        mint_url: MintUrl,
+        bip353_address: String,
+        amount_msat: u64,
+    ) -> Result<MeltQuote, FfiError> {
+        let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
+        let cdk_amount = cdk::Amount::from(amount_msat);
+        let quote = self
+            .inner
+            .melt_bip353_quote(&cdk_mint_url, &bip353_address, cdk_amount)
+            .await?;
+        Ok(quote.into())
+    }
+
+    /// Get a melt quote for a Lightning address
+    ///
+    /// This method resolves a Lightning address (e.g., "alice@example.com") to a Lightning invoice
+    /// and then creates a melt quote for that invoice at the specified mint.
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `lightning_address` - Lightning address in the format "user@domain.com"
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    pub async fn melt_lightning_address_quote(
+        &self,
+        mint_url: MintUrl,
+        lightning_address: String,
+        amount_msat: u64,
+    ) -> Result<MeltQuote, FfiError> {
+        let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
+        let cdk_amount = cdk::Amount::from(amount_msat);
+        let quote = self
+            .inner
+            .melt_lightning_address_quote(&cdk_mint_url, &lightning_address, cdk_amount)
+            .await?;
+        Ok(quote.into())
+    }
+
+    /// Get a melt quote for a human-readable address
+    ///
+    /// This method accepts a human-readable address that could be either a BIP353 address
+    /// or a Lightning address. It intelligently determines which to try based on mint support:
+    ///
+    /// 1. If the mint supports Bolt12, it tries BIP353 first
+    /// 2. Falls back to Lightning address only if BIP353 DNS resolution fails
+    /// 3. If BIP353 resolves but fails at the mint, it does NOT fall back to Lightning address
+    /// 4. If the mint doesn't support Bolt12, it tries Lightning address directly
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `address` - Human-readable address (BIP353 or Lightning address)
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    #[cfg(not(target_arch = "wasm32"))]
+    pub async fn melt_human_readable_quote(
+        &self,
+        mint_url: MintUrl,
+        address: String,
+        amount_msat: u64,
+    ) -> Result<MeltQuote, FfiError> {
+        let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
+        let cdk_amount = cdk::Amount::from(amount_msat);
+        let quote = self
+            .inner
+            .melt_human_readable_quote(&cdk_mint_url, &address, cdk_amount)
+            .await?;
+        Ok(quote.into())
+    }
+
     /// Melt tokens (pay a bolt11 invoice)
     pub async fn melt(
         &self,

+ 92 - 0
crates/cdk/src/wallet/multi_mint_wallet.rs

@@ -1656,6 +1656,98 @@ impl MultiMintWallet {
 
         wallet.fetch_mint_info().await
     }
+
+    /// Melt Quote for BIP353 human-readable address
+    ///
+    /// This method resolves a BIP353 address (e.g., "alice@example.com") to a Lightning offer
+    /// and then creates a melt quote for that offer at the specified mint.
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `bip353_address` - Human-readable address in the format "user@domain.com"
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    ///
+    /// # Returns
+    ///
+    /// A `MeltQuote` that can be used to execute the payment
+    #[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
+    #[instrument(skip(self, amount_msat))]
+    pub async fn melt_bip353_quote(
+        &self,
+        mint_url: &MintUrl,
+        bip353_address: &str,
+        amount_msat: impl Into<Amount>,
+    ) -> Result<crate::wallet::types::MeltQuote, Error> {
+        let wallets = self.wallets.read().await;
+        let wallet = wallets.get(mint_url).ok_or(Error::UnknownMint {
+            mint_url: mint_url.to_string(),
+        })?;
+
+        wallet.melt_bip353_quote(bip353_address, amount_msat).await
+    }
+
+    /// Melt Quote for Lightning address
+    ///
+    /// This method resolves a Lightning address (e.g., "alice@example.com") to a Lightning invoice
+    /// and then creates a melt quote for that invoice at the specified mint.
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `lightning_address` - Lightning address in the format "user@domain.com"
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    ///
+    /// # Returns
+    ///
+    /// A `MeltQuote` that can be used to execute the payment
+    #[instrument(skip(self, amount_msat))]
+    pub async fn melt_lightning_address_quote(
+        &self,
+        mint_url: &MintUrl,
+        lightning_address: &str,
+        amount_msat: impl Into<Amount>,
+    ) -> Result<crate::wallet::types::MeltQuote, Error> {
+        let wallets = self.wallets.read().await;
+        let wallet = wallets.get(mint_url).ok_or(Error::UnknownMint {
+            mint_url: mint_url.to_string(),
+        })?;
+
+        wallet
+            .melt_lightning_address_quote(lightning_address, amount_msat)
+            .await
+    }
+
+    /// Get a melt quote for a human-readable address
+    ///
+    /// This method accepts a human-readable address that could be either a BIP353 address
+    /// or a Lightning address. It intelligently determines which to try based on mint support:
+    ///
+    /// 1. If the mint supports Bolt12, it tries BIP353 first
+    /// 2. Falls back to Lightning address only if BIP353 DNS resolution fails
+    /// 3. If BIP353 resolves but fails at the mint, it does NOT fall back to Lightning address
+    /// 4. If the mint doesn't support Bolt12, it tries Lightning address directly
+    ///
+    /// # Arguments
+    ///
+    /// * `mint_url` - The mint to use for creating the melt quote
+    /// * `address` - Human-readable address (BIP353 or Lightning address)
+    /// * `amount_msat` - Amount to pay in millisatoshis
+    #[cfg(all(feature = "bip353", feature = "wallet", not(target_arch = "wasm32")))]
+    #[instrument(skip(self, amount_msat))]
+    pub async fn melt_human_readable_quote(
+        &self,
+        mint_url: &MintUrl,
+        address: &str,
+        amount_msat: impl Into<Amount>,
+    ) -> Result<crate::wallet::types::MeltQuote, Error> {
+        let wallets = self.wallets.read().await;
+        let wallet = wallets.get(mint_url).ok_or(Error::UnknownMint {
+            mint_url: mint_url.to_string(),
+        })?;
+
+        wallet.melt_human_readable_quote(address, amount_msat).await
+    }
 }
 
 impl Drop for MultiMintWallet {