1use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6
7use cdk_common::bitcoin;
8use serde::{Deserialize, Serialize};
9
10use super::amount::{Amount, SplitTarget};
11use super::proof::{Proofs, SpendingConditions};
12use crate::error::FfiError;
13use crate::token::Token;
14use crate::{CurrencyUnit, MintUrl, PublicKey};
15
16#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
18pub struct SendMemo {
19 pub memo: String,
21 pub include_memo: bool,
23}
24
25impl From<SendMemo> for cdk::wallet::SendMemo {
26 fn from(memo: SendMemo) -> Self {
27 cdk::wallet::SendMemo {
28 memo: memo.memo,
29 include_memo: memo.include_memo,
30 }
31 }
32}
33
34impl From<cdk::wallet::SendMemo> for SendMemo {
35 fn from(memo: cdk::wallet::SendMemo) -> Self {
36 Self {
37 memo: memo.memo,
38 include_memo: memo.include_memo,
39 }
40 }
41}
42
43impl SendMemo {
44 pub fn to_json(&self) -> Result<String, FfiError> {
46 Ok(serde_json::to_string(self)?)
47 }
48}
49
50#[uniffi::export]
52pub fn decode_send_memo(json: String) -> Result<SendMemo, FfiError> {
53 Ok(serde_json::from_str(&json)?)
54}
55
56#[uniffi::export]
58pub fn encode_send_memo(memo: SendMemo) -> Result<String, FfiError> {
59 Ok(serde_json::to_string(&memo)?)
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Enum)]
64pub enum SendKind {
65 OnlineExact,
67 OnlineTolerance { tolerance: Amount },
69 OfflineExact,
71 OfflineTolerance { tolerance: Amount },
73}
74
75impl From<SendKind> for cdk::wallet::SendKind {
76 fn from(kind: SendKind) -> Self {
77 match kind {
78 SendKind::OnlineExact => cdk::wallet::SendKind::OnlineExact,
79 SendKind::OnlineTolerance { tolerance } => {
80 cdk::wallet::SendKind::OnlineTolerance(tolerance.into())
81 }
82 SendKind::OfflineExact => cdk::wallet::SendKind::OfflineExact,
83 SendKind::OfflineTolerance { tolerance } => {
84 cdk::wallet::SendKind::OfflineTolerance(tolerance.into())
85 }
86 }
87 }
88}
89
90#[derive(Debug, Clone, uniffi::Record)]
92pub struct P2PKSigningKey {
93 pub pubkey: PublicKey,
95 pub derivation_path: String,
97 pub derivation_index: u32,
99 pub created_time: u64,
101}
102
103impl TryFrom<P2PKSigningKey> for cdk_common::wallet::P2PKSigningKey {
104 type Error = crate::error::FfiError;
105
106 fn try_from(key: P2PKSigningKey) -> Result<Self, FfiError> {
107 Ok(Self {
108 pubkey: key.pubkey.try_into()?,
109 derivation_path: key
110 .derivation_path
111 .parse()
112 .map_err(|e: bitcoin::bip32::Error| FfiError::Internal {
113 error_message: e.to_string(),
114 })?,
115 derivation_index: key.derivation_index,
116 created_time: key.created_time,
117 })
118 }
119}
120
121impl From<cdk_common::wallet::P2PKSigningKey> for P2PKSigningKey {
122 fn from(key: cdk_common::wallet::P2PKSigningKey) -> Self {
123 Self {
124 pubkey: key.pubkey.into(),
125 derivation_path: key.derivation_path.to_string(),
126 derivation_index: key.derivation_index,
127 created_time: key.created_time,
128 }
129 }
130}
131
132impl From<cdk::wallet::SendKind> for SendKind {
133 fn from(kind: cdk::wallet::SendKind) -> Self {
134 match kind {
135 cdk::wallet::SendKind::OnlineExact => SendKind::OnlineExact,
136 cdk::wallet::SendKind::OnlineTolerance(tolerance) => SendKind::OnlineTolerance {
137 tolerance: tolerance.into(),
138 },
139 cdk::wallet::SendKind::OfflineExact => SendKind::OfflineExact,
140 cdk::wallet::SendKind::OfflineTolerance(tolerance) => SendKind::OfflineTolerance {
141 tolerance: tolerance.into(),
142 },
143 }
144 }
145}
146
147#[derive(
149 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, uniffi::Enum, Default,
150)]
151pub enum P2PKLockedProofSendMode {
152 #[default]
154 Swap,
155 SignAndSend,
157}
158
159impl From<P2PKLockedProofSendMode> for cdk::wallet::P2PKLockedProofSendMode {
160 fn from(mode: P2PKLockedProofSendMode) -> Self {
161 match mode {
162 P2PKLockedProofSendMode::Swap => cdk::wallet::P2PKLockedProofSendMode::Swap,
163 P2PKLockedProofSendMode::SignAndSend => {
164 cdk::wallet::P2PKLockedProofSendMode::SignAndSend
165 }
166 }
167 }
168}
169
170impl From<cdk::wallet::P2PKLockedProofSendMode> for P2PKLockedProofSendMode {
171 fn from(mode: cdk::wallet::P2PKLockedProofSendMode) -> Self {
172 match mode {
173 cdk::wallet::P2PKLockedProofSendMode::Swap => P2PKLockedProofSendMode::Swap,
174 cdk::wallet::P2PKLockedProofSendMode::SignAndSend => {
175 P2PKLockedProofSendMode::SignAndSend
176 }
177 }
178 }
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
183pub struct SendOptions {
184 pub memo: Option<SendMemo>,
186 pub conditions: Option<SpendingConditions>,
188 pub amount_split_target: SplitTarget,
190 pub send_kind: SendKind,
192 pub include_fee: bool,
194 pub use_p2bk: bool,
195 pub max_proofs: Option<u32>,
197 pub metadata: HashMap<String, String>,
199 #[serde(default)]
201 pub p2pk_signing_keys: Vec<SecretKey>,
202 #[serde(default)]
204 pub p2pk_locked_proof_send_mode: P2PKLockedProofSendMode,
205}
206
207impl Default for SendOptions {
208 fn default() -> Self {
209 Self {
210 memo: None,
211 conditions: None,
212 amount_split_target: SplitTarget::None,
213 send_kind: SendKind::OnlineExact,
214 include_fee: false,
215 max_proofs: None,
216 metadata: HashMap::new(),
217 use_p2bk: false,
218 p2pk_signing_keys: Vec::new(),
219 p2pk_locked_proof_send_mode: P2PKLockedProofSendMode::Swap,
220 }
221 }
222}
223
224impl TryFrom<SendOptions> for cdk::wallet::SendOptions {
225 type Error = FfiError;
226
227 fn try_from(opts: SendOptions) -> Result<Self, Self::Error> {
228 let p2pk_signing_keys = opts
229 .p2pk_signing_keys
230 .into_iter()
231 .map(TryInto::try_into)
232 .collect::<Result<Vec<_>, _>>()?;
233
234 Ok(cdk::wallet::SendOptions {
235 memo: opts.memo.map(Into::into),
236 conditions: opts.conditions.map(TryInto::try_into).transpose()?,
237 amount_split_target: opts.amount_split_target.into(),
238 send_kind: opts.send_kind.into(),
239 include_fee: opts.include_fee,
240 max_proofs: opts.max_proofs.map(|p| p as usize),
241 metadata: opts.metadata,
242 use_p2bk: opts.use_p2bk,
243 p2pk_signing_keys,
244 p2pk_locked_proof_send_mode: opts.p2pk_locked_proof_send_mode.into(),
245 })
246 }
247}
248
249impl From<cdk::wallet::SendOptions> for SendOptions {
250 fn from(opts: cdk::wallet::SendOptions) -> Self {
251 Self {
252 memo: opts.memo.map(Into::into),
253 conditions: opts.conditions.map(Into::into),
254 amount_split_target: opts.amount_split_target.into(),
255 send_kind: opts.send_kind.into(),
256 include_fee: opts.include_fee,
257 max_proofs: opts.max_proofs.map(|p| p as u32),
258 metadata: opts.metadata,
259 use_p2bk: opts.use_p2bk,
260 p2pk_signing_keys: opts.p2pk_signing_keys.into_iter().map(Into::into).collect(),
261 p2pk_locked_proof_send_mode: opts.p2pk_locked_proof_send_mode.into(),
262 }
263 }
264}
265
266impl SendOptions {
267 pub fn to_json(&self) -> Result<String, FfiError> {
269 Ok(serde_json::to_string(self)?)
270 }
271}
272
273#[uniffi::export]
275pub fn decode_send_options(json: String) -> Result<SendOptions, FfiError> {
276 Ok(serde_json::from_str(&json)?)
277}
278
279#[uniffi::export]
281pub fn encode_send_options(options: SendOptions) -> Result<String, FfiError> {
282 Ok(serde_json::to_string(&options)?)
283}
284
285#[derive(Clone, Serialize, Deserialize, uniffi::Record)]
287#[serde(transparent)]
288pub struct SecretKey {
289 pub hex: String,
291}
292
293impl fmt::Debug for SecretKey {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 f.debug_struct("SecretKey")
296 .field("hex", &"[redacted]")
297 .finish()
298 }
299}
300
301impl SecretKey {
302 pub fn from_hex(hex: String) -> Result<Self, FfiError> {
304 if hex.len() != 64 {
306 return Err(FfiError::internal(
307 "Secret key hex must be exactly 64 characters (32 bytes)",
308 ));
309 }
310
311 if !hex.chars().all(|c| c.is_ascii_hexdigit()) {
313 return Err(FfiError::internal(
314 "Secret key hex contains invalid characters",
315 ));
316 }
317
318 Ok(Self { hex })
319 }
320
321 pub fn random() -> Self {
323 use cdk::nuts::SecretKey as CdkSecretKey;
324 let secret_key = CdkSecretKey::generate();
325 Self {
326 hex: secret_key.to_secret_hex(),
327 }
328 }
329}
330
331impl TryFrom<SecretKey> for cdk::nuts::SecretKey {
332 type Error = FfiError;
333
334 fn try_from(key: SecretKey) -> Result<Self, Self::Error> {
335 cdk::nuts::SecretKey::from_hex(&key.hex)
336 .map_err(|e| FfiError::internal(format!("Invalid secret key: {}", e)))
337 }
338}
339
340impl From<cdk::nuts::SecretKey> for SecretKey {
341 fn from(key: cdk::nuts::SecretKey) -> Self {
342 Self {
343 hex: key.to_secret_hex(),
344 }
345 }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
350pub struct ReceiveOptions {
351 pub amount_split_target: SplitTarget,
353 #[serde(default)]
355 pub p2pk_signing_keys: Vec<SecretKey>,
356 pub preimages: Vec<String>,
358 pub metadata: HashMap<String, String>,
360}
361
362impl Default for ReceiveOptions {
363 fn default() -> Self {
364 Self {
365 amount_split_target: SplitTarget::None,
366 p2pk_signing_keys: Vec::new(),
367 preimages: Vec::new(),
368 metadata: HashMap::new(),
369 }
370 }
371}
372
373impl TryFrom<ReceiveOptions> for cdk::wallet::ReceiveOptions {
374 type Error = FfiError;
375
376 fn try_from(opts: ReceiveOptions) -> Result<Self, Self::Error> {
377 let p2pk_signing_keys = opts
378 .p2pk_signing_keys
379 .into_iter()
380 .map(TryInto::try_into)
381 .collect::<Result<Vec<_>, _>>()?;
382
383 Ok(cdk::wallet::ReceiveOptions {
384 amount_split_target: opts.amount_split_target.into(),
385 p2pk_signing_keys,
386 preimages: opts.preimages,
387 metadata: opts.metadata,
388 })
389 }
390}
391
392impl From<cdk::wallet::ReceiveOptions> for ReceiveOptions {
393 fn from(opts: cdk::wallet::ReceiveOptions) -> Self {
394 Self {
395 amount_split_target: opts.amount_split_target.into(),
396 p2pk_signing_keys: opts.p2pk_signing_keys.into_iter().map(Into::into).collect(),
397 preimages: opts.preimages,
398 metadata: opts.metadata,
399 }
400 }
401}
402
403impl ReceiveOptions {
404 pub fn to_json(&self) -> Result<String, FfiError> {
406 Ok(serde_json::to_string(self)?)
407 }
408}
409
410#[uniffi::export]
412pub fn decode_receive_options(json: String) -> Result<ReceiveOptions, FfiError> {
413 Ok(serde_json::from_str(&json)?)
414}
415
416#[uniffi::export]
418pub fn encode_receive_options(options: ReceiveOptions) -> Result<String, FfiError> {
419 Ok(serde_json::to_string(&options)?)
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
424pub struct NUT13Options {
425 pub batch_size: u32,
427 pub max_gap: u32,
429}
430
431impl Default for NUT13Options {
432 fn default() -> Self {
433 cdk::wallet::NUT13Options::default().into()
434 }
435}
436
437impl TryFrom<NUT13Options> for cdk::wallet::NUT13Options {
438 type Error = FfiError;
439
440 fn try_from(opts: NUT13Options) -> Result<Self, Self::Error> {
441 Ok(cdk::wallet::NUT13Options::new(
442 opts.batch_size,
443 opts.max_gap,
444 )?)
445 }
446}
447
448impl From<cdk::wallet::NUT13Options> for NUT13Options {
449 fn from(opts: cdk::wallet::NUT13Options) -> Self {
450 NUT13Options {
451 batch_size: opts.batch_size,
452 max_gap: opts.max_gap,
453 }
454 }
455}
456
457#[derive(uniffi::Object)]
463pub struct PreparedSend {
464 wallet: std::sync::Arc<cdk::Wallet>,
465 operation_id: uuid::Uuid,
466 amount: Amount,
467 options: cdk::wallet::SendOptions,
468 proofs_to_swap: cdk::nuts::Proofs,
469 proofs_to_send: cdk::nuts::Proofs,
470 swap_fee: Amount,
471 send_fee: Amount,
472}
473
474impl std::fmt::Debug for PreparedSend {
475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476 f.debug_struct("PreparedSend")
477 .field("operation_id", &self.operation_id)
478 .field("amount", &self.amount)
479 .finish()
480 }
481}
482
483impl PreparedSend {
484 pub fn new(
486 wallet: std::sync::Arc<cdk::Wallet>,
487 prepared: &cdk::wallet::PreparedSend<'_>,
488 ) -> Self {
489 Self {
490 wallet,
491 operation_id: prepared.operation_id(),
492 amount: prepared.amount().into(),
493 options: prepared.options().clone(),
494 proofs_to_swap: prepared.proofs_to_swap().clone(),
495 proofs_to_send: prepared.proofs_to_send().clone(),
496 swap_fee: prepared.swap_fee().into(),
497 send_fee: prepared.send_fee().into(),
498 }
499 }
500}
501
502#[uniffi::export(async_runtime = "tokio")]
503impl PreparedSend {
504 pub fn operation_id(&self) -> String {
506 self.operation_id.to_string()
507 }
508
509 pub fn amount(&self) -> Amount {
511 self.amount
512 }
513
514 pub fn proofs(&self) -> Proofs {
516 let mut all_proofs: Vec<_> = self
517 .proofs_to_swap
518 .iter()
519 .cloned()
520 .map(|p| p.into())
521 .collect();
522 all_proofs.extend(self.proofs_to_send.iter().cloned().map(|p| p.into()));
523 all_proofs
524 }
525
526 pub fn fee(&self) -> Amount {
528 Amount::new(self.swap_fee.value + self.send_fee.value)
529 }
530
531 pub async fn confirm(
533 self: std::sync::Arc<Self>,
534 memo: Option<String>,
535 ) -> Result<Token, FfiError> {
536 let send_memo = memo.map(|m| cdk::wallet::SendMemo::for_token(&m));
537 let token = self
538 .wallet
539 .confirm_send(
540 self.operation_id,
541 self.amount.into(),
542 self.options.clone(),
543 self.proofs_to_swap.clone(),
544 self.proofs_to_send.clone(),
545 self.swap_fee.into(),
546 self.send_fee.into(),
547 send_memo,
548 )
549 .await?;
550
551 Ok(token.into())
552 }
553
554 pub async fn cancel(self: std::sync::Arc<Self>) -> Result<(), FfiError> {
556 self.wallet
557 .cancel_send(
558 self.operation_id,
559 self.proofs_to_swap.clone(),
560 self.proofs_to_send.clone(),
561 )
562 .await?;
563 Ok(())
564 }
565}
566
567#[derive(Debug, Clone, uniffi::Record)]
569pub struct FinalizedMelt {
570 pub quote_id: String,
571 pub state: super::quote::QuoteState,
572 pub preimage: Option<String>,
573 pub change: Option<Proofs>,
574 pub amount: Amount,
575 pub fee_paid: Amount,
576}
577
578impl From<cdk_common::common::FinalizedMelt> for FinalizedMelt {
579 fn from(finalized: cdk_common::common::FinalizedMelt) -> Self {
580 Self {
581 quote_id: finalized.quote_id().to_string(),
582 state: finalized.state().into(),
583 preimage: finalized.payment_proof().map(|s: &str| s.to_string()),
584 change: finalized
585 .change()
586 .map(|proofs| proofs.iter().cloned().map(|p| p.into()).collect()),
587 amount: finalized.amount().into(),
588 fee_paid: finalized.fee_paid().into(),
589 }
590 }
591}
592
593#[derive(uniffi::Object)]
603pub struct PendingMelt {
604 wallet: Arc<cdk::Wallet>,
605 quote_id: String,
606 operation_id: uuid::Uuid,
607 payment_method: cdk_common::PaymentMethod,
608}
609
610impl std::fmt::Debug for PendingMelt {
611 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
612 f.debug_struct("PendingMelt")
613 .field("operation_id", &self.operation_id)
614 .field("quote_id", &self.quote_id)
615 .finish()
616 }
617}
618
619#[uniffi::export(async_runtime = "tokio")]
620impl PendingMelt {
621 pub fn quote_id(&self) -> String {
623 self.quote_id.clone()
624 }
625
626 pub fn operation_id(&self) -> String {
628 self.operation_id.to_string()
629 }
630
631 pub async fn wait(&self) -> Result<FinalizedMelt, FfiError> {
642 let finalized = self
643 .wallet
644 .wait_pending_melt(
645 self.operation_id,
646 &self.quote_id,
647 self.payment_method.clone(),
648 )
649 .await?;
650
651 Ok(finalized.into())
652 }
653}
654
655#[derive(Debug, Clone, uniffi::Enum)]
661pub enum MeltConfirmOutcome {
662 Paid { finalized: FinalizedMelt },
664 Pending { pending: Arc<PendingMelt> },
666}
667
668#[derive(uniffi::Object)]
674pub struct PreparedMelt {
675 wallet: Arc<cdk::Wallet>,
676 operation_id: uuid::Uuid,
677 quote: cdk_common::wallet::MeltQuote,
678 proofs: cdk::nuts::Proofs,
679 proofs_to_swap: cdk::nuts::Proofs,
680 swap_fee: Amount,
681 input_fee: Amount,
682 input_fee_without_swap: Amount,
683 metadata: HashMap<String, String>,
684}
685
686impl std::fmt::Debug for PreparedMelt {
687 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
688 f.debug_struct("PreparedMelt")
689 .field("operation_id", &self.operation_id)
690 .field("quote_id", &self.quote.id)
691 .field("amount", &self.quote.amount)
692 .finish()
693 }
694}
695
696impl PreparedMelt {
697 pub fn new(wallet: Arc<cdk::Wallet>, prepared: &cdk::wallet::PreparedMelt<'_>) -> Self {
699 Self {
700 wallet,
701 operation_id: prepared.operation_id(),
702 quote: prepared.quote().clone(),
703 proofs: prepared.proofs().clone(),
704 proofs_to_swap: prepared.proofs_to_swap().clone(),
705 swap_fee: prepared.swap_fee().into(),
706 input_fee: prepared.input_fee().into(),
707 input_fee_without_swap: prepared.input_fee_without_swap().into(),
708 metadata: prepared.metadata().clone(),
709 }
710 }
711
712 async fn confirm_prefer_async_with_options(
713 &self,
714 options: MeltConfirmOptions,
715 ) -> Result<MeltConfirmOutcome, FfiError> {
716 let outcome = self
717 .wallet
718 .confirm_prepared_melt_prefer_async_with_options(
719 self.operation_id,
720 self.quote.clone(),
721 self.proofs.clone(),
722 self.proofs_to_swap.clone(),
723 self.input_fee.into(),
724 self.input_fee_without_swap.into(),
725 self.metadata.clone(),
726 options.into(),
727 )
728 .await?;
729
730 match outcome {
731 cdk::wallet::MeltOutcome::Paid(finalized) => Ok(MeltConfirmOutcome::Paid {
732 finalized: finalized.into(),
733 }),
734 cdk::wallet::MeltOutcome::Pending(_) => Ok(MeltConfirmOutcome::Pending {
735 pending: Arc::new(PendingMelt {
736 wallet: Arc::clone(&self.wallet),
737 quote_id: self.quote.id.clone(),
738 operation_id: self.operation_id,
739 payment_method: self.quote.payment_method.clone(),
740 }),
741 }),
742 }
743 }
744}
745
746#[uniffi::export(async_runtime = "tokio")]
747impl PreparedMelt {
748 pub fn operation_id(&self) -> String {
750 self.operation_id.to_string()
751 }
752
753 pub fn quote_id(&self) -> String {
755 self.quote.id.clone()
756 }
757
758 pub fn amount(&self) -> Amount {
760 self.quote.amount.into()
761 }
762
763 pub fn fee_reserve(&self) -> Amount {
765 self.quote.fee_reserve.into()
766 }
767
768 pub fn swap_fee(&self) -> Amount {
770 self.swap_fee
771 }
772
773 pub fn input_fee(&self) -> Amount {
775 self.input_fee
776 }
777
778 pub fn total_fee(&self) -> Amount {
780 Amount::new(self.swap_fee.value + self.input_fee.value)
781 }
782
783 pub fn requires_swap(&self) -> bool {
785 !self.proofs_to_swap.is_empty()
786 }
787
788 pub fn total_fee_with_swap(&self) -> Amount {
790 Amount::new(self.swap_fee.value + self.input_fee.value)
791 }
792
793 pub fn input_fee_without_swap(&self) -> Amount {
795 self.input_fee_without_swap
796 }
797
798 pub fn fee_savings_without_swap(&self) -> Amount {
800 let total_with = self.swap_fee.value + self.input_fee.value;
801 let total_without = self.input_fee_without_swap.value;
802 if total_with > total_without {
803 Amount::new(total_with - total_without)
804 } else {
805 Amount::new(0)
806 }
807 }
808
809 pub fn change_amount_without_swap(&self) -> Amount {
811 use cdk::nuts::nut00::ProofsMethods;
812 let all_proofs_total = self.proofs.total_amount().unwrap_or(cdk::Amount::ZERO)
813 + self
814 .proofs_to_swap
815 .total_amount()
816 .unwrap_or(cdk::Amount::ZERO);
817 let needed =
818 self.quote.amount + self.quote.fee_reserve + self.input_fee_without_swap.into();
819 all_proofs_total
820 .checked_sub(needed)
821 .map(|a| a.into())
822 .unwrap_or(Amount::new(0))
823 }
824
825 pub fn proofs(&self) -> Proofs {
827 self.proofs.iter().cloned().map(|p| p.into()).collect()
828 }
829
830 pub async fn confirm(&self) -> Result<FinalizedMelt, FfiError> {
832 self.confirm_with_options(MeltConfirmOptions::default())
833 .await
834 }
835
836 pub async fn confirm_with_options(
838 &self,
839 options: MeltConfirmOptions,
840 ) -> Result<FinalizedMelt, FfiError> {
841 let finalized = self
842 .wallet
843 .confirm_prepared_melt_with_options(
844 self.operation_id,
845 self.quote.clone(),
846 self.proofs.clone(),
847 self.proofs_to_swap.clone(),
848 self.input_fee.into(),
849 self.input_fee_without_swap.into(),
850 self.metadata.clone(),
851 options.into(),
852 )
853 .await?;
854
855 Ok(finalized.into())
856 }
857
858 pub async fn confirm_prefer_async(&self) -> Result<MeltConfirmOutcome, FfiError> {
871 self.confirm_prefer_async_with_options(MeltConfirmOptions::default())
872 .await
873 }
874
875 pub async fn cancel(&self) -> Result<(), FfiError> {
877 self.wallet
878 .cancel_prepared_melt(
879 self.operation_id,
880 self.proofs.clone(),
881 self.proofs_to_swap.clone(),
882 )
883 .await?;
884 Ok(())
885 }
886}
887
888#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Enum)]
890pub enum MeltOptions {
891 Mpp { amount: Amount },
893 Amountless { amount_msat: Amount },
895}
896
897impl From<MeltOptions> for cdk::nuts::MeltOptions {
898 fn from(opts: MeltOptions) -> Self {
899 match opts {
900 MeltOptions::Mpp { amount } => {
901 let cdk_amount: cdk::Amount = amount.into();
902 cdk::nuts::MeltOptions::new_mpp(cdk_amount)
903 }
904 MeltOptions::Amountless { amount_msat } => {
905 let cdk_amount: cdk::Amount = amount_msat.into();
906 cdk::nuts::MeltOptions::new_amountless(cdk_amount)
907 }
908 }
909 }
910}
911
912impl From<cdk::nuts::MeltOptions> for MeltOptions {
913 fn from(opts: cdk::nuts::MeltOptions) -> Self {
914 match opts {
915 cdk::nuts::MeltOptions::Mpp { mpp } => MeltOptions::Mpp {
916 amount: mpp.amount.into(),
917 },
918 cdk::nuts::MeltOptions::Amountless { amountless } => MeltOptions::Amountless {
919 amount_msat: amountless.amount_msat.into(),
920 },
921 }
922 }
923}
924
925#[derive(Debug, Clone, uniffi::Record)]
927pub struct Restored {
928 pub spent: Amount,
929 pub unspent: Amount,
930 pub pending: Amount,
931}
932
933impl From<cdk_common::wallet::Restored> for Restored {
934 fn from(restored: cdk_common::wallet::Restored) -> Self {
935 Self {
936 spent: restored.spent.into(),
937 unspent: restored.unspent.into(),
938 pending: restored.pending.into(),
939 }
940 }
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, uniffi::Record)]
945pub struct RecoveryReport {
946 pub recovered: u64,
948 pub compensated: u64,
950 pub skipped: u64,
952 pub failed: u64,
954}
955
956impl From<cdk::wallet::RecoveryReport> for RecoveryReport {
957 fn from(report: cdk::wallet::RecoveryReport) -> Self {
958 Self {
959 recovered: report.recovered as u64,
960 compensated: report.compensated as u64,
961 skipped: report.skipped as u64,
962 failed: report.failed as u64,
963 }
964 }
965}
966
967#[derive(Debug, Clone, Default, Serialize, Deserialize, uniffi::Record)]
969pub struct MeltConfirmOptions {
970 pub skip_swap: bool,
973}
974
975impl From<MeltConfirmOptions> for cdk::wallet::MeltConfirmOptions {
976 fn from(opts: MeltConfirmOptions) -> Self {
977 cdk::wallet::MeltConfirmOptions {
978 skip_swap: opts.skip_swap,
979 }
980 }
981}
982
983impl From<cdk::wallet::MeltConfirmOptions> for MeltConfirmOptions {
984 fn from(opts: cdk::wallet::MeltConfirmOptions) -> Self {
985 Self {
986 skip_swap: opts.skip_swap,
987 }
988 }
989}
990
991#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)]
993pub struct WalletKey {
994 pub mint_url: MintUrl,
996 pub unit: CurrencyUnit,
998}
999
1000impl TryFrom<WalletKey> for cdk::WalletKey {
1001 type Error = FfiError;
1002
1003 fn try_from(value: WalletKey) -> Result<Self, Self::Error> {
1004 Ok(Self {
1005 mint_url: value.mint_url.try_into()?,
1006 unit: value.unit.into(),
1007 })
1008 }
1009}
1010
1011impl From<cdk::WalletKey> for WalletKey {
1012 fn from(value: cdk::WalletKey) -> Self {
1013 Self {
1014 mint_url: value.mint_url.into(),
1015 unit: value.unit.into(),
1016 }
1017 }
1018}