Skip to main content

cashu/nuts/nut17/
mod.rs

1//! Specific Subscription for the cdk crate
2use serde::de::DeserializeOwned;
3use serde::{Deserialize, Serialize};
4
5use super::PublicKey;
6use crate::nut00::KnownMethod;
7use crate::nut25::MeltQuoteBolt12Response;
8use crate::nuts::{
9    CurrencyUnit, MeltQuoteBolt11Response, MeltQuoteCustomResponse, MintQuoteBolt11Response,
10    MintQuoteCustomResponse, PaymentMethod, ProofState,
11};
12use crate::quote_id::QuoteIdError;
13use crate::MintQuoteBolt12Response;
14
15pub mod ws;
16
17/// Subscription Parameter according to the standard
18#[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash, Deserialize)]
19#[serde(bound = "I: DeserializeOwned + Serialize")]
20pub struct Params<I> {
21    /// Kind
22    pub kind: Kind,
23    /// Filters
24    pub filters: Vec<String>,
25    /// Subscription Id
26    #[serde(rename = "subId")]
27    pub id: I,
28}
29
30/// Check state Settings
31#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
32#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
33pub struct SupportedSettings {
34    /// Supported methods
35    pub supported: Vec<SupportedMethods>,
36}
37
38/// Supported WS Methods
39#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
41pub struct SupportedMethods {
42    /// Payment Method
43    pub method: PaymentMethod,
44    /// Unit
45    pub unit: CurrencyUnit,
46    /// Command
47    pub commands: Vec<WsCommand>,
48}
49
50impl SupportedMethods {
51    /// Create [`SupportedMethods`]
52    pub fn new(method: PaymentMethod, unit: CurrencyUnit, commands: Vec<WsCommand>) -> Self {
53        Self {
54            method,
55            unit,
56            commands,
57        }
58    }
59
60    /// Create [`SupportedMethods`] for Bolt11 with all supported commands
61    pub fn default_bolt11(unit: CurrencyUnit) -> Self {
62        let commands = vec![
63            WsCommand::Bolt11MintQuote,
64            WsCommand::Bolt11MeltQuote,
65            WsCommand::ProofState,
66        ];
67
68        Self {
69            method: PaymentMethod::Known(KnownMethod::Bolt11),
70            unit,
71            commands,
72        }
73    }
74
75    /// Create [`SupportedMethods`] for Bolt12 with all supported commands
76    pub fn default_bolt12(unit: CurrencyUnit) -> Self {
77        let commands = vec![
78            WsCommand::Bolt12MintQuote,
79            WsCommand::Bolt12MeltQuote,
80            WsCommand::ProofState,
81        ];
82
83        Self {
84            method: PaymentMethod::Known(KnownMethod::Bolt12),
85            unit,
86            commands,
87        }
88    }
89
90    /// Create [`SupportedMethods`] for custom payment method with all supported commands
91    pub fn default_custom(method: PaymentMethod, unit: CurrencyUnit) -> Self {
92        let method_name = method.to_string();
93        let commands = vec![
94            WsCommand::Custom(format!("{}_mint_quote", method_name)),
95            WsCommand::Custom(format!("{}_melt_quote", method_name)),
96            WsCommand::ProofState,
97        ];
98
99        Self {
100            method,
101            unit,
102            commands,
103        }
104    }
105}
106
107impl WsCommand {
108    /// Create a custom mint quote command for a payment method
109    pub fn custom_mint_quote(method: &str) -> Self {
110        WsCommand::Custom(format!("{}_mint_quote", method))
111    }
112
113    /// Create a custom melt quote command for a payment method
114    pub fn custom_melt_quote(method: &str) -> Self {
115        WsCommand::Custom(format!("{}_melt_quote", method))
116    }
117}
118
119/// WebSocket commands supported by the Cashu mint
120#[derive(Debug, Clone, PartialEq, Eq, Hash)]
121#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
122pub enum WsCommand {
123    /// Command to request a Lightning invoice for minting tokens
124    Bolt11MintQuote,
125    /// Command to request a Lightning payment for melting tokens
126    Bolt11MeltQuote,
127    /// Websocket support for Bolt12 Mint Quote
128    Bolt12MintQuote,
129    /// Websocket support for Bolt12 Melt Quote
130    Bolt12MeltQuote,
131    /// Command to check the state of a proof
132    ProofState,
133    /// Custom payment method command
134    Custom(String),
135}
136
137impl Serialize for WsCommand {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: serde::Serializer,
141    {
142        let s = match self {
143            WsCommand::Bolt11MintQuote => "bolt11_mint_quote",
144            WsCommand::Bolt11MeltQuote => "bolt11_melt_quote",
145            WsCommand::Bolt12MintQuote => "bolt12_mint_quote",
146            WsCommand::Bolt12MeltQuote => "bolt12_melt_quote",
147            WsCommand::ProofState => "proof_state",
148            WsCommand::Custom(custom) => custom.as_str(),
149        };
150        serializer.serialize_str(s)
151    }
152}
153
154impl<'de> Deserialize<'de> for WsCommand {
155    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156    where
157        D: serde::Deserializer<'de>,
158    {
159        let s = String::deserialize(deserializer)?;
160        Ok(match s.as_str() {
161            "bolt11_mint_quote" => WsCommand::Bolt11MintQuote,
162            "bolt11_melt_quote" => WsCommand::Bolt11MeltQuote,
163            "bolt12_mint_quote" => WsCommand::Bolt12MintQuote,
164            "bolt12_melt_quote" => WsCommand::Bolt12MeltQuote,
165            "proof_state" => WsCommand::ProofState,
166            custom => WsCommand::Custom(custom.to_string()),
167        })
168    }
169}
170
171impl<T> From<MintQuoteBolt12Response<T>> for NotificationPayload<T>
172where
173    T: Clone,
174{
175    fn from(mint_quote: MintQuoteBolt12Response<T>) -> NotificationPayload<T> {
176        NotificationPayload::MintQuoteBolt12Response(mint_quote)
177    }
178}
179
180impl<T> From<MeltQuoteBolt12Response<T>> for NotificationPayload<T>
181where
182    T: Clone,
183{
184    fn from(melt_quote: MeltQuoteBolt12Response<T>) -> NotificationPayload<T> {
185        NotificationPayload::MeltQuoteBolt12Response(melt_quote)
186    }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
190#[serde(bound = "T: Serialize + DeserializeOwned")]
191#[serde(untagged)]
192/// Subscription response
193pub enum NotificationPayload<T>
194where
195    T: Clone,
196{
197    /// Proof State
198    ProofState(ProofState),
199    /// Melt Quote Bolt11 Response
200    MeltQuoteBolt11Response(MeltQuoteBolt11Response<T>),
201    /// Mint Quote Bolt11 Response
202    MintQuoteBolt11Response(MintQuoteBolt11Response<T>),
203    /// Mint Quote Bolt12 Response
204    MintQuoteBolt12Response(MintQuoteBolt12Response<T>),
205    /// Melt Quote Bolt12 Response
206    MeltQuoteBolt12Response(MeltQuoteBolt12Response<T>),
207    /// Custom Mint Quote Response (method, response)
208    CustomMintQuoteResponse(String, MintQuoteCustomResponse<T>),
209    /// Custom Melt Quote Response (method, response)
210    CustomMeltQuoteResponse(String, MeltQuoteCustomResponse<T>),
211}
212
213#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Hash, Serialize)]
214#[serde(bound = "T: Serialize + DeserializeOwned")]
215/// A parsed notification
216pub enum NotificationId<T>
217where
218    T: Clone,
219{
220    /// ProofState id is a Pubkey
221    ProofState(PublicKey),
222    /// MeltQuote id is an QuoteId
223    MeltQuoteBolt11(T),
224    /// MintQuote id is an QuoteId
225    MintQuoteBolt11(T),
226    /// MintQuote id is an QuoteId
227    MintQuoteBolt12(T),
228    /// MintQuote id is an QuoteId
229    MeltQuoteBolt12(T),
230    /// MintQuote id is an QuoteId
231    MintQuoteCustom(String, T),
232    /// MintQuote id is an QuoteId
233    MeltQuoteCustom(String, T),
234}
235
236/// Kind
237#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)]
238pub enum Kind {
239    /// Bolt 11 Melt Quote
240    Bolt11MeltQuote,
241    /// Bolt 11 Mint Quote
242    Bolt11MintQuote,
243    /// Proof State
244    ProofState,
245    /// Bolt 12 Mint Quote
246    Bolt12MintQuote,
247    /// Bolt 12 Melt Quote
248    Bolt12MeltQuote,
249    /// Custom
250    Custom(String),
251}
252
253impl Serialize for Kind {
254    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
255    where
256        S: serde::Serializer,
257    {
258        let s = match self {
259            Kind::Bolt11MintQuote => "bolt11_mint_quote",
260            Kind::Bolt11MeltQuote => "bolt11_melt_quote",
261            Kind::Bolt12MintQuote => "bolt12_mint_quote",
262            Kind::Bolt12MeltQuote => "bolt12_melt_quote",
263            Kind::ProofState => "proof_state",
264            Kind::Custom(custom) => custom.as_str(),
265        };
266        serializer.serialize_str(s)
267    }
268}
269
270impl<'de> Deserialize<'de> for Kind {
271    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
272    where
273        D: serde::Deserializer<'de>,
274    {
275        let s = String::deserialize(deserializer)?;
276        Ok(match s.as_str() {
277            "bolt11_mint_quote" => Kind::Bolt11MintQuote,
278            "bolt11_melt_quote" => Kind::Bolt11MeltQuote,
279            "bolt12_mint_quote" => Kind::Bolt12MintQuote,
280            "bolt12_melt_quote" => Kind::Bolt12MeltQuote,
281            "proof_state" => Kind::ProofState,
282            custom => Kind::Custom(custom.to_string()),
283        })
284    }
285}
286
287impl<I> AsRef<I> for Params<I> {
288    fn as_ref(&self) -> &I {
289        &self.id
290    }
291}
292
293/// Parsing error
294#[derive(thiserror::Error, Debug)]
295pub enum Error {
296    #[error("Uuid Error: {0}")]
297    /// Uuid Error
298    QuoteId(#[from] QuoteIdError),
299
300    #[error("PublicKey Error: {0}")]
301    /// PublicKey Error
302    PublicKey(#[from] crate::nuts::nut01::Error),
303}