Skip to main content

cdk_ffi/
wallet_trait.rs

1//! WalletTrait implementation for the FFI Wallet.
2//!
3//! Implements `cdk_common::wallet::Wallet` for the FFI `Wallet` type,
4//! converting between FFI types and CDK types at the boundary.
5
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use async_trait::async_trait;
10#[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
11use cdk_common::bitcoin;
12use cdk_common::wallet::Wallet as WalletTraitDef;
13
14use crate::error::FfiError;
15use crate::types::*;
16use crate::wallet::Wallet;
17
18#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
19#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
20impl WalletTraitDef for Wallet {
21    type Error = FfiError;
22    type Amount = Amount;
23    type MintUrl = MintUrl;
24    type CurrencyUnit = CurrencyUnit;
25    type MintInfo = MintInfo;
26    type KeySetInfo = KeySetInfo;
27    type MintQuote = MintQuote;
28    type MeltQuote = MeltQuote;
29    type PaymentMethod = PaymentMethod;
30    type MeltOptions = MeltOptions;
31    type OperationId = String;
32    type PreparedSend<'a> = Arc<PreparedSend>;
33    type PreparedMelt<'a> = PreparedMelt;
34    type Subscription = Arc<crate::types::ActiveSubscription>;
35    type SubscribeParams = crate::types::SubscribeParams;
36    type RecoveryReport = RecoveryReport;
37
38    fn mint_url(&self) -> Self::MintUrl {
39        self.inner().mint_url.clone().into()
40    }
41
42    fn unit(&self) -> Self::CurrencyUnit {
43        self.inner().unit.clone().into()
44    }
45
46    async fn total_balance(&self) -> Result<Self::Amount, Self::Error> {
47        let balance = WalletTraitDef::total_balance(self.inner().as_ref()).await?;
48        Ok(balance.into())
49    }
50
51    async fn total_pending_balance(&self) -> Result<Self::Amount, Self::Error> {
52        let balance = WalletTraitDef::total_pending_balance(self.inner().as_ref()).await?;
53        Ok(balance.into())
54    }
55
56    async fn total_reserved_balance(&self) -> Result<Self::Amount, Self::Error> {
57        let balance = WalletTraitDef::total_reserved_balance(self.inner().as_ref()).await?;
58        Ok(balance.into())
59    }
60
61    async fn fetch_mint_info(&self) -> Result<Option<Self::MintInfo>, Self::Error> {
62        let info = WalletTraitDef::fetch_mint_info(self.inner().as_ref()).await?;
63        Ok(info.map(Into::into))
64    }
65
66    async fn load_mint_info(&self) -> Result<Self::MintInfo, Self::Error> {
67        let info = WalletTraitDef::load_mint_info(self.inner().as_ref()).await?;
68        Ok(info.into())
69    }
70
71    async fn refresh_keysets(&self) -> Result<Vec<Self::KeySetInfo>, Self::Error> {
72        let keysets = WalletTraitDef::refresh_keysets(self.inner().as_ref()).await?;
73        Ok(keysets.into_iter().map(Into::into).collect())
74    }
75
76    async fn get_active_keyset(&self) -> Result<Self::KeySetInfo, Self::Error> {
77        let keyset = WalletTraitDef::get_active_keyset(self.inner().as_ref()).await?;
78        Ok(keyset.into())
79    }
80
81    async fn load_keyset_keys(
82        &self,
83        keyset_id: cdk_common::nuts::Id,
84    ) -> Result<cdk_common::nuts::Keys, Self::Error> {
85        let keys = WalletTraitDef::load_keyset_keys(self.inner().as_ref(), keyset_id).await?;
86        Ok(keys)
87    }
88
89    async fn get_mint_keysets(
90        &self,
91        filter: cdk_common::wallet::KeysetFilter,
92    ) -> Result<Vec<Self::KeySetInfo>, Self::Error> {
93        let keysets = WalletTraitDef::get_mint_keysets(self.inner().as_ref(), filter).await?;
94        Ok(keysets.into_iter().map(Into::into).collect())
95    }
96
97    async fn fetch_active_keyset(&self) -> Result<Self::KeySetInfo, Self::Error> {
98        let keyset = WalletTraitDef::fetch_active_keyset(self.inner().as_ref()).await?;
99        Ok(keyset.into())
100    }
101
102    async fn get_keyset_fees_and_amounts(
103        &self,
104    ) -> Result<cdk_common::amount::KeysetFeeAndAmounts, Self::Error> {
105        let fees = WalletTraitDef::get_keyset_fees_and_amounts(self.inner().as_ref()).await?;
106        Ok(fees)
107    }
108
109    async fn get_keyset_count_fee(
110        &self,
111        keyset_id: &cdk_common::nuts::Id,
112        count: u64,
113    ) -> Result<Self::Amount, Self::Error> {
114        let fee =
115            WalletTraitDef::get_keyset_count_fee(self.inner().as_ref(), keyset_id, count).await?;
116        Ok(fee.into())
117    }
118
119    async fn get_keyset_fees_and_amounts_by_id(
120        &self,
121        keyset_id: cdk_common::nuts::Id,
122    ) -> Result<cdk_common::amount::FeeAndAmounts, Self::Error> {
123        let fee =
124            WalletTraitDef::get_keyset_fees_and_amounts_by_id(self.inner().as_ref(), keyset_id)
125                .await?;
126        Ok(fee)
127    }
128
129    async fn mint_quote(
130        &self,
131        method: Self::PaymentMethod,
132        amount: Option<Self::Amount>,
133        description: Option<String>,
134        extra: Option<String>,
135    ) -> Result<Self::MintQuote, Self::Error> {
136        let quote = WalletTraitDef::mint_quote(
137            self.inner().as_ref(),
138            method.into(),
139            amount.map(Into::into),
140            description,
141            extra,
142        )
143        .await?;
144        Ok(quote.into())
145    }
146
147    async fn melt_quote(
148        &self,
149        method: Self::PaymentMethod,
150        request: String,
151        options: Option<Self::MeltOptions>,
152        extra: Option<String>,
153    ) -> Result<Self::MeltQuote, Self::Error> {
154        let quote = WalletTraitDef::melt_quote(
155            self.inner().as_ref(),
156            method.into(),
157            request,
158            options.map(Into::into),
159            extra,
160        )
161        .await?;
162        Ok(quote.into())
163    }
164
165    async fn list_transactions(
166        &self,
167        direction: Option<cdk_common::wallet::TransactionDirection>,
168    ) -> Result<Vec<cdk_common::wallet::Transaction>, Self::Error> {
169        let txs = WalletTraitDef::list_transactions(self.inner().as_ref(), direction).await?;
170        Ok(txs)
171    }
172
173    async fn get_transaction(
174        &self,
175        id: cdk_common::wallet::TransactionId,
176    ) -> Result<Option<cdk_common::wallet::Transaction>, Self::Error> {
177        let tx = WalletTraitDef::get_transaction(self.inner().as_ref(), id).await?;
178        Ok(tx)
179    }
180
181    async fn get_proofs_for_transaction(
182        &self,
183        id: cdk_common::wallet::TransactionId,
184    ) -> Result<cdk_common::Proofs, Self::Error> {
185        let proofs = WalletTraitDef::get_proofs_for_transaction(self.inner().as_ref(), id).await?;
186        Ok(proofs)
187    }
188
189    async fn revert_transaction(
190        &self,
191        id: cdk_common::wallet::TransactionId,
192    ) -> Result<(), Self::Error> {
193        WalletTraitDef::revert_transaction(self.inner().as_ref(), id).await?;
194        Ok(())
195    }
196
197    async fn check_all_pending_proofs(&self) -> Result<Self::Amount, Self::Error> {
198        let amount = WalletTraitDef::check_all_pending_proofs(self.inner().as_ref()).await?;
199        Ok(amount.into())
200    }
201
202    async fn recover_incomplete_sagas(&self) -> Result<Self::RecoveryReport, Self::Error> {
203        let report = WalletTraitDef::recover_incomplete_sagas(self.inner().as_ref()).await?;
204        Ok(report.into())
205    }
206
207    async fn check_proofs_spent(
208        &self,
209        proofs: cdk_common::Proofs,
210    ) -> Result<Vec<cdk_common::nuts::nut07::ProofState>, Self::Error> {
211        let states = WalletTraitDef::check_proofs_spent(self.inner().as_ref(), proofs).await?;
212        Ok(states)
213    }
214
215    async fn get_keyset_fees_by_id(
216        &self,
217        keyset_id: cdk_common::nuts::Id,
218    ) -> Result<u64, Self::Error> {
219        let fee = WalletTraitDef::get_keyset_fees_by_id(self.inner().as_ref(), keyset_id).await?;
220        Ok(fee)
221    }
222
223    async fn calculate_fee(
224        &self,
225        proof_count: u64,
226        keyset_id: cdk_common::nuts::Id,
227    ) -> Result<Self::Amount, Self::Error> {
228        let fee =
229            WalletTraitDef::calculate_fee(self.inner().as_ref(), proof_count, keyset_id).await?;
230        Ok(fee.into())
231    }
232
233    async fn receive(
234        &self,
235        encoded_token: &str,
236        options: cdk_common::wallet::ReceiveOptions,
237    ) -> Result<Self::Amount, Self::Error> {
238        let amount = WalletTraitDef::receive(self.inner().as_ref(), encoded_token, options).await?;
239        Ok(amount.into())
240    }
241
242    async fn receive_proofs(
243        &self,
244        proofs: cdk_common::Proofs,
245        options: cdk_common::wallet::ReceiveOptions,
246        memo: Option<String>,
247        token: Option<String>,
248    ) -> Result<Self::Amount, Self::Error> {
249        let amount =
250            WalletTraitDef::receive_proofs(self.inner().as_ref(), proofs, options, memo, token)
251                .await?;
252        Ok(amount.into())
253    }
254
255    async fn prepare_send(
256        &self,
257        amount: Self::Amount,
258        options: cdk_common::wallet::SendOptions,
259    ) -> Result<Arc<PreparedSend>, Self::Error> {
260        let prepared = self.inner().prepare_send(amount.into(), options).await?;
261        Ok(Arc::new(PreparedSend::new(self.inner().clone(), &prepared)))
262    }
263
264    async fn get_pending_sends(&self) -> Result<Vec<String>, Self::Error> {
265        let ids = WalletTraitDef::get_pending_sends(self.inner().as_ref()).await?;
266        Ok(ids.into_iter().map(|id| id.to_string()).collect())
267    }
268
269    async fn revoke_send(&self, operation_id: String) -> Result<Self::Amount, Self::Error> {
270        let uuid = uuid::Uuid::parse_str(&operation_id)
271            .map_err(|e| FfiError::internal(format!("Invalid operation ID: {}", e)))?;
272        let amount = WalletTraitDef::revoke_send(self.inner().as_ref(), uuid).await?;
273        Ok(amount.into())
274    }
275
276    async fn check_send_status(&self, operation_id: String) -> Result<bool, Self::Error> {
277        let uuid = uuid::Uuid::parse_str(&operation_id)
278            .map_err(|e| FfiError::internal(format!("Invalid operation ID: {}", e)))?;
279        let claimed = WalletTraitDef::check_send_status(self.inner().as_ref(), uuid).await?;
280        Ok(claimed)
281    }
282
283    async fn mint(
284        &self,
285        quote_id: &str,
286        split_target: cdk_common::amount::SplitTarget,
287        spending_conditions: Option<cdk_common::nuts::SpendingConditions>,
288    ) -> Result<cdk_common::Proofs, Self::Error> {
289        let proofs = WalletTraitDef::mint(
290            self.inner().as_ref(),
291            quote_id,
292            split_target,
293            spending_conditions,
294        )
295        .await?;
296        Ok(proofs)
297    }
298
299    async fn check_mint_quote_status(
300        &self,
301        quote_id: &str,
302    ) -> Result<Self::MintQuote, Self::Error> {
303        let quote =
304            WalletTraitDef::check_mint_quote_status(self.inner().as_ref(), quote_id).await?;
305        Ok(quote.into())
306    }
307
308    async fn fetch_mint_quote(
309        &self,
310        quote_id: &str,
311        payment_method: Option<Self::PaymentMethod>,
312    ) -> Result<Self::MintQuote, Self::Error> {
313        let method = payment_method.map(Into::into);
314        let quote =
315            WalletTraitDef::fetch_mint_quote(self.inner().as_ref(), quote_id, method).await?;
316        Ok(quote.into())
317    }
318
319    async fn prepare_melt(
320        &self,
321        quote_id: &str,
322        metadata: HashMap<String, String>,
323    ) -> Result<PreparedMelt, Self::Error> {
324        let prepared = self.inner().prepare_melt(quote_id, metadata).await?;
325        Ok(PreparedMelt::new(self.inner().clone(), &prepared))
326    }
327
328    async fn prepare_melt_proofs(
329        &self,
330        quote_id: &str,
331        proofs: cdk_common::Proofs,
332        metadata: HashMap<String, String>,
333    ) -> Result<PreparedMelt, Self::Error> {
334        let prepared = self
335            .inner()
336            .prepare_melt_proofs(quote_id, proofs, metadata)
337            .await?;
338        Ok(PreparedMelt::new(self.inner().clone(), &prepared))
339    }
340
341    async fn prepare_melt_token(
342        &self,
343        quote_id: &str,
344        encoded_token: &str,
345        metadata: HashMap<String, String>,
346    ) -> Result<PreparedMelt, Self::Error> {
347        let prepared = self
348            .inner()
349            .prepare_melt_token(quote_id, encoded_token, metadata)
350            .await?;
351        Ok(PreparedMelt::new(self.inner().clone(), &prepared))
352    }
353
354    async fn swap(
355        &self,
356        amount: Option<Self::Amount>,
357        split_target: cdk_common::amount::SplitTarget,
358        input_proofs: cdk_common::Proofs,
359        spending_conditions: Option<cdk_common::nuts::SpendingConditions>,
360        include_fees: bool,
361        use_p2bk: bool,
362    ) -> Result<Option<cdk_common::Proofs>, Self::Error> {
363        let result = WalletTraitDef::swap(
364            self.inner().as_ref(),
365            amount.map(Into::into),
366            split_target,
367            input_proofs,
368            spending_conditions,
369            include_fees,
370            use_p2bk,
371        )
372        .await?;
373        Ok(result)
374    }
375
376    async fn set_cat(&self, cat: String) -> Result<(), Self::Error> {
377        WalletTraitDef::set_cat(self.inner().as_ref(), cat).await?;
378        Ok(())
379    }
380
381    async fn set_refresh_token(&self, refresh_token: String) -> Result<(), Self::Error> {
382        WalletTraitDef::set_refresh_token(self.inner().as_ref(), refresh_token).await?;
383        Ok(())
384    }
385
386    async fn refresh_access_token(&self) -> Result<(), Self::Error> {
387        WalletTraitDef::refresh_access_token(self.inner().as_ref()).await?;
388        Ok(())
389    }
390
391    async fn mint_blind_auth(
392        &self,
393        amount: Self::Amount,
394    ) -> Result<cdk_common::Proofs, Self::Error> {
395        let proofs = WalletTraitDef::mint_blind_auth(self.inner().as_ref(), amount.into()).await?;
396        Ok(proofs)
397    }
398
399    async fn get_unspent_auth_proofs(
400        &self,
401    ) -> Result<Vec<cdk_common::nuts::AuthProof>, Self::Error> {
402        let proofs = WalletTraitDef::get_unspent_auth_proofs(self.inner().as_ref()).await?;
403        Ok(proofs)
404    }
405
406    async fn restore(&self) -> Result<cdk_common::wallet::Restored, Self::Error> {
407        let restored = WalletTraitDef::restore(self.inner().as_ref()).await?;
408        Ok(restored)
409    }
410
411    async fn restore_with_opts(
412        &self,
413        opts: cdk_common::wallet::NUT13Options,
414    ) -> Result<cdk_common::wallet::Restored, Self::Error> {
415        let restored = WalletTraitDef::restore_with_opts(self.inner().as_ref(), opts).await?;
416        Ok(restored)
417    }
418
419    async fn verify_token_dleq(&self, token_str: &str) -> Result<(), Self::Error> {
420        WalletTraitDef::verify_token_dleq(self.inner().as_ref(), token_str).await?;
421        Ok(())
422    }
423
424    async fn pay_request(
425        &self,
426        request: cdk_common::nuts::nut18::PaymentRequest,
427        custom_amount: Option<Self::Amount>,
428    ) -> Result<(), Self::Error> {
429        WalletTraitDef::pay_request(
430            self.inner().as_ref(),
431            request,
432            custom_amount.map(Into::into),
433        )
434        .await?;
435        Ok(())
436    }
437
438    async fn subscribe_mint_quote_state(
439        &self,
440        quote_ids: Vec<String>,
441        method: Self::PaymentMethod,
442    ) -> Result<Arc<crate::types::ActiveSubscription>, Self::Error> {
443        let cdk_sub = WalletTraitDef::subscribe_mint_quote_state(
444            self.inner().as_ref(),
445            quote_ids,
446            method.into(),
447        )
448        .await?;
449        let sub_id = uuid::Uuid::new_v4().to_string();
450        Ok(Arc::new(crate::types::ActiveSubscription::new(
451            cdk_sub, sub_id,
452        )))
453    }
454
455    fn set_metadata_cache_ttl(&self, ttl_secs: Option<u64>) {
456        let ttl = ttl_secs.map(std::time::Duration::from_secs);
457        self.inner().set_metadata_cache_ttl(ttl);
458    }
459
460    async fn subscribe(
461        &self,
462        params: crate::types::SubscribeParams,
463    ) -> Result<Arc<crate::types::ActiveSubscription>, Self::Error> {
464        let cdk_params: cdk_common::subscription::WalletParams = params.into();
465        let sub_id = cdk_params.id.to_string();
466        let active_sub = WalletTraitDef::subscribe(self.inner().as_ref(), cdk_params).await?;
467        Ok(Arc::new(crate::types::ActiveSubscription::new(
468            active_sub, sub_id,
469        )))
470    }
471
472    #[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
473    async fn melt_bip353_quote(
474        &self,
475        bip353_address: &str,
476        amount_msat: Self::Amount,
477        network: bitcoin::Network,
478    ) -> Result<Self::MeltQuote, Self::Error> {
479        let cdk_amount: cdk_common::Amount = amount_msat.into();
480        let quote = self
481            .inner()
482            .melt_bip353_quote(bip353_address, cdk_amount, network)
483            .await?;
484        Ok(quote.into())
485    }
486
487    #[cfg(not(target_arch = "wasm32"))]
488    async fn melt_lightning_address_quote(
489        &self,
490        lightning_address: &str,
491        amount_msat: Self::Amount,
492    ) -> Result<Self::MeltQuote, Self::Error> {
493        let cdk_amount: cdk_common::Amount = amount_msat.into();
494        let quote = self
495            .inner()
496            .melt_lightning_address_quote(lightning_address, cdk_amount)
497            .await?;
498        Ok(quote.into())
499    }
500
501    #[cfg(all(feature = "bip353", not(target_arch = "wasm32")))]
502    async fn melt_human_readable_quote(
503        &self,
504        address: &str,
505        amount_msat: Self::Amount,
506        network: bitcoin::Network,
507    ) -> Result<Self::MeltQuote, Self::Error> {
508        let cdk_amount: cdk_common::Amount = amount_msat.into();
509        let quote = self
510            .inner()
511            .melt_human_readable_quote(address, cdk_amount, network)
512            .await?;
513        Ok(quote.into())
514    }
515
516    async fn get_proofs_by_states(
517        &self,
518        states: Vec<cdk_common::nuts::State>,
519    ) -> Result<cdk_common::Proofs, Self::Error> {
520        let proofs = WalletTraitDef::get_proofs_by_states(self.inner().as_ref(), states).await?;
521        Ok(proofs)
522    }
523
524    /// generates and stores public key in database
525    async fn generate_public_key(&self) -> Result<cdk::nuts::PublicKey, Self::Error> {
526        let quote = WalletTraitDef::generate_public_key(self.inner().as_ref()).await?;
527        Ok(quote)
528    }
529
530    /// gets public key by it's hex value
531    async fn get_public_key(
532        &self,
533        pubkey: &cdk::nuts::PublicKey,
534    ) -> Result<Option<cdk_common::wallet::P2PKSigningKey>, Self::Error> {
535        let pubkey = WalletTraitDef::get_public_key(self.inner().as_ref(), pubkey).await?;
536        Ok(pubkey)
537    }
538
539    /// gets list of stored public keys in database
540    async fn get_public_keys(
541        &self,
542    ) -> Result<Vec<cdk_common::wallet::P2PKSigningKey>, Self::Error> {
543        let pubkeys = WalletTraitDef::get_public_keys(self.inner().as_ref()).await?;
544        Ok(pubkeys)
545    }
546
547    /// Gets the latest generated P2PK signing key (most recently created)
548    async fn get_latest_public_key(
549        &self,
550    ) -> Result<Option<cdk_common::wallet::P2PKSigningKey>, Self::Error> {
551        let pubkey = WalletTraitDef::get_latest_public_key(self.inner().as_ref()).await?;
552        Ok(pubkey)
553    }
554
555    /// try to get secret key from p2pk signing key in localstore
556    async fn get_signing_key(
557        &self,
558        pubkey: &cdk::nuts::PublicKey,
559    ) -> Result<Option<cdk::nuts::SecretKey>, Self::Error> {
560        let signing_key = WalletTraitDef::get_signing_key(self.inner().as_ref(), pubkey).await?;
561        Ok(signing_key)
562    }
563}