1use serde::{Deserialize, Serialize};
4
5use super::amount::{Amount, CurrencyUnit};
6use super::mint::MintUrl;
7use crate::error::FfiError;
8
9#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
11pub struct MintQuote {
12 pub id: String,
14 pub amount: Option<Amount>,
19 pub unit: CurrencyUnit,
21 pub request: String,
23 pub state: QuoteState,
25 pub expiry: u64,
27 pub mint_url: MintUrl,
29 pub amount_issued: Amount,
31 pub amount_paid: Amount,
33 pub estimated_blocks: Option<u32>,
35 pub payment_method: PaymentMethod,
37 pub secret_key: Option<String>,
39 pub used_by_operation: Option<String>,
41 #[serde(default)]
43 pub version: u32,
44}
45
46impl From<cdk::wallet::MintQuote> for MintQuote {
47 fn from(quote: cdk::wallet::MintQuote) -> Self {
48 Self {
49 id: quote.id.clone(),
50 amount: quote.amount.map(Into::into),
51 unit: quote.unit.clone().into(),
52 request: quote.request.clone(),
53 state: quote.state.into(),
54 expiry: quote.expiry,
55 mint_url: quote.mint_url.clone().into(),
56 amount_issued: quote.amount_issued.into(),
57 amount_paid: quote.amount_paid.into(),
58 estimated_blocks: quote.estimated_blocks,
59 payment_method: quote.payment_method.into(),
60 secret_key: quote.secret_key.map(|sk| sk.to_secret_hex()),
61 used_by_operation: quote.used_by_operation.map(|id| id.to_string()),
62 version: quote.version,
63 }
64 }
65}
66
67impl TryFrom<MintQuote> for cdk::wallet::MintQuote {
68 type Error = FfiError;
69
70 fn try_from(quote: MintQuote) -> Result<Self, Self::Error> {
71 let secret_key = quote
72 .secret_key
73 .map(|hex| cdk::nuts::SecretKey::from_hex(&hex))
74 .transpose()
75 .map_err(|e| FfiError::internal(format!("Invalid secret key: {}", e)))?;
76
77 Ok(Self {
78 id: quote.id,
79 amount: quote.amount.map(Into::into),
80 unit: quote.unit.into(),
81 request: quote.request,
82 state: quote.state.into(),
83 expiry: quote.expiry,
84 mint_url: quote.mint_url.try_into()?,
85 amount_issued: quote.amount_issued.into(),
86 amount_paid: quote.amount_paid.into(),
87 estimated_blocks: quote.estimated_blocks,
88 payment_method: quote.payment_method.into(),
89 secret_key,
90 used_by_operation: quote.used_by_operation,
91 version: quote.version,
92 })
93 }
94}
95
96#[uniffi::export]
98pub fn mint_quote_total_amount(quote: &MintQuote) -> Result<Amount, FfiError> {
99 let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
100 Ok(cdk_quote.total_amount().into())
101}
102
103#[uniffi::export]
105pub fn mint_quote_is_expired(quote: &MintQuote, current_time: u64) -> Result<bool, FfiError> {
106 let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
107 Ok(cdk_quote.is_expired(current_time))
108}
109
110#[uniffi::export]
112pub fn mint_quote_amount_mintable(quote: &MintQuote) -> Result<Amount, FfiError> {
113 let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
114 Ok(cdk_quote.amount_mintable().into())
115}
116
117#[uniffi::export]
119pub fn decode_mint_quote(json: String) -> Result<MintQuote, FfiError> {
120 let quote: cdk::wallet::MintQuote = serde_json::from_str(&json)?;
121 Ok(quote.into())
122}
123
124#[uniffi::export]
126pub fn encode_mint_quote(quote: MintQuote) -> Result<String, FfiError> {
127 Ok(serde_json::to_string("e)?)
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
132pub struct MintQuoteBolt11Response {
133 pub quote: String,
135 pub request: String,
137 pub state: QuoteState,
139 pub expiry: Option<u64>,
141 pub amount: Option<Amount>,
143 pub unit: Option<CurrencyUnit>,
145 pub pubkey: Option<String>,
147}
148
149impl From<cdk::nuts::MintQuoteBolt11Response<String>> for MintQuoteBolt11Response {
150 fn from(response: cdk::nuts::MintQuoteBolt11Response<String>) -> Self {
151 Self {
152 quote: response.quote,
153 request: response.request,
154 state: response.state.into(),
155 expiry: response.expiry,
156 amount: response.amount.map(Into::into),
157 unit: response.unit.map(Into::into),
158 pubkey: response.pubkey.map(|p| p.to_string()),
159 }
160 }
161}
162
163impl From<cdk::wallet::MintQuote> for MintQuoteBolt11Response {
164 fn from(quote: cdk::wallet::MintQuote) -> Self {
165 Self {
166 quote: quote.id,
167 request: quote.request,
168 state: quote.state.into(),
169 expiry: Some(quote.expiry),
170 amount: quote.amount.map(Into::into),
171 unit: Some(quote.unit.into()),
172 pubkey: quote.secret_key.map(|sk| sk.public_key().to_string()),
173 }
174 }
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
182pub struct MintQuoteCustomResponse {
183 pub quote: String,
185 pub request: String,
187 pub expiry: Option<u64>,
189 pub amount: Option<Amount>,
191 pub amount_paid: Amount,
193 pub amount_issued: Amount,
195 pub unit: Option<CurrencyUnit>,
197 pub pubkey: Option<String>,
199 pub extra: Option<String>,
204}
205
206impl From<cdk::nuts::MintQuoteCustomResponse<String>> for MintQuoteCustomResponse {
207 fn from(response: cdk::nuts::MintQuoteCustomResponse<String>) -> Self {
208 let extra = if response.extra.is_null() {
209 None
210 } else {
211 Some(response.extra.to_string())
212 };
213
214 Self {
215 quote: response.quote,
216 request: response.request,
217 expiry: response.expiry,
218 amount: response.amount.map(Into::into),
219 amount_paid: response.amount_paid.into(),
220 amount_issued: response.amount_issued.into(),
221 unit: response.unit.map(Into::into),
222 pubkey: response.pubkey.map(|p| p.to_string()),
223 extra,
224 }
225 }
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
230pub struct MeltQuoteBolt11Response {
231 pub quote: String,
233 pub amount: Amount,
235 pub fee_reserve: Amount,
237 pub state: QuoteState,
239 pub expiry: u64,
241 pub payment_proof: Option<String>,
243 pub request: Option<String>,
245 pub unit: Option<CurrencyUnit>,
247}
248
249impl From<cdk::nuts::MeltQuoteBolt11Response<String>> for MeltQuoteBolt11Response {
250 fn from(response: cdk::nuts::MeltQuoteBolt11Response<String>) -> Self {
251 Self {
252 quote: response.quote,
253 amount: response.amount.into(),
254 fee_reserve: response.fee_reserve.into(),
255 state: response.state.into(),
256 expiry: response.expiry,
257 payment_proof: response.payment_preimage,
258 request: response.request,
259 unit: response.unit.map(Into::into),
260 }
261 }
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
269pub struct MeltQuoteCustomResponse {
270 pub quote: String,
272 pub amount: Amount,
274 pub fee_reserve: Option<Amount>,
276 pub state: QuoteState,
278 pub expiry: u64,
280 pub payment_proof: Option<String>,
282 pub request: Option<String>,
284 pub unit: Option<CurrencyUnit>,
286 pub extra: Option<String>,
291}
292
293impl From<cdk::nuts::MeltQuoteCustomResponse<String>> for MeltQuoteCustomResponse {
294 fn from(response: cdk::nuts::MeltQuoteCustomResponse<String>) -> Self {
295 let extra = if response.extra.is_null() {
296 None
297 } else {
298 Some(response.extra.to_string())
299 };
300
301 Self {
302 quote: response.quote,
303 amount: response.amount.into(),
304 fee_reserve: response.fee_reserve.map(Into::into),
305 state: response.state.into(),
306 expiry: response.expiry,
307 payment_proof: response.payment_preimage,
308 request: response.request,
309 unit: response.unit.map(Into::into),
310 extra,
311 }
312 }
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
317pub enum PaymentMethod {
318 Bolt11,
320 Bolt12,
322 Onchain,
324 Custom { method: String },
326}
327
328impl From<cdk::nuts::PaymentMethod> for PaymentMethod {
329 fn from(method: cdk::nuts::PaymentMethod) -> Self {
330 match method.as_str() {
331 "bolt11" => Self::Bolt11,
332 "bolt12" => Self::Bolt12,
333 "onchain" => Self::Onchain,
334 s => Self::Custom {
335 method: s.to_string(),
336 },
337 }
338 }
339}
340
341impl From<PaymentMethod> for cdk::nuts::PaymentMethod {
342 fn from(method: PaymentMethod) -> Self {
343 match method {
344 PaymentMethod::Bolt11 => Self::from("bolt11"),
345 PaymentMethod::Bolt12 => Self::from("bolt12"),
346 PaymentMethod::Onchain => Self::from("onchain"),
347 PaymentMethod::Custom { method } => Self::from(method),
348 }
349 }
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
354pub struct MintQuoteOnchainResponse {
355 pub quote: String,
357 pub request: String,
359 pub unit: CurrencyUnit,
361 pub expiry: Option<u64>,
363 pub pubkey: String,
365 pub amount_paid: Amount,
367 pub amount_issued: Amount,
369}
370
371impl From<cdk::nuts::MintQuoteOnchainResponse<String>> for MintQuoteOnchainResponse {
372 fn from(response: cdk::nuts::MintQuoteOnchainResponse<String>) -> Self {
373 Self {
374 quote: response.quote,
375 request: response.request,
376 unit: response.unit.into(),
377 expiry: response.expiry,
378 pubkey: response.pubkey.to_string(),
379 amount_paid: response.amount_paid.into(),
380 amount_issued: response.amount_issued.into(),
381 }
382 }
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
387pub struct MeltQuoteOnchainFeeOption {
388 pub fee_index: u32,
390 pub fee_reserve: Amount,
392 pub estimated_blocks: u32,
394}
395
396impl From<cdk::nuts::nut30::MeltQuoteOnchainFeeOption> for MeltQuoteOnchainFeeOption {
397 fn from(option: cdk::nuts::nut30::MeltQuoteOnchainFeeOption) -> Self {
398 Self {
399 fee_index: option.fee_index,
400 fee_reserve: option.fee_reserve.into(),
401 estimated_blocks: option.estimated_blocks,
402 }
403 }
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
408pub struct MeltQuoteOnchainResponse {
409 pub quote: String,
411 pub amount: Amount,
413 pub unit: CurrencyUnit,
415 pub state: QuoteState,
417 pub expiry: u64,
419 pub request: String,
421 pub fee_options: Vec<MeltQuoteOnchainFeeOption>,
423 pub selected_fee_index: Option<u32>,
425 pub outpoint: Option<String>,
427 pub change: Option<String>,
429}
430
431impl From<cdk::nuts::MeltQuoteOnchainResponse<String>> for MeltQuoteOnchainResponse {
432 fn from(response: cdk::nuts::MeltQuoteOnchainResponse<String>) -> Self {
433 let change = response
434 .change
435 .as_ref()
436 .and_then(|change| serde_json::to_string(change).ok());
437
438 Self {
439 quote: response.quote,
440 amount: response.amount.into(),
441 unit: response.unit.into(),
442 state: response.state.into(),
443 expiry: response.expiry,
444 request: response.request,
445 fee_options: response.fee_options.into_iter().map(Into::into).collect(),
446 selected_fee_index: response.selected_fee_index,
447 outpoint: response.outpoint,
448 change,
449 }
450 }
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
455pub struct MeltQuote {
456 pub id: String,
458 pub mint_url: Option<MintUrl>,
460 pub amount: Amount,
462 pub unit: CurrencyUnit,
464 pub request: String,
466 pub fee_reserve: Amount,
468 pub state: QuoteState,
470 pub expiry: u64,
472 pub payment_proof: Option<String>,
474 pub estimated_blocks: Option<u32>,
476 pub fee_index: Option<u32>,
478 pub payment_method: PaymentMethod,
480 pub used_by_operation: Option<String>,
482 #[serde(default)]
484 pub version: u32,
485}
486
487impl From<cdk::wallet::MeltQuote> for MeltQuote {
488 fn from(quote: cdk::wallet::MeltQuote) -> Self {
489 Self {
490 id: quote.id.clone(),
491 mint_url: quote.mint_url.map(Into::into),
492 amount: quote.amount.into(),
493 unit: quote.unit.clone().into(),
494 request: quote.request.clone(),
495 fee_reserve: quote.fee_reserve.into(),
496 state: quote.state.into(),
497 expiry: quote.expiry,
498 payment_proof: quote.payment_proof.clone(),
499 estimated_blocks: quote.estimated_blocks,
500 fee_index: quote.fee_index,
501 payment_method: quote.payment_method.into(),
502 used_by_operation: quote.used_by_operation.map(|id| id.to_string()),
503 version: quote.version,
504 }
505 }
506}
507
508impl TryFrom<MeltQuote> for cdk::wallet::MeltQuote {
509 type Error = FfiError;
510
511 fn try_from(quote: MeltQuote) -> Result<Self, Self::Error> {
512 Ok(Self {
513 id: quote.id,
514 mint_url: quote.mint_url.map(|m| m.try_into()).transpose()?,
515 amount: quote.amount.into(),
516 unit: quote.unit.into(),
517 request: quote.request,
518 fee_reserve: quote.fee_reserve.into(),
519 state: quote.state.into(),
520 expiry: quote.expiry,
521 payment_proof: quote.payment_proof,
522 estimated_blocks: quote.estimated_blocks,
523 fee_index: quote.fee_index,
524 payment_method: quote.payment_method.into(),
525 used_by_operation: quote.used_by_operation,
526 version: quote.version,
527 })
528 }
529}
530
531impl MeltQuote {
532 pub fn to_json(&self) -> Result<String, FfiError> {
534 Ok(serde_json::to_string(self)?)
535 }
536}
537
538#[uniffi::export]
540pub fn decode_melt_quote(json: String) -> Result<MeltQuote, FfiError> {
541 let quote: cdk::wallet::MeltQuote = serde_json::from_str(&json)?;
542 Ok(quote.into())
543}
544
545#[uniffi::export]
547pub fn encode_melt_quote(quote: MeltQuote) -> Result<String, FfiError> {
548 Ok(serde_json::to_string("e)?)
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
553pub enum QuoteState {
554 Unpaid,
555 Paid,
556 Pending,
557 Issued,
558}
559
560impl From<cdk::nuts::nut05::QuoteState> for QuoteState {
561 fn from(state: cdk::nuts::nut05::QuoteState) -> Self {
562 match state {
563 cdk::nuts::nut05::QuoteState::Unpaid => QuoteState::Unpaid,
564 cdk::nuts::nut05::QuoteState::Paid => QuoteState::Paid,
565 cdk::nuts::nut05::QuoteState::Pending => QuoteState::Pending,
566 cdk::nuts::nut05::QuoteState::Unknown => QuoteState::Unpaid,
567 cdk::nuts::nut05::QuoteState::Failed => QuoteState::Unpaid,
568 }
569 }
570}
571
572impl From<QuoteState> for cdk::nuts::nut05::QuoteState {
573 fn from(state: QuoteState) -> Self {
574 match state {
575 QuoteState::Unpaid => cdk::nuts::nut05::QuoteState::Unpaid,
576 QuoteState::Paid => cdk::nuts::nut05::QuoteState::Paid,
577 QuoteState::Pending => cdk::nuts::nut05::QuoteState::Pending,
578 QuoteState::Issued => cdk::nuts::nut05::QuoteState::Paid, }
580 }
581}
582
583impl From<cdk::nuts::MintQuoteState> for QuoteState {
584 fn from(state: cdk::nuts::MintQuoteState) -> Self {
585 match state {
586 cdk::nuts::MintQuoteState::Unpaid => QuoteState::Unpaid,
587 cdk::nuts::MintQuoteState::Paid => QuoteState::Paid,
588 cdk::nuts::MintQuoteState::Issued => QuoteState::Issued,
589 }
590 }
591}
592
593impl From<QuoteState> for cdk::nuts::MintQuoteState {
594 fn from(state: QuoteState) -> Self {
595 match state {
596 QuoteState::Unpaid => cdk::nuts::MintQuoteState::Unpaid,
597 QuoteState::Paid => cdk::nuts::MintQuoteState::Paid,
598 QuoteState::Issued => cdk::nuts::MintQuoteState::Issued,
599 QuoteState::Pending => cdk::nuts::MintQuoteState::Unpaid,
600 }
601 }
602}
603
604