lwk/
bip321.rs

1//! BIP321 URI parsing and access
2
3use std::{fmt::Display, str::FromStr, sync::Arc};
4
5use crate::LwkError;
6
7/// A parsed Bitcoin BIP321 URI with optional parameters.
8///
9/// BIP321 extends BIP21 by allowing URIs without a bitcoin address in the path,
10/// as long as there is at least one payment instruction in the query parameters.
11///
12/// For example: `bitcoin:?ark=ark1qq...&amount=0.00000222`
13#[derive(uniffi::Object)]
14pub struct Bip321 {
15    inner: lwk_payment_instructions::Bip321,
16}
17
18impl Display for Bip321 {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(f, "{}", self.inner)
21    }
22}
23
24impl From<lwk_payment_instructions::Bip321> for Bip321 {
25    fn from(inner: lwk_payment_instructions::Bip321) -> Self {
26        Self { inner }
27    }
28}
29
30#[uniffi::export]
31impl Bip321 {
32    /// Parse a BIP321 URI string
33    #[uniffi::constructor]
34    pub fn new(s: &str) -> Result<Arc<Self>, LwkError> {
35        let inner = lwk_payment_instructions::Bip321::from_str(s)
36            .map_err(|e| LwkError::Generic { msg: e })?;
37        Ok(Arc::new(Self { inner }))
38    }
39
40    /// Returns the original URI string
41    pub fn as_str(&self) -> String {
42        self.inner.as_str().to_string()
43    }
44
45    /// Returns the amount in satoshis if present
46    pub fn amount(&self) -> Option<u64> {
47        self.inner.amount()
48    }
49
50    /// Returns the label if present
51    pub fn label(&self) -> Option<String> {
52        self.inner.label()
53    }
54
55    /// Returns the message if present
56    pub fn message(&self) -> Option<String> {
57        self.inner.message()
58    }
59
60    /// Returns the lightning BOLT11 invoice as a string if present
61    #[cfg(feature = "lightning")]
62    pub fn lightning(&self) -> Option<Arc<crate::Bolt11Invoice>> {
63        self.inner
64            .lightning()
65            .and_then(|inv| crate::Bolt11Invoice::new(&inv.to_string()).ok())
66    }
67
68    /// Returns the BOLT12 offer as a string if present
69    pub fn offer(&self) -> Option<String> {
70        self.inner.offer().map(|o| o.to_string())
71    }
72
73    /// Returns the payjoin endpoint URL if present
74    pub fn payjoin(&self) -> Option<String> {
75        self.inner.payjoin().map(|u| u.to_string())
76    }
77
78    /// Returns whether payjoin output substitution is allowed (defaults to true if absent)
79    pub fn payjoin_output_substitution(&self) -> bool {
80        self.inner.payjoin_output_substitution()
81    }
82
83    /// Returns the silent payment address (BIP-352) if present
84    pub fn silent_payment_address(&self) -> Option<String> {
85        self.inner.silent_payment_address().map(|sp| sp.to_string())
86    }
87
88    /// Returns the ark address if present
89    pub fn ark(&self) -> Option<String> {
90        self.inner.ark()
91    }
92}