1use std::collections::HashMap;
2use std::sync::Arc;
3
4use cdk_sqlite::wallet::WalletSqliteDatabase as CdkWalletSqliteDatabase;
5
6use crate::{
7 CurrencyUnit, FfiError, Id, KeySet, KeySetInfo, Keys, MeltQuote, MintInfo, MintQuote, MintUrl,
8 ProofInfo, ProofState, PublicKey, SpendingConditions, Transaction, TransactionDirection,
9 TransactionId, WalletDatabase,
10};
11
12#[derive(uniffi::Object)]
14pub struct WalletSqliteDatabase {
15 inner: Arc<CdkWalletSqliteDatabase>,
16}
17use cdk::cdk_database::WalletDatabase as CdkWalletDatabase;
18
19impl WalletSqliteDatabase {
20 }
22
23#[uniffi::export]
24impl WalletSqliteDatabase {
25 #[uniffi::constructor]
27 pub fn new(file_path: String) -> Result<Arc<Self>, FfiError> {
28 let db = match tokio::runtime::Handle::try_current() {
29 Ok(handle) => tokio::task::block_in_place(|| {
30 handle
31 .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await })
32 }),
33 Err(_) => {
34 tokio::runtime::Runtime::new()
36 .map_err(|e| FfiError::Database {
37 msg: format!("Failed to create runtime: {}", e),
38 })?
39 .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await })
40 }
41 }
42 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
43 Ok(Arc::new(Self {
44 inner: Arc::new(db),
45 }))
46 }
47
48 #[uniffi::constructor]
50 pub fn new_in_memory() -> Result<Arc<Self>, FfiError> {
51 let db = match tokio::runtime::Handle::try_current() {
52 Ok(handle) => tokio::task::block_in_place(|| {
53 handle.block_on(async move { cdk_sqlite::wallet::memory::empty().await })
54 }),
55 Err(_) => {
56 tokio::runtime::Runtime::new()
58 .map_err(|e| FfiError::Database {
59 msg: format!("Failed to create runtime: {}", e),
60 })?
61 .block_on(async move { cdk_sqlite::wallet::memory::empty().await })
62 }
63 }
64 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
65 Ok(Arc::new(Self {
66 inner: Arc::new(db),
67 }))
68 }
69}
70
71#[uniffi::export(async_runtime = "tokio")]
72#[async_trait::async_trait]
73impl WalletDatabase for WalletSqliteDatabase {
74 async fn add_mint(
76 &self,
77 mint_url: MintUrl,
78 mint_info: Option<MintInfo>,
79 ) -> Result<(), FfiError> {
80 let cdk_mint_url = mint_url.try_into()?;
81 let cdk_mint_info = mint_info.map(Into::into);
82 self.inner
83 .add_mint(cdk_mint_url, cdk_mint_info)
84 .await
85 .map_err(|e| FfiError::Database { msg: e.to_string() })
86 }
87
88 async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
89 let cdk_mint_url = mint_url.try_into()?;
90 self.inner
91 .remove_mint(cdk_mint_url)
92 .await
93 .map_err(|e| FfiError::Database { msg: e.to_string() })
94 }
95
96 async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
97 let cdk_mint_url = mint_url.try_into()?;
98 let result = self
99 .inner
100 .get_mint(cdk_mint_url)
101 .await
102 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
103 Ok(result.map(Into::into))
104 }
105
106 async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
107 let result = self
108 .inner
109 .get_mints()
110 .await
111 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
112 Ok(result
113 .into_iter()
114 .map(|(k, v)| (k.into(), v.map(Into::into)))
115 .collect())
116 }
117
118 async fn update_mint_url(
119 &self,
120 old_mint_url: MintUrl,
121 new_mint_url: MintUrl,
122 ) -> Result<(), FfiError> {
123 let cdk_old_mint_url = old_mint_url.try_into()?;
124 let cdk_new_mint_url = new_mint_url.try_into()?;
125 self.inner
126 .update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
127 .await
128 .map_err(|e| FfiError::Database { msg: e.to_string() })
129 }
130
131 async fn add_mint_keysets(
133 &self,
134 mint_url: MintUrl,
135 keysets: Vec<KeySetInfo>,
136 ) -> Result<(), FfiError> {
137 let cdk_mint_url = mint_url.try_into()?;
138 let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
139 self.inner
140 .add_mint_keysets(cdk_mint_url, cdk_keysets)
141 .await
142 .map_err(|e| FfiError::Database { msg: e.to_string() })
143 }
144
145 async fn get_mint_keysets(
146 &self,
147 mint_url: MintUrl,
148 ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
149 let cdk_mint_url = mint_url.try_into()?;
150 let result = self
151 .inner
152 .get_mint_keysets(cdk_mint_url)
153 .await
154 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
155 Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
156 }
157
158 async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
159 let cdk_id = keyset_id.into();
160 let result = self
161 .inner
162 .get_keyset_by_id(&cdk_id)
163 .await
164 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
165 Ok(result.map(Into::into))
166 }
167
168 async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
170 let cdk_quote = quote.try_into()?;
171 self.inner
172 .add_mint_quote(cdk_quote)
173 .await
174 .map_err(|e| FfiError::Database { msg: e.to_string() })
175 }
176
177 async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
178 let result = self
179 .inner
180 .get_mint_quote("e_id)
181 .await
182 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
183 Ok(result.map(|q| q.into()))
184 }
185
186 async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
187 let result = self
188 .inner
189 .get_mint_quotes()
190 .await
191 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
192 Ok(result.into_iter().map(|q| q.into()).collect())
193 }
194
195 async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
196 self.inner
197 .remove_mint_quote("e_id)
198 .await
199 .map_err(|e| FfiError::Database { msg: e.to_string() })
200 }
201
202 async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
204 let cdk_quote = quote.try_into()?;
205 self.inner
206 .add_melt_quote(cdk_quote)
207 .await
208 .map_err(|e| FfiError::Database { msg: e.to_string() })
209 }
210
211 async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
212 let result = self
213 .inner
214 .get_melt_quote("e_id)
215 .await
216 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
217 Ok(result.map(|q| q.into()))
218 }
219
220 async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
221 let result = self
222 .inner
223 .get_melt_quotes()
224 .await
225 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
226 Ok(result.into_iter().map(|q| q.into()).collect())
227 }
228
229 async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
230 self.inner
231 .remove_melt_quote("e_id)
232 .await
233 .map_err(|e| FfiError::Database { msg: e.to_string() })
234 }
235
236 async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
238 let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
240 self.inner
241 .add_keys(cdk_keyset)
242 .await
243 .map_err(|e| FfiError::Database { msg: e.to_string() })
244 }
245
246 async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
247 let cdk_id = id.into();
248 let result = self
249 .inner
250 .get_keys(&cdk_id)
251 .await
252 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
253 Ok(result.map(Into::into))
254 }
255
256 async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
257 let cdk_id = id.into();
258 self.inner
259 .remove_keys(&cdk_id)
260 .await
261 .map_err(|e| FfiError::Database { msg: e.to_string() })
262 }
263
264 async fn update_proofs(
266 &self,
267 added: Vec<ProofInfo>,
268 removed_ys: Vec<PublicKey>,
269 ) -> Result<(), FfiError> {
270 let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
272 .into_iter()
273 .map(|info| {
274 Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
275 proof: info.proof.try_into()?,
276 y: info.y.try_into()?,
277 mint_url: info.mint_url.try_into()?,
278 state: info.state.into(),
279 spending_condition: info
280 .spending_condition
281 .map(|sc| sc.try_into())
282 .transpose()?,
283 unit: info.unit.into(),
284 })
285 })
286 .collect();
287 let cdk_added = cdk_added?;
288
289 let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
290 removed_ys.into_iter().map(|pk| pk.try_into()).collect();
291 let cdk_removed_ys = cdk_removed_ys?;
292
293 self.inner
294 .update_proofs(cdk_added, cdk_removed_ys)
295 .await
296 .map_err(|e| FfiError::Database { msg: e.to_string() })
297 }
298
299 async fn get_proofs(
300 &self,
301 mint_url: Option<MintUrl>,
302 unit: Option<CurrencyUnit>,
303 state: Option<Vec<ProofState>>,
304 spending_conditions: Option<Vec<SpendingConditions>>,
305 ) -> Result<Vec<ProofInfo>, FfiError> {
306 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
307 let cdk_unit = unit.map(Into::into);
308 let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
309 let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
310 spending_conditions
311 .map(|sc| {
312 sc.into_iter()
313 .map(|c| c.try_into())
314 .collect::<Result<Vec<_>, FfiError>>()
315 })
316 .transpose()?;
317
318 let result = self
319 .inner
320 .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
321 .await
322 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
323
324 Ok(result.into_iter().map(Into::into).collect())
325 }
326
327 async fn get_balance(
328 &self,
329 mint_url: Option<MintUrl>,
330 unit: Option<CurrencyUnit>,
331 state: Option<Vec<ProofState>>,
332 ) -> Result<u64, FfiError> {
333 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
334 let cdk_unit = unit.map(Into::into);
335 let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
336
337 self.inner
338 .get_balance(cdk_mint_url, cdk_unit, cdk_state)
339 .await
340 .map_err(|e| FfiError::Database { msg: e.to_string() })
341 }
342
343 async fn update_proofs_state(
344 &self,
345 ys: Vec<PublicKey>,
346 state: ProofState,
347 ) -> Result<(), FfiError> {
348 let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
349 ys.into_iter().map(|pk| pk.try_into()).collect();
350 let cdk_ys = cdk_ys?;
351 let cdk_state = state.into();
352
353 self.inner
354 .update_proofs_state(cdk_ys, cdk_state)
355 .await
356 .map_err(|e| FfiError::Database { msg: e.to_string() })
357 }
358
359 async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
361 let cdk_id = keyset_id.into();
362 self.inner
363 .increment_keyset_counter(&cdk_id, count)
364 .await
365 .map_err(|e| FfiError::Database { msg: e.to_string() })
366 }
367
368 async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
370 let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
372
373 self.inner
374 .add_transaction(cdk_transaction)
375 .await
376 .map_err(|e| FfiError::Database { msg: e.to_string() })
377 }
378
379 async fn get_transaction(
380 &self,
381 transaction_id: TransactionId,
382 ) -> Result<Option<Transaction>, FfiError> {
383 let cdk_id = transaction_id.try_into()?;
384 let result = self
385 .inner
386 .get_transaction(cdk_id)
387 .await
388 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
389 Ok(result.map(Into::into))
390 }
391
392 async fn list_transactions(
393 &self,
394 mint_url: Option<MintUrl>,
395 direction: Option<TransactionDirection>,
396 unit: Option<CurrencyUnit>,
397 ) -> Result<Vec<Transaction>, FfiError> {
398 let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
399 let cdk_direction = direction.map(Into::into);
400 let cdk_unit = unit.map(Into::into);
401
402 let result = self
403 .inner
404 .list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
405 .await
406 .map_err(|e| FfiError::Database { msg: e.to_string() })?;
407
408 Ok(result.into_iter().map(Into::into).collect())
409 }
410
411 async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
412 let cdk_id = transaction_id.try_into()?;
413 self.inner
414 .remove_transaction(cdk_id)
415 .await
416 .map_err(|e| FfiError::Database { msg: e.to_string() })
417 }
418}