Cesar Rodas 1 сар өмнө
parent
commit
9c4e75dfec

+ 252 - 149
crates/cdk-ffi/src/database.rs

@@ -168,16 +168,20 @@ impl std::fmt::Debug for WalletDatabaseBridge {
     }
     }
 }
 }
 
 
-#[async_trait::async_trait]
-impl CdkWalletDatabase for WalletDatabaseBridge {
-    type Err = cdk::cdk_database::Error;
+/// Transaction bridge that wraps FFI atomic operations
+struct WalletDatabaseTransactionBridge {
+    ffi_db: Arc<dyn WalletDatabase>,
+}
 
 
-    // Mint Management
+#[async_trait::async_trait]
+impl<'a> cdk::cdk_database::WalletDatabaseTransaction<'a, cdk::cdk_database::Error>
+    for WalletDatabaseTransactionBridge
+{
     async fn add_mint(
     async fn add_mint(
-        &self,
+        &mut self,
         mint_url: cdk::mint_url::MintUrl,
         mint_url: cdk::mint_url::MintUrl,
         mint_info: Option<cdk::nuts::MintInfo>,
         mint_info: Option<cdk::nuts::MintInfo>,
-    ) -> Result<(), Self::Err> {
+    ) -> Result<(), cdk::cdk_database::Error> {
         let ffi_mint_url = mint_url.into();
         let ffi_mint_url = mint_url.into();
         let ffi_mint_info = mint_info.map(Into::into);
         let ffi_mint_info = mint_info.map(Into::into);
         self.ffi_db
         self.ffi_db
@@ -186,7 +190,10 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
     }
     }
 
 
-    async fn remove_mint(&self, mint_url: cdk::mint_url::MintUrl) -> Result<(), Self::Err> {
+    async fn remove_mint(
+        &mut self,
+        mint_url: cdk::mint_url::MintUrl,
+    ) -> Result<(), cdk::cdk_database::Error> {
         let ffi_mint_url = mint_url.into();
         let ffi_mint_url = mint_url.into();
         self.ffi_db
         self.ffi_db
             .remove_mint(ffi_mint_url)
             .remove_mint(ffi_mint_url)
@@ -194,6 +201,244 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
     }
     }
 
 
+    async fn update_mint_url(
+        &mut self,
+        old_mint_url: cdk::mint_url::MintUrl,
+        new_mint_url: cdk::mint_url::MintUrl,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_old = old_mint_url.into();
+        let ffi_new = new_mint_url.into();
+        self.ffi_db
+            .update_mint_url(ffi_old, ffi_new)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn add_mint_keysets(
+        &mut self,
+        mint_url: cdk::mint_url::MintUrl,
+        keysets: Vec<cdk::nuts::KeySetInfo>,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_mint_url = mint_url.into();
+        let ffi_keysets = keysets.into_iter().map(Into::into).collect();
+        self.ffi_db
+            .add_mint_keysets(ffi_mint_url, ffi_keysets)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn get_mint_quote(
+        &mut self,
+        quote_id: &str,
+    ) -> Result<Option<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
+        self.ffi_db
+            .get_mint_quote(quote_id.to_string())
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?
+            .map(|q| q.try_into())
+            .transpose()
+            .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn add_mint_quote(
+        &mut self,
+        quote: cdk::wallet::MintQuote,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_quote = quote.into();
+        self.ffi_db
+            .add_mint_quote(ffi_quote)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn remove_mint_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
+        self.ffi_db
+            .remove_mint_quote(quote_id.to_string())
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn get_melt_quote(
+        &mut self,
+        quote_id: &str,
+    ) -> Result<Option<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
+        self.ffi_db
+            .get_melt_quote(quote_id.to_string())
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?
+            .map(|q| q.try_into())
+            .transpose()
+            .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn add_melt_quote(
+        &mut self,
+        quote: cdk::wallet::MeltQuote,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_quote = quote.into();
+        self.ffi_db
+            .add_melt_quote(ffi_quote)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn remove_melt_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
+        self.ffi_db
+            .remove_melt_quote(quote_id.to_string())
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn add_keys(&mut self, keyset: cdk::nuts::KeySet) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_keyset = keyset.into();
+        self.ffi_db
+            .add_keys(ffi_keyset)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn remove_keys(&mut self, id: &cdk::nuts::Id) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_id = (*id).into();
+        self.ffi_db
+            .remove_keys(ffi_id)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn get_proofs(
+        &mut self,
+        mint_url: Option<cdk::mint_url::MintUrl>,
+        unit: Option<cdk::nuts::CurrencyUnit>,
+        state: Option<Vec<cdk::nuts::State>>,
+        spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
+    ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
+        let ffi_mint_url = mint_url.map(Into::into);
+        let ffi_unit = unit.map(Into::into);
+        let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
+        let ffi_spending = spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
+
+        let result = self
+            .ffi_db
+            .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
+
+        result
+            .into_iter()
+            .map(|info| {
+                Ok::<cdk::types::ProofInfo, cdk::cdk_database::Error>(cdk::types::ProofInfo {
+                    proof: info.proof.inner.clone(),
+                    y: info.y.try_into().map_err(|e: FfiError| {
+                        cdk::cdk_database::Error::Database(e.to_string().into())
+                    })?,
+                    mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
+                        cdk::cdk_database::Error::Database(e.to_string().into())
+                    })?,
+                    state: info.state.into(),
+                    spending_condition: info
+                        .spending_condition
+                        .map(|sc| sc.try_into())
+                        .transpose()
+                        .map_err(|e: FfiError| {
+                            cdk::cdk_database::Error::Database(e.to_string().into())
+                        })?,
+                    unit: info.unit.into(),
+                })
+            })
+            .collect()
+    }
+
+    async fn update_proofs(
+        &mut self,
+        added: Vec<cdk::types::ProofInfo>,
+        removed_ys: Vec<cdk::nuts::PublicKey>,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_added = added.into_iter().map(Into::into).collect();
+        let ffi_removed = removed_ys.into_iter().map(Into::into).collect();
+        self.ffi_db
+            .update_proofs(ffi_added, ffi_removed)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn update_proofs_state(
+        &mut self,
+        ys: Vec<cdk::nuts::PublicKey>,
+        state: cdk::nuts::State,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_ys = ys.into_iter().map(Into::into).collect();
+        let ffi_state = state.into();
+        self.ffi_db
+            .update_proofs_state(ffi_ys, ffi_state)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn increment_keyset_counter(
+        &mut self,
+        keyset_id: &cdk::nuts::Id,
+        count: u32,
+    ) -> Result<u32, cdk::cdk_database::Error> {
+        let ffi_id = (*keyset_id).into();
+        self.ffi_db
+            .increment_keyset_counter(ffi_id, count)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn add_transaction(
+        &mut self,
+        transaction: cdk::wallet::types::Transaction,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_tx = transaction.into();
+        self.ffi_db
+            .add_transaction(ffi_tx)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+
+    async fn remove_transaction(
+        &mut self,
+        transaction_id: cdk::wallet::types::TransactionId,
+    ) -> Result<(), cdk::cdk_database::Error> {
+        let ffi_id = transaction_id.into();
+        self.ffi_db
+            .remove_transaction(ffi_id)
+            .await
+            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
+    }
+}
+
+#[async_trait::async_trait]
+impl cdk::cdk_database::DbTransactionFinalizer for WalletDatabaseTransactionBridge {
+    type Err = cdk::cdk_database::Error;
+
+    async fn commit(self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
+        // No-op: FFI operations are atomic and auto-commit
+        Ok(())
+    }
+
+    async fn rollback(self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
+        // No-op: FFI operations are atomic
+        Ok(())
+    }
+}
+
+#[async_trait::async_trait]
+impl CdkWalletDatabase for WalletDatabaseBridge {
+    type Err = cdk::cdk_database::Error;
+
+    async fn begin_db_transaction<'a>(
+        &'a self,
+    ) -> Result<
+        Box<dyn cdk::cdk_database::WalletDatabaseTransaction<'a, Self::Err> + Send + Sync + 'a>,
+        cdk::cdk_database::Error,
+    > {
+        Ok(Box::new(WalletDatabaseTransactionBridge {
+            ffi_db: Arc::clone(&self.ffi_db),
+        }))
+    }
+
     async fn get_mint(
     async fn get_mint(
         &self,
         &self,
         mint_url: cdk::mint_url::MintUrl,
         mint_url: cdk::mint_url::MintUrl,
@@ -226,34 +471,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
         Ok(cdk_result)
         Ok(cdk_result)
     }
     }
 
 
-    async fn update_mint_url(
-        &self,
-        old_mint_url: cdk::mint_url::MintUrl,
-        new_mint_url: cdk::mint_url::MintUrl,
-    ) -> Result<(), Self::Err> {
-        let ffi_old_mint_url = old_mint_url.into();
-        let ffi_new_mint_url = new_mint_url.into();
-        self.ffi_db
-            .update_mint_url(ffi_old_mint_url, ffi_new_mint_url)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Keyset Management
-    async fn add_mint_keysets(
-        &self,
-        mint_url: cdk::mint_url::MintUrl,
-        keysets: Vec<cdk::nuts::KeySetInfo>,
-    ) -> Result<(), Self::Err> {
-        let ffi_mint_url = mint_url.into();
-        let ffi_keysets: Vec<KeySetInfo> = keysets.into_iter().map(Into::into).collect();
-
-        self.ffi_db
-            .add_mint_keysets(ffi_mint_url, ffi_keysets)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_mint_keysets(
     async fn get_mint_keysets(
         &self,
         &self,
         mint_url: cdk::mint_url::MintUrl,
         mint_url: cdk::mint_url::MintUrl,
@@ -280,15 +497,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
         Ok(result.map(Into::into))
         Ok(result.map(Into::into))
     }
     }
 
 
-    // Mint Quote Management
-    async fn add_mint_quote(&self, quote: cdk::wallet::MintQuote) -> Result<(), Self::Err> {
-        let ffi_quote = quote.into();
-        self.ffi_db
-            .add_mint_quote(ffi_quote)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_mint_quote(
     async fn get_mint_quote(
         &self,
         &self,
         quote_id: &str,
         quote_id: &str,
@@ -321,22 +529,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .collect::<Result<Vec<_>, _>>()?)
             .collect::<Result<Vec<_>, _>>()?)
     }
     }
 
 
-    async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
-        self.ffi_db
-            .remove_mint_quote(quote_id.to_string())
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Melt Quote Management
-    async fn add_melt_quote(&self, quote: cdk::wallet::MeltQuote) -> Result<(), Self::Err> {
-        let ffi_quote = quote.into();
-        self.ffi_db
-            .add_melt_quote(ffi_quote)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_melt_quote(
     async fn get_melt_quote(
         &self,
         &self,
         quote_id: &str,
         quote_id: &str,
@@ -369,22 +561,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .collect::<Result<Vec<_>, _>>()?)
             .collect::<Result<Vec<_>, _>>()?)
     }
     }
 
 
-    async fn remove_melt_quote(&self, quote_id: &str) -> Result<(), Self::Err> {
-        self.ffi_db
-            .remove_melt_quote(quote_id.to_string())
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Keys Management
-    async fn add_keys(&self, keyset: cdk::nuts::KeySet) -> Result<(), Self::Err> {
-        let ffi_keyset: KeySet = keyset.into();
-        self.ffi_db
-            .add_keys(ffi_keyset)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_keys(&self, id: &cdk::nuts::Id) -> Result<Option<cdk::nuts::Keys>, Self::Err> {
     async fn get_keys(&self, id: &cdk::nuts::Id) -> Result<Option<cdk::nuts::Keys>, Self::Err> {
         let ffi_id: Id = (*id).into();
         let ffi_id: Id = (*id).into();
         let result = self
         let result = self
@@ -403,29 +579,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .transpose()
             .transpose()
     }
     }
 
 
-    async fn remove_keys(&self, id: &cdk::nuts::Id) -> Result<(), Self::Err> {
-        let ffi_id = (*id).into();
-        self.ffi_db
-            .remove_keys(ffi_id)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Proof Management
-    async fn update_proofs(
-        &self,
-        added: Vec<cdk::types::ProofInfo>,
-        removed_ys: Vec<cdk::nuts::PublicKey>,
-    ) -> Result<(), Self::Err> {
-        let ffi_added: Vec<ProofInfo> = added.into_iter().map(Into::into).collect();
-        let ffi_removed_ys: Vec<PublicKey> = removed_ys.into_iter().map(Into::into).collect();
-
-        self.ffi_db
-            .update_proofs(ffi_added, ffi_removed_ys)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_proofs(
     async fn get_proofs(
         &self,
         &self,
         mint_url: Option<cdk::mint_url::MintUrl>,
         mint_url: Option<cdk::mint_url::MintUrl>,
@@ -491,45 +644,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
     }
     }
 
 
-    async fn update_proofs_state(
-        &self,
-        ys: Vec<cdk::nuts::PublicKey>,
-        state: cdk::nuts::State,
-    ) -> Result<(), Self::Err> {
-        let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
-        let ffi_state = state.into();
-
-        self.ffi_db
-            .update_proofs_state(ffi_ys, ffi_state)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Keyset Counter Management
-    async fn increment_keyset_counter(
-        &self,
-        keyset_id: &cdk::nuts::Id,
-        count: u32,
-    ) -> Result<u32, Self::Err> {
-        let ffi_id = (*keyset_id).into();
-        self.ffi_db
-            .increment_keyset_counter(ffi_id, count)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
-    // Transaction Management
-    async fn add_transaction(
-        &self,
-        transaction: cdk::wallet::types::Transaction,
-    ) -> Result<(), Self::Err> {
-        let ffi_transaction = transaction.into();
-        self.ffi_db
-            .add_transaction(ffi_transaction)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
-
     async fn get_transaction(
     async fn get_transaction(
         &self,
         &self,
         transaction_id: cdk::wallet::types::TransactionId,
         transaction_id: cdk::wallet::types::TransactionId,
@@ -569,17 +683,6 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
             .collect::<Result<Vec<_>, FfiError>>()
             .collect::<Result<Vec<_>, FfiError>>()
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
             .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
     }
     }
-
-    async fn remove_transaction(
-        &self,
-        transaction_id: cdk::wallet::types::TransactionId,
-    ) -> Result<(), Self::Err> {
-        let ffi_id = transaction_id.into();
-        self.ffi_db
-            .remove_transaction(ffi_id)
-            .await
-            .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
-    }
 }
 }
 
 
 /// FFI-safe wallet database backend selection
 /// FFI-safe wallet database backend selection

+ 8 - 0
crates/cdk-ffi/src/error.rs

@@ -122,3 +122,11 @@ impl From<serde_json::Error> for FfiError {
         }
         }
     }
     }
 }
 }
+
+impl From<cdk::cdk_database::Error> for FfiError {
+    fn from(err: cdk::cdk_database::Error) -> Self {
+        FfiError::Database {
+            msg: err.to_string(),
+        }
+    }
+}

+ 78 - 51
crates/cdk-ffi/src/postgres.rs

@@ -47,20 +47,20 @@ impl WalletDatabase for WalletPostgresDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_info = mint_info.map(Into::into);
         let cdk_mint_info = mint_info.map(Into::into);
-        println!("adding new mint");
-        self.inner
-            .add_mint(cdk_mint_url, cdk_mint_info)
-            .await
-            .map_err(|e| {
-                println!("ffi error {:?}", e);
-                FfiError::Database { msg: e.to_string() }
-            })
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint(cdk_mint_url, cdk_mint_info).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
     async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
     async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
-        self.inner
-            .remove_mint(cdk_mint_url)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_mint(cdk_mint_url).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
     async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
     async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
@@ -90,9 +90,11 @@ impl WalletDatabase for WalletPostgresDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_old_mint_url = old_mint_url.try_into()?;
         let cdk_old_mint_url = old_mint_url.try_into()?;
         let cdk_new_mint_url = new_mint_url.try_into()?;
         let cdk_new_mint_url = new_mint_url.try_into()?;
-        self.inner
-            .update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_mint_url(cdk_old_mint_url, cdk_new_mint_url).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
     async fn add_mint_keysets(
     async fn add_mint_keysets(
@@ -102,9 +104,11 @@ impl WalletDatabase for WalletPostgresDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
         let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
-        self.inner
-            .add_mint_keysets(cdk_mint_url, cdk_keysets)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint_keysets(cdk_mint_url, cdk_keysets).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
     async fn get_mint_keysets(
     async fn get_mint_keysets(
@@ -133,9 +137,11 @@ impl WalletDatabase for WalletPostgresDatabase {
     // Mint Quote Management
     // Mint Quote Management
     async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
     async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
         let cdk_quote = quote.try_into()?;
         let cdk_quote = quote.try_into()?;
-        self.inner
-            .add_mint_quote(cdk_quote)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint_quote(cdk_quote).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -158,18 +164,22 @@ impl WalletDatabase for WalletPostgresDatabase {
     }
     }
 
 
     async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
     async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
-        self.inner
-            .remove_mint_quote(&quote_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_mint_quote(&quote_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
     // Melt Quote Management
     // Melt Quote Management
     async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
     async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
         let cdk_quote = quote.try_into()?;
         let cdk_quote = quote.try_into()?;
-        self.inner
-            .add_melt_quote(cdk_quote)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_melt_quote(cdk_quote).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -192,9 +202,11 @@ impl WalletDatabase for WalletPostgresDatabase {
     }
     }
 
 
     async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
     async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
-        self.inner
-            .remove_melt_quote(&quote_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_melt_quote(&quote_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -202,9 +214,11 @@ impl WalletDatabase for WalletPostgresDatabase {
     async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
     async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
         // Convert FFI KeySet to cdk::nuts::KeySet
         // Convert FFI KeySet to cdk::nuts::KeySet
         let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
         let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
-        self.inner
-            .add_keys(cdk_keyset)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_keys(cdk_keyset).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -220,9 +234,11 @@ impl WalletDatabase for WalletPostgresDatabase {
 
 
     async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
     async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
         let cdk_id = id.into();
         let cdk_id = id.into();
-        self.inner
-            .remove_keys(&cdk_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_keys(&cdk_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -255,9 +271,11 @@ impl WalletDatabase for WalletPostgresDatabase {
             removed_ys.into_iter().map(|pk| pk.try_into()).collect();
             removed_ys.into_iter().map(|pk| pk.try_into()).collect();
         let cdk_removed_ys = cdk_removed_ys?;
         let cdk_removed_ys = cdk_removed_ys?;
 
 
-        self.inner
-            .update_proofs(cdk_added, cdk_removed_ys)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_proofs(cdk_added, cdk_removed_ys).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -315,19 +333,24 @@ impl WalletDatabase for WalletPostgresDatabase {
         let cdk_ys = cdk_ys?;
         let cdk_ys = cdk_ys?;
         let cdk_state = state.into();
         let cdk_state = state.into();
 
 
-        self.inner
-            .update_proofs_state(cdk_ys, cdk_state)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_proofs_state(cdk_ys, cdk_state).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
     // Keyset Counter Management
     // Keyset Counter Management
     async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
     async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
         let cdk_id = keyset_id.into();
         let cdk_id = keyset_id.into();
-        self.inner
-            .increment_keyset_counter(&cdk_id, count)
-            .await
-            .map_err(|e| FfiError::Database { msg: e.to_string() })
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        let result = tx.increment_keyset_counter(&cdk_id, count).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Ok(result)
     }
     }
 
 
     // Transaction Management
     // Transaction Management
@@ -335,9 +358,11 @@ impl WalletDatabase for WalletPostgresDatabase {
         // Convert FFI Transaction to CDK Transaction using TryFrom
         // Convert FFI Transaction to CDK Transaction using TryFrom
         let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
         let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
 
 
-        self.inner
-            .add_transaction(cdk_transaction)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_transaction(cdk_transaction).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -375,9 +400,11 @@ impl WalletDatabase for WalletPostgresDatabase {
 
 
     async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
     async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
         let cdk_id = transaction_id.try_into()?;
         let cdk_id = transaction_id.try_into()?;
-        self.inner
-            .remove_transaction(cdk_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_transaction(cdk_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 }
 }

+ 77 - 46
crates/cdk-ffi/src/sqlite.rs

@@ -79,17 +79,21 @@ impl WalletDatabase for WalletSqliteDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_info = mint_info.map(Into::into);
         let cdk_mint_info = mint_info.map(Into::into);
-        self.inner
-            .add_mint(cdk_mint_url, cdk_mint_info)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint(cdk_mint_url, cdk_mint_info).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
     async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
     async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
-        self.inner
-            .remove_mint(cdk_mint_url)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_mint(cdk_mint_url).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -122,9 +126,11 @@ impl WalletDatabase for WalletSqliteDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_old_mint_url = old_mint_url.try_into()?;
         let cdk_old_mint_url = old_mint_url.try_into()?;
         let cdk_new_mint_url = new_mint_url.try_into()?;
         let cdk_new_mint_url = new_mint_url.try_into()?;
-        self.inner
-            .update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_mint_url(cdk_old_mint_url, cdk_new_mint_url).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -136,9 +142,11 @@ impl WalletDatabase for WalletSqliteDatabase {
     ) -> Result<(), FfiError> {
     ) -> Result<(), FfiError> {
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_mint_url = mint_url.try_into()?;
         let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
         let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
-        self.inner
-            .add_mint_keysets(cdk_mint_url, cdk_keysets)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint_keysets(cdk_mint_url, cdk_keysets).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -168,9 +176,11 @@ impl WalletDatabase for WalletSqliteDatabase {
     // Mint Quote Management
     // Mint Quote Management
     async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
     async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
         let cdk_quote = quote.try_into()?;
         let cdk_quote = quote.try_into()?;
-        self.inner
-            .add_mint_quote(cdk_quote)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_mint_quote(cdk_quote).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -193,18 +203,22 @@ impl WalletDatabase for WalletSqliteDatabase {
     }
     }
 
 
     async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
     async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
-        self.inner
-            .remove_mint_quote(&quote_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_mint_quote(&quote_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
     // Melt Quote Management
     // Melt Quote Management
     async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
     async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
         let cdk_quote = quote.try_into()?;
         let cdk_quote = quote.try_into()?;
-        self.inner
-            .add_melt_quote(cdk_quote)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_melt_quote(cdk_quote).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -227,9 +241,11 @@ impl WalletDatabase for WalletSqliteDatabase {
     }
     }
 
 
     async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
     async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
-        self.inner
-            .remove_melt_quote(&quote_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_melt_quote(&quote_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -237,9 +253,11 @@ impl WalletDatabase for WalletSqliteDatabase {
     async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
     async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
         // Convert FFI KeySet to cdk::nuts::KeySet
         // Convert FFI KeySet to cdk::nuts::KeySet
         let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
         let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
-        self.inner
-            .add_keys(cdk_keyset)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_keys(cdk_keyset).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -255,9 +273,11 @@ impl WalletDatabase for WalletSqliteDatabase {
 
 
     async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
     async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
         let cdk_id = id.into();
         let cdk_id = id.into();
-        self.inner
-            .remove_keys(&cdk_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_keys(&cdk_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -290,9 +310,11 @@ impl WalletDatabase for WalletSqliteDatabase {
             removed_ys.into_iter().map(|pk| pk.try_into()).collect();
             removed_ys.into_iter().map(|pk| pk.try_into()).collect();
         let cdk_removed_ys = cdk_removed_ys?;
         let cdk_removed_ys = cdk_removed_ys?;
 
 
-        self.inner
-            .update_proofs(cdk_added, cdk_removed_ys)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_proofs(cdk_added, cdk_removed_ys).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -350,19 +372,24 @@ impl WalletDatabase for WalletSqliteDatabase {
         let cdk_ys = cdk_ys?;
         let cdk_ys = cdk_ys?;
         let cdk_state = state.into();
         let cdk_state = state.into();
 
 
-        self.inner
-            .update_proofs_state(cdk_ys, cdk_state)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.update_proofs_state(cdk_ys, cdk_state).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
     // Keyset Counter Management
     // Keyset Counter Management
     async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
     async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
         let cdk_id = keyset_id.into();
         let cdk_id = keyset_id.into();
-        self.inner
-            .increment_keyset_counter(&cdk_id, count)
-            .await
-            .map_err(|e| FfiError::Database { msg: e.to_string() })
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        let result = tx.increment_keyset_counter(&cdk_id, count).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Ok(result)
     }
     }
 
 
     // Transaction Management
     // Transaction Management
@@ -370,9 +397,11 @@ impl WalletDatabase for WalletSqliteDatabase {
         // Convert FFI Transaction to CDK Transaction using TryFrom
         // Convert FFI Transaction to CDK Transaction using TryFrom
         let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
         let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
 
 
-        self.inner
-            .add_transaction(cdk_transaction)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.add_transaction(cdk_transaction).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 
 
@@ -410,9 +439,11 @@ impl WalletDatabase for WalletSqliteDatabase {
 
 
     async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
     async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
         let cdk_id = transaction_id.try_into()?;
         let cdk_id = transaction_id.try_into()?;
-        self.inner
-            .remove_transaction(cdk_id)
-            .await
+        let mut tx = self.inner.begin_db_transaction().await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        tx.remove_transaction(cdk_id).await
+            .map_err(|e| FfiError::Database { msg: e.to_string() })?;
+        Box::new(tx).commit().await
             .map_err(|e| FfiError::Database { msg: e.to_string() })
             .map_err(|e| FfiError::Database { msg: e.to_string() })
     }
     }
 }
 }

+ 4 - 2
crates/cdk-ffi/src/wallet.rs

@@ -123,10 +123,12 @@ impl Wallet {
             proofs.into_iter().map(|p| p.try_into()).collect();
             proofs.into_iter().map(|p| p.try_into()).collect();
         let cdk_proofs = cdk_proofs?;
         let cdk_proofs = cdk_proofs?;
 
 
+        let mut tx = self.inner.localstore.begin_db_transaction().await?;
         let amount = self
         let amount = self
             .inner
             .inner
-            .receive_proofs(cdk_proofs, options.into(), memo)
+            .receive_proofs(&mut tx, cdk_proofs, options.into(), memo)
             .await?;
             .await?;
+        Box::new(tx).commit().await?;
         Ok(amount.into())
         Ok(amount.into())
     }
     }
 
 
@@ -295,7 +297,7 @@ impl Wallet {
             proofs.into_iter().map(|p| p.try_into()).collect();
             proofs.into_iter().map(|p| p.try_into()).collect();
         let cdk_proofs = cdk_proofs?;
         let cdk_proofs = cdk_proofs?;
 
 
-        let proof_states = self.inner.check_proofs_spent(cdk_proofs).await?;
+        let proof_states = self.inner.check_proofs_spent(cdk_proofs, None).await?;
         // Convert ProofState to bool (spent = true, unspent = false)
         // Convert ProofState to bool (spent = true, unspent = false)
         let spent_bools = proof_states
         let spent_bools = proof_states
             .into_iter()
             .into_iter()

+ 2 - 2
crates/cdk/src/lib.rs

@@ -9,11 +9,11 @@ compile_error!("The 'tor' feature is not supported on wasm32 targets (browser).
 
 
 pub mod cdk_database {
 pub mod cdk_database {
     //! CDK Database
     //! CDK Database
-    pub use cdk_common::database::Error;
+    pub use cdk_common::database::{DbTransactionFinalizer, Error};
     #[cfg(all(feature = "mint", feature = "auth"))]
     #[cfg(all(feature = "mint", feature = "auth"))]
     pub use cdk_common::database::MintAuthDatabase;
     pub use cdk_common::database::MintAuthDatabase;
     #[cfg(feature = "wallet")]
     #[cfg(feature = "wallet")]
-    pub use cdk_common::database::WalletDatabase;
+    pub use cdk_common::database::{WalletDatabase, WalletDatabaseTransaction};
     #[cfg(feature = "mint")]
     #[cfg(feature = "mint")]
     pub use cdk_common::database::{
     pub use cdk_common::database::{
         MintDatabase, MintKVStore, MintKVStoreDatabase, MintKVStoreTransaction, MintKeysDatabase,
         MintDatabase, MintKVStore, MintKVStoreDatabase, MintKVStoreTransaction, MintKeysDatabase,