1use std::collections::HashMap;
4use std::sync::Arc;
5
6use cdk_common::database::WalletDatabase as CdkWalletDatabase;
7use cdk_common::wallet::WalletSaga;
8use cdk_sql_common::pool::DatabasePool;
9use cdk_sql_common::SQLWalletDatabase;
10
11use crate::error::FfiError;
12#[cfg(feature = "postgres")]
13use crate::postgres::WalletPostgresDatabase;
14use crate::sqlite::WalletSqliteDatabase;
15use crate::types::*;
16
17#[uniffi::export(with_foreign)]
20#[async_trait::async_trait]
21pub trait WalletDatabase: Send + Sync {
22 async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError>;
26
27 async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError>;
29
30 async fn get_mint_keysets(
32 &self,
33 mint_url: MintUrl,
34 ) -> Result<Option<Vec<KeySetInfo>>, FfiError>;
35
36 async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
38
39 async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
41
42 async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
44
45 async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
48
49 async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
51
52 async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError>;
54
55 async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
57
58 async fn get_proofs(
60 &self,
61 mint_url: Option<MintUrl>,
62 unit: Option<CurrencyUnit>,
63 state: Option<Vec<ProofState>>,
64 spending_conditions: Option<Vec<SpendingConditions>>,
65 ) -> Result<Vec<ProofInfo>, FfiError>;
66
67 async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError>;
69
70 async fn get_balance(
72 &self,
73 mint_url: Option<MintUrl>,
74 unit: Option<CurrencyUnit>,
75 state: Option<Vec<ProofState>>,
76 ) -> Result<u64, FfiError>;
77
78 async fn get_transaction(
80 &self,
81 transaction_id: TransactionId,
82 ) -> Result<Option<Transaction>, FfiError>;
83
84 async fn list_transactions(
86 &self,
87 mint_url: Option<MintUrl>,
88 direction: Option<TransactionDirection>,
89 unit: Option<CurrencyUnit>,
90 ) -> Result<Vec<Transaction>, FfiError>;
91
92 async fn kv_read(
94 &self,
95 primary_namespace: String,
96 secondary_namespace: String,
97 key: String,
98 ) -> Result<Option<Vec<u8>>, FfiError>;
99
100 async fn kv_list(
102 &self,
103 primary_namespace: String,
104 secondary_namespace: String,
105 ) -> Result<Vec<String>, FfiError>;
106
107 async fn kv_write(
109 &self,
110 primary_namespace: String,
111 secondary_namespace: String,
112 key: String,
113 value: Vec<u8>,
114 ) -> Result<(), FfiError>;
115
116 async fn kv_remove(
118 &self,
119 primary_namespace: String,
120 secondary_namespace: String,
121 key: String,
122 ) -> Result<(), FfiError>;
123
124 async fn update_proofs(
128 &self,
129 added: Vec<ProofInfo>,
130 removed_ys: Vec<PublicKey>,
131 ) -> Result<(), FfiError>;
132
133 async fn update_proofs_state(
135 &self,
136 ys: Vec<PublicKey>,
137 state: ProofState,
138 ) -> Result<(), FfiError>;
139
140 async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError>;
142
143 async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError>;
145
146 async fn update_mint_url(
148 &self,
149 old_mint_url: MintUrl,
150 new_mint_url: MintUrl,
151 ) -> Result<(), FfiError>;
152
153 async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError>;
155
156 async fn add_mint(
158 &self,
159 mint_url: MintUrl,
160 mint_info: Option<MintInfo>,
161 ) -> Result<(), FfiError>;
162
163 async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError>;
165
166 async fn add_mint_keysets(
168 &self,
169 mint_url: MintUrl,
170 keysets: Vec<KeySetInfo>,
171 ) -> Result<(), FfiError>;
172
173 async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError>;
175
176 async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError>;
178
179 async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError>;
181
182 async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError>;
184
185 async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError>;
187
188 async fn remove_keys(&self, id: Id) -> Result<(), FfiError>;
190
191 async fn add_saga(&self, saga_json: String) -> Result<(), FfiError>;
196
197 async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError>;
199
200 async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError>;
205
206 async fn delete_saga(&self, id: String) -> Result<(), FfiError>;
208
209 async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError>;
211
212 async fn reserve_proofs(
216 &self,
217 ys: Vec<PublicKey>,
218 operation_id: String,
219 ) -> Result<(), FfiError>;
220
221 async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError>;
223
224 async fn get_reserved_proofs(&self, operation_id: String) -> Result<Vec<ProofInfo>, FfiError>;
226
227 async fn reserve_melt_quote(
231 &self,
232 quote_id: String,
233 operation_id: String,
234 ) -> Result<(), FfiError>;
235
236 async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError>;
238
239 async fn reserve_mint_quote(
241 &self,
242 quote_id: String,
243 operation_id: String,
244 ) -> Result<(), FfiError>;
245
246 async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError>;
248}
249
250struct WalletDatabaseBridge {
253 ffi_db: Arc<dyn WalletDatabase>,
254}
255
256impl WalletDatabaseBridge {
257 fn new(ffi_db: Arc<dyn WalletDatabase>) -> Self {
258 Self { ffi_db }
259 }
260}
261
262impl std::fmt::Debug for WalletDatabaseBridge {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 write!(f, "WalletDatabaseBridge")
265 }
266}
267
268#[async_trait::async_trait]
269impl CdkWalletDatabase<cdk::cdk_database::Error> for WalletDatabaseBridge {
270 async fn kv_read(
271 &self,
272 primary_namespace: &str,
273 secondary_namespace: &str,
274 key: &str,
275 ) -> Result<Option<Vec<u8>>, cdk::cdk_database::Error> {
276 self.ffi_db
277 .kv_read(
278 primary_namespace.to_string(),
279 secondary_namespace.to_string(),
280 key.to_string(),
281 )
282 .await
283 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
284 }
285
286 async fn kv_list(
287 &self,
288 primary_namespace: &str,
289 secondary_namespace: &str,
290 ) -> Result<Vec<String>, cdk::cdk_database::Error> {
291 self.ffi_db
292 .kv_list(
293 primary_namespace.to_string(),
294 secondary_namespace.to_string(),
295 )
296 .await
297 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
298 }
299
300 async fn get_mint(
302 &self,
303 mint_url: cdk::mint_url::MintUrl,
304 ) -> Result<Option<cdk::nuts::MintInfo>, cdk::cdk_database::Error> {
305 let ffi_mint_url = mint_url.into();
306 let result = self
307 .ffi_db
308 .get_mint(ffi_mint_url)
309 .await
310 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
311 Ok(result.map(Into::into))
312 }
313
314 async fn get_mints(
315 &self,
316 ) -> Result<
317 HashMap<cdk::mint_url::MintUrl, Option<cdk::nuts::MintInfo>>,
318 cdk::cdk_database::Error,
319 > {
320 let result = self
321 .ffi_db
322 .get_mints()
323 .await
324 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
325
326 let mut cdk_result = HashMap::new();
327 for (ffi_mint_url, mint_info_opt) in result {
328 let cdk_url = ffi_mint_url
329 .try_into()
330 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
331 cdk_result.insert(cdk_url, mint_info_opt.map(Into::into));
332 }
333 Ok(cdk_result)
334 }
335
336 async fn get_mint_keysets(
338 &self,
339 mint_url: cdk::mint_url::MintUrl,
340 ) -> Result<Option<Vec<cdk::nuts::KeySetInfo>>, cdk::cdk_database::Error> {
341 let ffi_mint_url = mint_url.into();
342 let result = self
343 .ffi_db
344 .get_mint_keysets(ffi_mint_url)
345 .await
346 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
347 Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
348 }
349
350 async fn get_keyset_by_id(
351 &self,
352 keyset_id: &cdk::nuts::Id,
353 ) -> Result<Option<cdk::nuts::KeySetInfo>, cdk::cdk_database::Error> {
354 let ffi_id = (*keyset_id).into();
355 let result = self
356 .ffi_db
357 .get_keyset_by_id(ffi_id)
358 .await
359 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
360 Ok(result.map(Into::into))
361 }
362
363 async fn get_mint_quote(
365 &self,
366 quote_id: &str,
367 ) -> Result<Option<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
368 let result = self
369 .ffi_db
370 .get_mint_quote(quote_id.to_string())
371 .await
372 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
373 Ok(result
374 .map(|q| {
375 q.try_into()
376 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
377 })
378 .transpose()?)
379 }
380
381 async fn get_mint_quotes(
382 &self,
383 ) -> Result<Vec<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
384 let result = self
385 .ffi_db
386 .get_mint_quotes()
387 .await
388 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
389 Ok(result
390 .into_iter()
391 .map(|q| {
392 q.try_into()
393 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
394 })
395 .collect::<Result<Vec<_>, _>>()?)
396 }
397
398 async fn get_unissued_mint_quotes(
399 &self,
400 ) -> Result<Vec<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
401 let result = self
402 .ffi_db
403 .get_unissued_mint_quotes()
404 .await
405 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
406 Ok(result
407 .into_iter()
408 .map(|q| {
409 q.try_into()
410 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
411 })
412 .collect::<Result<Vec<_>, _>>()?)
413 }
414
415 async fn get_melt_quote(
417 &self,
418 quote_id: &str,
419 ) -> Result<Option<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
420 let result = self
421 .ffi_db
422 .get_melt_quote(quote_id.to_string())
423 .await
424 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
425 Ok(result
426 .map(|q| {
427 q.try_into()
428 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
429 })
430 .transpose()?)
431 }
432
433 async fn get_melt_quotes(
434 &self,
435 ) -> Result<Vec<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
436 let result = self
437 .ffi_db
438 .get_melt_quotes()
439 .await
440 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
441 Ok(result
442 .into_iter()
443 .map(|q| {
444 q.try_into()
445 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
446 })
447 .collect::<Result<Vec<_>, _>>()?)
448 }
449
450 async fn get_keys(
452 &self,
453 id: &cdk::nuts::Id,
454 ) -> Result<Option<cdk::nuts::Keys>, cdk::cdk_database::Error> {
455 let ffi_id: Id = (*id).into();
456 let result = self
457 .ffi_db
458 .get_keys(ffi_id)
459 .await
460 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
461
462 result
464 .map(|ffi_keys| {
465 ffi_keys
466 .try_into()
467 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
468 })
469 .transpose()
470 }
471
472 async fn get_proofs(
474 &self,
475 mint_url: Option<cdk::mint_url::MintUrl>,
476 unit: Option<cdk::nuts::CurrencyUnit>,
477 state: Option<Vec<cdk::nuts::State>>,
478 spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
479 ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
480 let ffi_mint_url = mint_url.map(Into::into);
481 let ffi_unit = unit.map(Into::into);
482 let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
483 let ffi_spending_conditions =
484 spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
485
486 let result = self
487 .ffi_db
488 .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending_conditions)
489 .await
490 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
491
492 let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
494 .into_iter()
495 .map(|info| {
496 Ok(cdk::types::ProofInfo {
497 proof: info.proof.try_into().map_err(|e: FfiError| {
498 cdk::cdk_database::Error::Database(e.to_string().into())
499 })?,
500 y: info.y.try_into().map_err(|e: FfiError| {
501 cdk::cdk_database::Error::Database(e.to_string().into())
502 })?,
503 mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
504 cdk::cdk_database::Error::Database(e.to_string().into())
505 })?,
506 state: info.state.into(),
507 spending_condition: info
508 .spending_condition
509 .map(|sc| sc.try_into())
510 .transpose()
511 .map_err(|e: FfiError| {
512 cdk::cdk_database::Error::Database(e.to_string().into())
513 })?,
514 unit: info.unit.into(),
515 used_by_operation: info
516 .used_by_operation
517 .map(|id| uuid::Uuid::parse_str(&id))
518 .transpose()
519 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
520 created_by_operation: info
521 .created_by_operation
522 .map(|id| uuid::Uuid::parse_str(&id))
523 .transpose()
524 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
525 })
526 })
527 .collect();
528
529 cdk_result
530 }
531
532 async fn get_proofs_by_ys(
533 &self,
534 ys: Vec<cdk::nuts::PublicKey>,
535 ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
536 let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
537
538 let result = self
539 .ffi_db
540 .get_proofs_by_ys(ffi_ys)
541 .await
542 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
543
544 let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
546 .into_iter()
547 .map(|info| {
548 Ok(cdk::types::ProofInfo {
549 proof: info.proof.try_into().map_err(|e: FfiError| {
550 cdk::cdk_database::Error::Database(e.to_string().into())
551 })?,
552 y: info.y.try_into().map_err(|e: FfiError| {
553 cdk::cdk_database::Error::Database(e.to_string().into())
554 })?,
555 mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
556 cdk::cdk_database::Error::Database(e.to_string().into())
557 })?,
558 state: info.state.into(),
559 spending_condition: info
560 .spending_condition
561 .map(|sc| sc.try_into())
562 .transpose()
563 .map_err(|e: FfiError| {
564 cdk::cdk_database::Error::Database(e.to_string().into())
565 })?,
566 unit: info.unit.into(),
567 used_by_operation: info
568 .used_by_operation
569 .map(|id| uuid::Uuid::parse_str(&id))
570 .transpose()
571 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
572 created_by_operation: info
573 .created_by_operation
574 .map(|id| uuid::Uuid::parse_str(&id))
575 .transpose()
576 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
577 })
578 })
579 .collect();
580
581 cdk_result
582 }
583
584 async fn get_balance(
585 &self,
586 mint_url: Option<cdk::mint_url::MintUrl>,
587 unit: Option<cdk::nuts::CurrencyUnit>,
588 state: Option<Vec<cdk::nuts::State>>,
589 ) -> Result<u64, cdk::cdk_database::Error> {
590 let ffi_mint_url = mint_url.map(Into::into);
591 let ffi_unit = unit.map(Into::into);
592 let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
593
594 self.ffi_db
595 .get_balance(ffi_mint_url, ffi_unit, ffi_state)
596 .await
597 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
598 }
599
600 async fn get_transaction(
602 &self,
603 transaction_id: cdk::wallet::types::TransactionId,
604 ) -> Result<Option<cdk::wallet::types::Transaction>, cdk::cdk_database::Error> {
605 let ffi_id = transaction_id.into();
606 let result = self
607 .ffi_db
608 .get_transaction(ffi_id)
609 .await
610 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
611
612 result
613 .map(|tx| tx.try_into())
614 .transpose()
615 .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
616 }
617
618 async fn list_transactions(
619 &self,
620 mint_url: Option<cdk::mint_url::MintUrl>,
621 direction: Option<cdk::wallet::types::TransactionDirection>,
622 unit: Option<cdk::nuts::CurrencyUnit>,
623 ) -> Result<Vec<cdk::wallet::types::Transaction>, cdk::cdk_database::Error> {
624 let ffi_mint_url = mint_url.map(Into::into);
625 let ffi_direction = direction.map(Into::into);
626 let ffi_unit = unit.map(Into::into);
627
628 let result = self
629 .ffi_db
630 .list_transactions(ffi_mint_url, ffi_direction, ffi_unit)
631 .await
632 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
633
634 result
635 .into_iter()
636 .map(|tx| tx.try_into())
637 .collect::<Result<Vec<_>, FfiError>>()
638 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
639 }
640
641 async fn update_proofs(
644 &self,
645 added: Vec<cdk::types::ProofInfo>,
646 removed_ys: Vec<cdk::nuts::PublicKey>,
647 ) -> Result<(), cdk::cdk_database::Error> {
648 let ffi_added: Vec<ProofInfo> = added.into_iter().map(Into::into).collect();
649 let ffi_removed_ys: Vec<PublicKey> = removed_ys.into_iter().map(Into::into).collect();
650 self.ffi_db
651 .update_proofs(ffi_added, ffi_removed_ys)
652 .await
653 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
654 }
655
656 async fn update_proofs_state(
657 &self,
658 ys: Vec<cdk::nuts::PublicKey>,
659 state: cdk::nuts::State,
660 ) -> Result<(), cdk::cdk_database::Error> {
661 let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
662 let ffi_state = state.into();
663 self.ffi_db
664 .update_proofs_state(ffi_ys, ffi_state)
665 .await
666 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
667 }
668
669 async fn add_transaction(
670 &self,
671 transaction: cdk::wallet::types::Transaction,
672 ) -> Result<(), cdk::cdk_database::Error> {
673 let ffi_transaction = transaction.into();
674 self.ffi_db
675 .add_transaction(ffi_transaction)
676 .await
677 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
678 }
679
680 async fn update_mint_url(
681 &self,
682 old_mint_url: cdk::mint_url::MintUrl,
683 new_mint_url: cdk::mint_url::MintUrl,
684 ) -> Result<(), cdk::cdk_database::Error> {
685 let ffi_old = old_mint_url.into();
686 let ffi_new = new_mint_url.into();
687 self.ffi_db
688 .update_mint_url(ffi_old, ffi_new)
689 .await
690 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
691 }
692
693 async fn increment_keyset_counter(
694 &self,
695 keyset_id: &cdk::nuts::Id,
696 count: u32,
697 ) -> Result<u32, cdk::cdk_database::Error> {
698 let ffi_id = (*keyset_id).into();
699 self.ffi_db
700 .increment_keyset_counter(ffi_id, count)
701 .await
702 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
703 }
704
705 async fn add_mint(
706 &self,
707 mint_url: cdk::mint_url::MintUrl,
708 mint_info: Option<cdk::nuts::MintInfo>,
709 ) -> Result<(), cdk::cdk_database::Error> {
710 let ffi_mint_url = mint_url.into();
711 let ffi_mint_info = mint_info.map(Into::into);
712 self.ffi_db
713 .add_mint(ffi_mint_url, ffi_mint_info)
714 .await
715 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
716 }
717
718 async fn remove_mint(
719 &self,
720 mint_url: cdk::mint_url::MintUrl,
721 ) -> Result<(), cdk::cdk_database::Error> {
722 let ffi_mint_url = mint_url.into();
723 self.ffi_db
724 .remove_mint(ffi_mint_url)
725 .await
726 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
727 }
728
729 async fn add_mint_keysets(
730 &self,
731 mint_url: cdk::mint_url::MintUrl,
732 keysets: Vec<cdk::nuts::KeySetInfo>,
733 ) -> Result<(), cdk::cdk_database::Error> {
734 let ffi_mint_url = mint_url.into();
735 let ffi_keysets: Vec<KeySetInfo> = keysets.into_iter().map(Into::into).collect();
736 self.ffi_db
737 .add_mint_keysets(ffi_mint_url, ffi_keysets)
738 .await
739 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
740 }
741
742 async fn add_mint_quote(
743 &self,
744 quote: cdk::wallet::MintQuote,
745 ) -> Result<(), cdk::cdk_database::Error> {
746 let ffi_quote = quote.into();
747 self.ffi_db
748 .add_mint_quote(ffi_quote)
749 .await
750 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
751 }
752
753 async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
754 self.ffi_db
755 .remove_mint_quote(quote_id.to_string())
756 .await
757 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
758 }
759
760 async fn add_melt_quote(
761 &self,
762 quote: cdk::wallet::MeltQuote,
763 ) -> Result<(), cdk::cdk_database::Error> {
764 let ffi_quote = quote.into();
765 self.ffi_db
766 .add_melt_quote(ffi_quote)
767 .await
768 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
769 }
770
771 async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
772 self.ffi_db
773 .remove_melt_quote(quote_id.to_string())
774 .await
775 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
776 }
777
778 async fn add_keys(&self, keyset: cdk::nuts::KeySet) -> Result<(), cdk::cdk_database::Error> {
779 let ffi_keyset: KeySet = keyset.into();
780 self.ffi_db
781 .add_keys(ffi_keyset)
782 .await
783 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
784 }
785
786 async fn remove_keys(&self, id: &cdk::nuts::Id) -> Result<(), cdk::cdk_database::Error> {
787 let ffi_id = (*id).into();
788 self.ffi_db
789 .remove_keys(ffi_id)
790 .await
791 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
792 }
793
794 async fn remove_transaction(
795 &self,
796 transaction_id: cdk::wallet::types::TransactionId,
797 ) -> Result<(), cdk::cdk_database::Error> {
798 let ffi_id = transaction_id.into();
799 self.ffi_db
800 .remove_transaction(ffi_id)
801 .await
802 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
803 }
804
805 async fn add_saga(&self, saga: WalletSaga) -> Result<(), cdk::cdk_database::Error> {
806 let json = serde_json::to_string(&saga)
807 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
808 self.ffi_db
809 .add_saga(json)
810 .await
811 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
812 }
813
814 async fn get_saga(
815 &self,
816 id: &uuid::Uuid,
817 ) -> Result<Option<WalletSaga>, cdk::cdk_database::Error> {
818 let json_opt = self
819 .ffi_db
820 .get_saga(id.to_string())
821 .await
822 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
823
824 match json_opt {
825 Some(json) => {
826 let saga: WalletSaga = serde_json::from_str(&json)
827 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
828 Ok(Some(saga))
829 }
830 None => Ok(None),
831 }
832 }
833
834 async fn update_saga(&self, saga: WalletSaga) -> Result<bool, cdk::cdk_database::Error> {
835 let json = serde_json::to_string(&saga)
836 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
837 self.ffi_db
838 .update_saga(json)
839 .await
840 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
841 }
842
843 async fn delete_saga(&self, id: &uuid::Uuid) -> Result<(), cdk::cdk_database::Error> {
844 self.ffi_db
845 .delete_saga(id.to_string())
846 .await
847 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
848 }
849
850 async fn get_incomplete_sagas(&self) -> Result<Vec<WalletSaga>, cdk::cdk_database::Error> {
851 let json_vec = self
852 .ffi_db
853 .get_incomplete_sagas()
854 .await
855 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
856
857 json_vec
858 .into_iter()
859 .map(|json| {
860 serde_json::from_str(&json)
861 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
862 })
863 .collect()
864 }
865
866 async fn reserve_proofs(
867 &self,
868 ys: Vec<cdk::nuts::PublicKey>,
869 operation_id: &uuid::Uuid,
870 ) -> Result<(), cdk::cdk_database::Error> {
871 let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
872 self.ffi_db
873 .reserve_proofs(ffi_ys, operation_id.to_string())
874 .await
875 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
876 }
877
878 async fn release_proofs(
879 &self,
880 operation_id: &uuid::Uuid,
881 ) -> Result<(), cdk::cdk_database::Error> {
882 self.ffi_db
883 .release_proofs(operation_id.to_string())
884 .await
885 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
886 }
887
888 async fn get_reserved_proofs(
889 &self,
890 operation_id: &uuid::Uuid,
891 ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
892 let result = self
893 .ffi_db
894 .get_reserved_proofs(operation_id.to_string())
895 .await
896 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
897
898 result
899 .into_iter()
900 .map(|info| {
901 Ok(cdk::types::ProofInfo {
902 proof: info.proof.try_into().map_err(|e: FfiError| {
903 cdk::cdk_database::Error::Database(e.to_string().into())
904 })?,
905 y: info.y.try_into().map_err(|e: FfiError| {
906 cdk::cdk_database::Error::Database(e.to_string().into())
907 })?,
908 mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
909 cdk::cdk_database::Error::Database(e.to_string().into())
910 })?,
911 state: info.state.into(),
912 spending_condition: info
913 .spending_condition
914 .map(|sc| sc.try_into())
915 .transpose()
916 .map_err(|e: FfiError| {
917 cdk::cdk_database::Error::Database(e.to_string().into())
918 })?,
919 unit: info.unit.into(),
920 used_by_operation: info
921 .used_by_operation
922 .map(|id| uuid::Uuid::parse_str(&id))
923 .transpose()
924 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
925 created_by_operation: info
926 .created_by_operation
927 .map(|id| uuid::Uuid::parse_str(&id))
928 .transpose()
929 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
930 })
931 })
932 .collect()
933 }
934
935 async fn reserve_melt_quote(
936 &self,
937 quote_id: &str,
938 operation_id: &uuid::Uuid,
939 ) -> Result<(), cdk::cdk_database::Error> {
940 self.ffi_db
941 .reserve_melt_quote(quote_id.to_string(), operation_id.to_string())
942 .await
943 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
944 }
945
946 async fn release_melt_quote(
947 &self,
948 operation_id: &uuid::Uuid,
949 ) -> Result<(), cdk::cdk_database::Error> {
950 self.ffi_db
951 .release_melt_quote(operation_id.to_string())
952 .await
953 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
954 }
955
956 async fn reserve_mint_quote(
957 &self,
958 quote_id: &str,
959 operation_id: &uuid::Uuid,
960 ) -> Result<(), cdk::cdk_database::Error> {
961 self.ffi_db
962 .reserve_mint_quote(quote_id.to_string(), operation_id.to_string())
963 .await
964 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
965 }
966
967 async fn release_mint_quote(
968 &self,
969 operation_id: &uuid::Uuid,
970 ) -> Result<(), cdk::cdk_database::Error> {
971 self.ffi_db
972 .release_mint_quote(operation_id.to_string())
973 .await
974 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
975 }
976
977 async fn kv_write(
978 &self,
979 primary_namespace: &str,
980 secondary_namespace: &str,
981 key: &str,
982 value: &[u8],
983 ) -> Result<(), cdk::cdk_database::Error> {
984 self.ffi_db
985 .kv_write(
986 primary_namespace.to_string(),
987 secondary_namespace.to_string(),
988 key.to_string(),
989 value.to_vec(),
990 )
991 .await
992 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
993 }
994
995 async fn kv_remove(
996 &self,
997 primary_namespace: &str,
998 secondary_namespace: &str,
999 key: &str,
1000 ) -> Result<(), cdk::cdk_database::Error> {
1001 self.ffi_db
1002 .kv_remove(
1003 primary_namespace.to_string(),
1004 secondary_namespace.to_string(),
1005 key.to_string(),
1006 )
1007 .await
1008 .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
1009 }
1010}
1011
1012pub(crate) struct FfiWalletSQLDatabase<RM>
1013where
1014 RM: DatabasePool + 'static,
1015{
1016 inner: SQLWalletDatabase<RM>,
1017}
1018
1019impl<RM> FfiWalletSQLDatabase<RM>
1020where
1021 RM: DatabasePool + 'static,
1022{
1023 pub fn new(inner: SQLWalletDatabase<RM>) -> Arc<Self> {
1025 Arc::new(Self { inner })
1026 }
1027}
1028
1029#[async_trait::async_trait]
1031impl<RM> WalletDatabase for FfiWalletSQLDatabase<RM>
1032where
1033 RM: DatabasePool + 'static,
1034{
1035 async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError> {
1038 let cdk_ys: Vec<cdk::nuts::PublicKey> = ys
1039 .into_iter()
1040 .map(|y| y.try_into())
1041 .collect::<Result<Vec<_>, FfiError>>()?;
1042
1043 let result = self
1044 .inner
1045 .get_proofs_by_ys(cdk_ys)
1046 .await
1047 .map_err(FfiError::internal)?;
1048
1049 Ok(result.into_iter().map(Into::into).collect())
1050 }
1051
1052 async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
1053 let cdk_mint_url = mint_url.try_into()?;
1054 let result = self
1055 .inner
1056 .get_mint(cdk_mint_url)
1057 .await
1058 .map_err(FfiError::internal)?;
1059 Ok(result.map(Into::into))
1060 }
1061
1062 async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
1063 let result = self.inner.get_mints().await.map_err(FfiError::internal)?;
1064 Ok(result
1065 .into_iter()
1066 .map(|(k, v)| (k.into(), v.map(Into::into)))
1067 .collect())
1068 }
1069
1070 async fn get_mint_keysets(
1071 &self,
1072 mint_url: MintUrl,
1073 ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
1074 let cdk_mint_url = mint_url.try_into()?;
1075 let result = self
1076 .inner
1077 .get_mint_keysets(cdk_mint_url)
1078 .await
1079 .map_err(FfiError::internal)?;
1080 Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
1081 }
1082
1083 async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
1084 let cdk_id = keyset_id.into();
1085 let result = self
1086 .inner
1087 .get_keyset_by_id(&cdk_id)
1088 .await
1089 .map_err(FfiError::internal)?;
1090 Ok(result.map(Into::into))
1091 }
1092
1093 async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
1094 let result = self
1095 .inner
1096 .get_mint_quote("e_id)
1097 .await
1098 .map_err(FfiError::internal)?;
1099 Ok(result.map(|q| q.into()))
1100 }
1101
1102 async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
1103 let result = self
1104 .inner
1105 .get_mint_quotes()
1106 .await
1107 .map_err(FfiError::internal)?;
1108 Ok(result.into_iter().map(|q| q.into()).collect())
1109 }
1110
1111 async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
1112 let result = self
1113 .inner
1114 .get_unissued_mint_quotes()
1115 .await
1116 .map_err(FfiError::internal)?;
1117 Ok(result.into_iter().map(|q| q.into()).collect())
1118 }
1119
1120 async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
1121 let result = self
1122 .inner
1123 .get_melt_quote("e_id)
1124 .await
1125 .map_err(FfiError::internal)?;
1126 Ok(result.map(|q| q.into()))
1127 }
1128
1129 async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
1130 let result = self
1131 .inner
1132 .get_melt_quotes()
1133 .await
1134 .map_err(FfiError::internal)?;
1135 Ok(result.into_iter().map(|q| q.into()).collect())
1136 }
1137
1138 async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
1139 let cdk_id = id.into();
1140 let result = self
1141 .inner
1142 .get_keys(&cdk_id)
1143 .await
1144 .map_err(FfiError::internal)?;
1145 Ok(result.map(Into::into))
1146 }
1147
1148 async fn get_proofs(
1149 &self,
1150 mint_url: Option<MintUrl>,
1151 unit: Option<CurrencyUnit>,
1152 state: Option<Vec<ProofState>>,
1153 spending_conditions: Option<Vec<SpendingConditions>>,
1154 ) -> Result<Vec<ProofInfo>, FfiError> {
1155 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
1156 let cdk_unit = unit.map(Into::into);
1157 let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
1158 let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
1159 spending_conditions
1160 .map(|sc| {
1161 sc.into_iter()
1162 .map(|c| c.try_into())
1163 .collect::<Result<Vec<_>, FfiError>>()
1164 })
1165 .transpose()?;
1166
1167 let result = self
1168 .inner
1169 .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
1170 .await
1171 .map_err(FfiError::internal)?;
1172
1173 Ok(result.into_iter().map(Into::into).collect())
1174 }
1175
1176 async fn get_balance(
1177 &self,
1178 mint_url: Option<MintUrl>,
1179 unit: Option<CurrencyUnit>,
1180 state: Option<Vec<ProofState>>,
1181 ) -> Result<u64, FfiError> {
1182 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
1183 let cdk_unit = unit.map(Into::into);
1184 let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
1185
1186 self.inner
1187 .get_balance(cdk_mint_url, cdk_unit, cdk_state)
1188 .await
1189 .map_err(FfiError::internal)
1190 }
1191
1192 async fn get_transaction(
1193 &self,
1194 transaction_id: TransactionId,
1195 ) -> Result<Option<Transaction>, FfiError> {
1196 let cdk_id = transaction_id.try_into()?;
1197 let result = self
1198 .inner
1199 .get_transaction(cdk_id)
1200 .await
1201 .map_err(FfiError::internal)?;
1202 Ok(result.map(Into::into))
1203 }
1204
1205 async fn list_transactions(
1206 &self,
1207 mint_url: Option<MintUrl>,
1208 direction: Option<TransactionDirection>,
1209 unit: Option<CurrencyUnit>,
1210 ) -> Result<Vec<Transaction>, FfiError> {
1211 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
1212 let cdk_direction = direction.map(Into::into);
1213 let cdk_unit = unit.map(Into::into);
1214
1215 let result = self
1216 .inner
1217 .list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
1218 .await
1219 .map_err(FfiError::internal)?;
1220
1221 Ok(result.into_iter().map(Into::into).collect())
1222 }
1223
1224 async fn kv_read(
1225 &self,
1226 primary_namespace: String,
1227 secondary_namespace: String,
1228 key: String,
1229 ) -> Result<Option<Vec<u8>>, FfiError> {
1230 self.inner
1231 .kv_read(&primary_namespace, &secondary_namespace, &key)
1232 .await
1233 .map_err(FfiError::internal)
1234 }
1235
1236 async fn kv_list(
1237 &self,
1238 primary_namespace: String,
1239 secondary_namespace: String,
1240 ) -> Result<Vec<String>, FfiError> {
1241 self.inner
1242 .kv_list(&primary_namespace, &secondary_namespace)
1243 .await
1244 .map_err(FfiError::internal)
1245 }
1246
1247 async fn kv_write(
1248 &self,
1249 primary_namespace: String,
1250 secondary_namespace: String,
1251 key: String,
1252 value: Vec<u8>,
1253 ) -> Result<(), FfiError> {
1254 self.inner
1255 .kv_write(&primary_namespace, &secondary_namespace, &key, &value)
1256 .await
1257 .map_err(FfiError::internal)
1258 }
1259
1260 async fn kv_remove(
1261 &self,
1262 primary_namespace: String,
1263 secondary_namespace: String,
1264 key: String,
1265 ) -> Result<(), FfiError> {
1266 self.inner
1267 .kv_remove(&primary_namespace, &secondary_namespace, &key)
1268 .await
1269 .map_err(FfiError::internal)
1270 }
1271
1272 async fn update_proofs(
1275 &self,
1276 added: Vec<ProofInfo>,
1277 removed_ys: Vec<PublicKey>,
1278 ) -> Result<(), FfiError> {
1279 let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
1280 .into_iter()
1281 .map(|info| {
1282 Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
1283 proof: info.proof.try_into()?,
1284 y: info.y.try_into()?,
1285 mint_url: info.mint_url.try_into()?,
1286 state: info.state.into(),
1287 spending_condition: info
1288 .spending_condition
1289 .map(|sc| sc.try_into())
1290 .transpose()?,
1291 unit: info.unit.into(),
1292 used_by_operation: info
1293 .used_by_operation
1294 .map(|id| uuid::Uuid::parse_str(&id))
1295 .transpose()
1296 .map_err(|e| FfiError::internal(e.to_string()))?,
1297 created_by_operation: info
1298 .created_by_operation
1299 .map(|id| uuid::Uuid::parse_str(&id))
1300 .transpose()
1301 .map_err(|e| FfiError::internal(e.to_string()))?,
1302 })
1303 })
1304 .collect();
1305 let cdk_added = cdk_added?;
1306
1307 let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
1308 removed_ys.into_iter().map(|pk| pk.try_into()).collect();
1309 let cdk_removed_ys = cdk_removed_ys?;
1310
1311 self.inner
1312 .update_proofs(cdk_added, cdk_removed_ys)
1313 .await
1314 .map_err(FfiError::internal)
1315 }
1316
1317 async fn update_proofs_state(
1318 &self,
1319 ys: Vec<PublicKey>,
1320 state: ProofState,
1321 ) -> Result<(), FfiError> {
1322 let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
1323 ys.into_iter().map(|pk| pk.try_into()).collect();
1324 let cdk_ys = cdk_ys?;
1325 let cdk_state = state.into();
1326
1327 self.inner
1328 .update_proofs_state(cdk_ys, cdk_state)
1329 .await
1330 .map_err(FfiError::internal)
1331 }
1332
1333 async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
1334 let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
1335 self.inner
1336 .add_transaction(cdk_transaction)
1337 .await
1338 .map_err(FfiError::internal)
1339 }
1340
1341 async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
1342 let cdk_id = transaction_id.try_into()?;
1343 self.inner
1344 .remove_transaction(cdk_id)
1345 .await
1346 .map_err(FfiError::internal)
1347 }
1348
1349 async fn update_mint_url(
1350 &self,
1351 old_mint_url: MintUrl,
1352 new_mint_url: MintUrl,
1353 ) -> Result<(), FfiError> {
1354 let cdk_old = old_mint_url.try_into()?;
1355 let cdk_new = new_mint_url.try_into()?;
1356 self.inner
1357 .update_mint_url(cdk_old, cdk_new)
1358 .await
1359 .map_err(FfiError::internal)
1360 }
1361
1362 async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
1363 let cdk_id = keyset_id.into();
1364 self.inner
1365 .increment_keyset_counter(&cdk_id, count)
1366 .await
1367 .map_err(FfiError::internal)
1368 }
1369
1370 async fn add_mint(
1371 &self,
1372 mint_url: MintUrl,
1373 mint_info: Option<MintInfo>,
1374 ) -> Result<(), FfiError> {
1375 let cdk_mint_url = mint_url.try_into()?;
1376 let cdk_mint_info = mint_info.map(Into::into);
1377 self.inner
1378 .add_mint(cdk_mint_url, cdk_mint_info)
1379 .await
1380 .map_err(FfiError::internal)
1381 }
1382
1383 async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
1384 let cdk_mint_url = mint_url.try_into()?;
1385 self.inner
1386 .remove_mint(cdk_mint_url)
1387 .await
1388 .map_err(FfiError::internal)
1389 }
1390
1391 async fn add_mint_keysets(
1392 &self,
1393 mint_url: MintUrl,
1394 keysets: Vec<KeySetInfo>,
1395 ) -> Result<(), FfiError> {
1396 let cdk_mint_url = mint_url.try_into()?;
1397 let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
1398 self.inner
1399 .add_mint_keysets(cdk_mint_url, cdk_keysets)
1400 .await
1401 .map_err(FfiError::internal)
1402 }
1403
1404 async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
1405 let cdk_quote = quote.try_into()?;
1406 self.inner
1407 .add_mint_quote(cdk_quote)
1408 .await
1409 .map_err(FfiError::internal)
1410 }
1411
1412 async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
1413 self.inner
1414 .remove_mint_quote("e_id)
1415 .await
1416 .map_err(FfiError::internal)
1417 }
1418
1419 async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
1420 let cdk_quote = quote.try_into()?;
1421 self.inner
1422 .add_melt_quote(cdk_quote)
1423 .await
1424 .map_err(FfiError::internal)
1425 }
1426
1427 async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
1428 self.inner
1429 .remove_melt_quote("e_id)
1430 .await
1431 .map_err(FfiError::internal)
1432 }
1433
1434 async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
1435 let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
1436 self.inner
1437 .add_keys(cdk_keyset)
1438 .await
1439 .map_err(FfiError::internal)
1440 }
1441
1442 async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
1443 let cdk_id = id.into();
1444 self.inner
1445 .remove_keys(&cdk_id)
1446 .await
1447 .map_err(FfiError::internal)
1448 }
1449
1450 async fn add_saga(&self, saga_json: String) -> Result<(), FfiError> {
1453 let saga: WalletSaga = serde_json::from_str(&saga_json).map_err(FfiError::internal)?;
1454 self.inner.add_saga(saga).await.map_err(FfiError::internal)
1455 }
1456
1457 async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError> {
1458 let id = uuid::Uuid::parse_str(&id).map_err(FfiError::internal)?;
1459 let result = self.inner.get_saga(&id).await.map_err(FfiError::internal)?;
1460
1461 match result {
1462 Some(saga) => {
1463 let json = serde_json::to_string(&saga).map_err(FfiError::internal)?;
1464 Ok(Some(json))
1465 }
1466 None => Ok(None),
1467 }
1468 }
1469
1470 async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError> {
1471 let saga: WalletSaga = serde_json::from_str(&saga_json).map_err(FfiError::internal)?;
1472 self.inner
1473 .update_saga(saga)
1474 .await
1475 .map_err(FfiError::internal)
1476 }
1477
1478 async fn delete_saga(&self, id: String) -> Result<(), FfiError> {
1479 let id = uuid::Uuid::parse_str(&id).map_err(FfiError::internal)?;
1480 self.inner
1481 .delete_saga(&id)
1482 .await
1483 .map_err(FfiError::internal)
1484 }
1485
1486 async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError> {
1487 let result = self
1488 .inner
1489 .get_incomplete_sagas()
1490 .await
1491 .map_err(FfiError::internal)?;
1492
1493 result
1494 .into_iter()
1495 .map(|saga| serde_json::to_string(&saga).map_err(FfiError::internal))
1496 .collect()
1497 }
1498
1499 async fn reserve_proofs(
1502 &self,
1503 ys: Vec<PublicKey>,
1504 operation_id: String,
1505 ) -> Result<(), FfiError> {
1506 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1507 let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
1508 ys.into_iter().map(|pk| pk.try_into()).collect();
1509 let cdk_ys = cdk_ys?;
1510 self.inner
1511 .reserve_proofs(cdk_ys, &operation_id)
1512 .await
1513 .map_err(FfiError::internal)
1514 }
1515
1516 async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError> {
1517 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1518 self.inner
1519 .release_proofs(&operation_id)
1520 .await
1521 .map_err(FfiError::internal)
1522 }
1523
1524 async fn get_reserved_proofs(&self, operation_id: String) -> Result<Vec<ProofInfo>, FfiError> {
1525 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1526 let result = self
1527 .inner
1528 .get_reserved_proofs(&operation_id)
1529 .await
1530 .map_err(FfiError::internal)?;
1531
1532 Ok(result.into_iter().map(Into::into).collect())
1533 }
1534
1535 async fn reserve_melt_quote(
1538 &self,
1539 quote_id: String,
1540 operation_id: String,
1541 ) -> Result<(), FfiError> {
1542 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1543 self.inner
1544 .reserve_melt_quote("e_id, &operation_id)
1545 .await
1546 .map_err(FfiError::internal)
1547 }
1548
1549 async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError> {
1550 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1551 self.inner
1552 .release_melt_quote(&operation_id)
1553 .await
1554 .map_err(FfiError::internal)
1555 }
1556
1557 async fn reserve_mint_quote(
1558 &self,
1559 quote_id: String,
1560 operation_id: String,
1561 ) -> Result<(), FfiError> {
1562 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1563 self.inner
1564 .reserve_mint_quote("e_id, &operation_id)
1565 .await
1566 .map_err(FfiError::internal)
1567 }
1568
1569 async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError> {
1570 let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
1571 self.inner
1572 .release_mint_quote(&operation_id)
1573 .await
1574 .map_err(FfiError::internal)
1575 }
1576}
1577
1578#[macro_export]
1587macro_rules! impl_ffi_wallet_database {
1588 ($wrapper_type:ty) => {
1589 #[uniffi::export(async_runtime = "tokio")]
1590 #[async_trait::async_trait]
1591 impl WalletDatabase for $wrapper_type {
1592 async fn get_proofs_by_ys(
1595 &self,
1596 ys: Vec<PublicKey>,
1597 ) -> Result<Vec<ProofInfo>, FfiError> {
1598 self.inner.get_proofs_by_ys(ys).await
1599 }
1600
1601 async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
1602 self.inner.get_mint(mint_url).await
1603 }
1604
1605 async fn get_mints(
1606 &self,
1607 ) -> Result<std::collections::HashMap<MintUrl, Option<MintInfo>>, FfiError> {
1608 self.inner.get_mints().await
1609 }
1610
1611 async fn get_mint_keysets(
1612 &self,
1613 mint_url: MintUrl,
1614 ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
1615 self.inner.get_mint_keysets(mint_url).await
1616 }
1617
1618 async fn get_keyset_by_id(
1619 &self,
1620 keyset_id: Id,
1621 ) -> Result<Option<KeySetInfo>, FfiError> {
1622 self.inner.get_keyset_by_id(keyset_id).await
1623 }
1624
1625 async fn get_mint_quote(
1626 &self,
1627 quote_id: String,
1628 ) -> Result<Option<MintQuote>, FfiError> {
1629 self.inner.get_mint_quote(quote_id).await
1630 }
1631
1632 async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
1633 self.inner.get_mint_quotes().await
1634 }
1635
1636 async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
1637 self.inner.get_unissued_mint_quotes().await
1638 }
1639
1640 async fn get_melt_quote(
1641 &self,
1642 quote_id: String,
1643 ) -> Result<Option<MeltQuote>, FfiError> {
1644 self.inner.get_melt_quote(quote_id).await
1645 }
1646
1647 async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
1648 self.inner.get_melt_quotes().await
1649 }
1650
1651 async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
1652 self.inner.get_keys(id).await
1653 }
1654
1655 async fn get_proofs(
1656 &self,
1657 mint_url: Option<MintUrl>,
1658 unit: Option<CurrencyUnit>,
1659 state: Option<Vec<ProofState>>,
1660 spending_conditions: Option<Vec<SpendingConditions>>,
1661 ) -> Result<Vec<ProofInfo>, FfiError> {
1662 self.inner
1663 .get_proofs(mint_url, unit, state, spending_conditions)
1664 .await
1665 }
1666
1667 async fn get_balance(
1668 &self,
1669 mint_url: Option<MintUrl>,
1670 unit: Option<CurrencyUnit>,
1671 state: Option<Vec<ProofState>>,
1672 ) -> Result<u64, FfiError> {
1673 self.inner.get_balance(mint_url, unit, state).await
1674 }
1675
1676 async fn get_transaction(
1677 &self,
1678 transaction_id: TransactionId,
1679 ) -> Result<Option<Transaction>, FfiError> {
1680 self.inner.get_transaction(transaction_id).await
1681 }
1682
1683 async fn list_transactions(
1684 &self,
1685 mint_url: Option<MintUrl>,
1686 direction: Option<TransactionDirection>,
1687 unit: Option<CurrencyUnit>,
1688 ) -> Result<Vec<Transaction>, FfiError> {
1689 self.inner
1690 .list_transactions(mint_url, direction, unit)
1691 .await
1692 }
1693
1694 async fn kv_read(
1695 &self,
1696 primary_namespace: String,
1697 secondary_namespace: String,
1698 key: String,
1699 ) -> Result<Option<Vec<u8>>, FfiError> {
1700 self.inner
1701 .kv_read(primary_namespace, secondary_namespace, key)
1702 .await
1703 }
1704
1705 async fn kv_list(
1706 &self,
1707 primary_namespace: String,
1708 secondary_namespace: String,
1709 ) -> Result<Vec<String>, FfiError> {
1710 self.inner
1711 .kv_list(primary_namespace, secondary_namespace)
1712 .await
1713 }
1714
1715 async fn kv_write(
1716 &self,
1717 primary_namespace: String,
1718 secondary_namespace: String,
1719 key: String,
1720 value: Vec<u8>,
1721 ) -> Result<(), FfiError> {
1722 self.inner
1723 .kv_write(primary_namespace, secondary_namespace, key, value)
1724 .await
1725 }
1726
1727 async fn kv_remove(
1728 &self,
1729 primary_namespace: String,
1730 secondary_namespace: String,
1731 key: String,
1732 ) -> Result<(), FfiError> {
1733 self.inner
1734 .kv_remove(primary_namespace, secondary_namespace, key)
1735 .await
1736 }
1737
1738 async fn update_proofs(
1741 &self,
1742 added: Vec<ProofInfo>,
1743 removed_ys: Vec<PublicKey>,
1744 ) -> Result<(), FfiError> {
1745 self.inner.update_proofs(added, removed_ys).await
1746 }
1747
1748 async fn update_proofs_state(
1749 &self,
1750 ys: Vec<PublicKey>,
1751 state: ProofState,
1752 ) -> Result<(), FfiError> {
1753 self.inner.update_proofs_state(ys, state).await
1754 }
1755
1756 async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
1757 self.inner.add_transaction(transaction).await
1758 }
1759
1760 async fn remove_transaction(
1761 &self,
1762 transaction_id: TransactionId,
1763 ) -> Result<(), FfiError> {
1764 self.inner.remove_transaction(transaction_id).await
1765 }
1766
1767 async fn update_mint_url(
1768 &self,
1769 old_mint_url: MintUrl,
1770 new_mint_url: MintUrl,
1771 ) -> Result<(), FfiError> {
1772 self.inner.update_mint_url(old_mint_url, new_mint_url).await
1773 }
1774
1775 async fn increment_keyset_counter(
1776 &self,
1777 keyset_id: Id,
1778 count: u32,
1779 ) -> Result<u32, FfiError> {
1780 self.inner.increment_keyset_counter(keyset_id, count).await
1781 }
1782
1783 async fn add_mint(
1784 &self,
1785 mint_url: MintUrl,
1786 mint_info: Option<MintInfo>,
1787 ) -> Result<(), FfiError> {
1788 self.inner.add_mint(mint_url, mint_info).await
1789 }
1790
1791 async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
1792 self.inner.remove_mint(mint_url).await
1793 }
1794
1795 async fn add_mint_keysets(
1796 &self,
1797 mint_url: MintUrl,
1798 keysets: Vec<KeySetInfo>,
1799 ) -> Result<(), FfiError> {
1800 self.inner.add_mint_keysets(mint_url, keysets).await
1801 }
1802
1803 async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
1804 self.inner.add_mint_quote(quote).await
1805 }
1806
1807 async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
1808 self.inner.remove_mint_quote(quote_id).await
1809 }
1810
1811 async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
1812 self.inner.add_melt_quote(quote).await
1813 }
1814
1815 async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
1816 self.inner.remove_melt_quote(quote_id).await
1817 }
1818
1819 async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
1820 self.inner.add_keys(keyset).await
1821 }
1822
1823 async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
1824 self.inner.remove_keys(id).await
1825 }
1826
1827 async fn add_saga(&self, saga_json: String) -> Result<(), FfiError> {
1830 self.inner.add_saga(saga_json).await
1831 }
1832
1833 async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError> {
1834 self.inner.get_saga(id).await
1835 }
1836
1837 async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError> {
1838 self.inner.update_saga(saga_json).await
1839 }
1840
1841 async fn delete_saga(&self, id: String) -> Result<(), FfiError> {
1842 self.inner.delete_saga(id).await
1843 }
1844
1845 async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError> {
1846 self.inner.get_incomplete_sagas().await
1847 }
1848
1849 async fn reserve_proofs(
1852 &self,
1853 ys: Vec<PublicKey>,
1854 operation_id: String,
1855 ) -> Result<(), FfiError> {
1856 self.inner.reserve_proofs(ys, operation_id).await
1857 }
1858
1859 async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError> {
1860 self.inner.release_proofs(operation_id).await
1861 }
1862
1863 async fn get_reserved_proofs(
1864 &self,
1865 operation_id: String,
1866 ) -> Result<Vec<ProofInfo>, FfiError> {
1867 self.inner.get_reserved_proofs(operation_id).await
1868 }
1869
1870 async fn reserve_melt_quote(
1873 &self,
1874 quote_id: String,
1875 operation_id: String,
1876 ) -> Result<(), FfiError> {
1877 self.inner.reserve_melt_quote(quote_id, operation_id).await
1878 }
1879
1880 async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError> {
1881 self.inner.release_melt_quote(operation_id).await
1882 }
1883
1884 async fn reserve_mint_quote(
1885 &self,
1886 quote_id: String,
1887 operation_id: String,
1888 ) -> Result<(), FfiError> {
1889 self.inner.reserve_mint_quote(quote_id, operation_id).await
1890 }
1891
1892 async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError> {
1893 self.inner.release_mint_quote(operation_id).await
1894 }
1895 }
1896 };
1897}
1898
1899#[derive(uniffi::Enum, Clone)]
1901pub enum WalletDbBackend {
1902 Sqlite {
1903 path: String,
1904 },
1905 #[cfg(feature = "postgres")]
1906 Postgres {
1907 url: String,
1908 },
1909}
1910
1911#[uniffi::export]
1913pub fn create_wallet_db(backend: WalletDbBackend) -> Result<Arc<dyn WalletDatabase>, FfiError> {
1914 match backend {
1915 WalletDbBackend::Sqlite { path } => {
1916 let sqlite = WalletSqliteDatabase::new(path)?;
1917 Ok(sqlite as Arc<dyn WalletDatabase>)
1918 }
1919 #[cfg(feature = "postgres")]
1920 WalletDbBackend::Postgres { url } => {
1921 let pg = WalletPostgresDatabase::new(url)?;
1922 Ok(pg as Arc<dyn WalletDatabase>)
1923 }
1924 }
1925}
1926
1927pub fn create_cdk_database_from_ffi(
1929 ffi_db: Arc<dyn WalletDatabase>,
1930) -> Arc<dyn CdkWalletDatabase<cdk::cdk_database::Error> + Send + Sync> {
1931 Arc::new(WalletDatabaseBridge::new(ffi_db))
1932}