use std::collections::HashMap;
use std::sync::Arc;
use cdk_common::bitcoin::bip32::DerivationPath;
use cdk_common::database::WalletDatabase as CdkWalletDatabase;
use cdk_common::wallet::WalletSaga;
use crate::error::FfiError;
#[cfg(feature = "postgres")]
use crate::postgres::WalletPostgresDatabase;
use crate::sqlite::WalletSqliteDatabase;
use crate::types::*;
#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait WalletDatabase: Send + Sync {
async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError>;
async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError>;
async fn get_mint_keysets(
&self,
mint_url: MintUrl,
) -> Result<Option<Vec<KeySetInfo>>, FfiError>;
async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError>;
async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
async fn get_proofs(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Vec<ProofInfo>, FfiError>;
async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError>;
async fn get_balance(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
) -> Result<u64, FfiError>;
async fn get_transaction(
&self,
transaction_id: TransactionId,
) -> Result<Option<Transaction>, FfiError>;
async fn list_transactions(
&self,
mint_url: Option<MintUrl>,
direction: Option<TransactionDirection>,
unit: Option<CurrencyUnit>,
) -> Result<Vec<Transaction>, FfiError>;
async fn kv_read(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<Option<Vec<u8>>, FfiError>;
async fn kv_list(
&self,
primary_namespace: String,
secondary_namespace: String,
) -> Result<Vec<String>, FfiError>;
async fn add_p2pk_key(
&self,
pubkey: PublicKey,
derivation_path: String,
derivation_index: u32,
) -> Result<(), FfiError>;
async fn get_p2pk_key(&self, pubkey: PublicKey) -> Result<Option<P2PKSigningKey>, FfiError>;
async fn list_p2pk_keys(&self) -> Result<Vec<P2PKSigningKey>, FfiError>;
async fn latest_p2pk(&self) -> Result<Option<P2PKSigningKey>, FfiError>;
async fn kv_write(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
value: Vec<u8>,
) -> Result<(), FfiError>;
async fn kv_remove(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<(), FfiError>;
async fn update_proofs(
&self,
added: Vec<ProofInfo>,
removed_ys: Vec<PublicKey>,
) -> Result<(), FfiError>;
async fn update_proofs_state(
&self,
ys: Vec<PublicKey>,
state: ProofState,
) -> Result<(), FfiError>;
async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError>;
async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError>;
async fn update_mint_url(
&self,
old_mint_url: MintUrl,
new_mint_url: MintUrl,
) -> Result<(), FfiError>;
async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError>;
async fn add_mint(
&self,
mint_url: MintUrl,
mint_info: Option<MintInfo>,
) -> Result<(), FfiError>;
async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError>;
async fn add_mint_keysets(
&self,
mint_url: MintUrl,
keysets: Vec<KeySetInfo>,
) -> Result<(), FfiError>;
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError>;
async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError>;
async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError>;
async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError>;
async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError>;
async fn remove_keys(&self, id: Id) -> Result<(), FfiError>;
async fn add_saga(&self, saga_json: String) -> Result<(), FfiError>;
async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError>;
async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError>;
async fn delete_saga(&self, id: String) -> Result<(), FfiError>;
async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError>;
async fn reserve_proofs(
&self,
ys: Vec<PublicKey>,
operation_id: String,
) -> Result<(), FfiError>;
async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError>;
async fn get_reserved_proofs(&self, operation_id: String) -> Result<Vec<ProofInfo>, FfiError>;
async fn reserve_melt_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError>;
async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError>;
async fn reserve_mint_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError>;
async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError>;
}
struct WalletDatabaseBridge {
ffi_db: Arc<dyn WalletDatabase>,
}
impl WalletDatabaseBridge {
fn new(ffi_db: Arc<dyn WalletDatabase>) -> Self {
Self { ffi_db }
}
}
impl std::fmt::Debug for WalletDatabaseBridge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "WalletDatabaseBridge")
}
}
#[async_trait::async_trait]
impl CdkWalletDatabase<cdk::cdk_database::Error> for WalletDatabaseBridge {
async fn kv_read(
&self,
primary_namespace: &str,
secondary_namespace: &str,
key: &str,
) -> Result<Option<Vec<u8>>, cdk::cdk_database::Error> {
self.ffi_db
.kv_read(
primary_namespace.to_string(),
secondary_namespace.to_string(),
key.to_string(),
)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn kv_list(
&self,
primary_namespace: &str,
secondary_namespace: &str,
) -> Result<Vec<String>, cdk::cdk_database::Error> {
self.ffi_db
.kv_list(
primary_namespace.to_string(),
secondary_namespace.to_string(),
)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_mint(
&self,
mint_url: cdk::mint_url::MintUrl,
) -> Result<Option<cdk::nuts::MintInfo>, cdk::cdk_database::Error> {
let ffi_mint_url = mint_url.into();
let result = self
.ffi_db
.get_mint(ffi_mint_url)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
result
.map(TryInto::try_into)
.transpose()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_mints(
&self,
) -> Result<
HashMap<cdk::mint_url::MintUrl, Option<cdk::nuts::MintInfo>>,
cdk::cdk_database::Error,
> {
let result = self
.ffi_db
.get_mints()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let mut cdk_result = HashMap::new();
for (ffi_mint_url, mint_info_opt) in result {
let cdk_url = ffi_mint_url
.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let cdk_mint_info = mint_info_opt
.map(TryInto::try_into)
.transpose()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
cdk_result.insert(cdk_url, cdk_mint_info);
}
Ok(cdk_result)
}
async fn get_mint_keysets(
&self,
mint_url: cdk::mint_url::MintUrl,
) -> Result<Option<Vec<cdk::nuts::KeySetInfo>>, cdk::cdk_database::Error> {
let ffi_mint_url = mint_url.into();
let result = self
.ffi_db
.get_mint_keysets(ffi_mint_url)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let cdk_keysets = result
.map(|keysets| {
keysets
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()
})
.transpose()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(cdk_keysets)
}
async fn get_keyset_by_id(
&self,
keyset_id: &cdk::nuts::Id,
) -> Result<Option<cdk::nuts::KeySetInfo>, cdk::cdk_database::Error> {
let ffi_id = (*keyset_id).into();
let result = self
.ffi_db
.get_keyset_by_id(ffi_id)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let cdk_keyset = result
.map(TryInto::try_into)
.transpose()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(cdk_keyset)
}
async fn get_mint_quote(
&self,
quote_id: &str,
) -> Result<Option<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_mint_quote(quote_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.map(|q| {
q.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.transpose()?)
}
async fn get_mint_quotes(
&self,
) -> Result<Vec<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_mint_quotes()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.into_iter()
.map(|q| {
q.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.collect::<Result<Vec<_>, _>>()?)
}
async fn get_unissued_mint_quotes(
&self,
) -> Result<Vec<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_unissued_mint_quotes()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.into_iter()
.map(|q| {
q.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.collect::<Result<Vec<_>, _>>()?)
}
async fn get_melt_quote(
&self,
quote_id: &str,
) -> Result<Option<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_melt_quote(quote_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.map(|q| {
q.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.transpose()?)
}
async fn get_melt_quotes(
&self,
) -> Result<Vec<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_melt_quotes()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.into_iter()
.map(|q| {
q.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.collect::<Result<Vec<_>, _>>()?)
}
async fn get_keys(
&self,
id: &cdk::nuts::Id,
) -> Result<Option<cdk::nuts::Keys>, cdk::cdk_database::Error> {
let ffi_id: Id = (*id).into();
let result = self
.ffi_db
.get_keys(ffi_id)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
result
.map(|ffi_keys| {
ffi_keys
.try_into()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.transpose()
}
async fn get_proofs(
&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_conditions =
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_conditions)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
.into_iter()
.map(|info| {
Ok(cdk::types::ProofInfo {
proof: info.proof.try_into().map_err(|e: FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})?,
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(),
used_by_operation: info
.used_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
created_by_operation: info
.created_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
})
})
.collect();
cdk_result
}
async fn get_proofs_by_ys(
&self,
ys: Vec<cdk::nuts::PublicKey>,
) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
let result = self
.ffi_db
.get_proofs_by_ys(ffi_ys)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
.into_iter()
.map(|info| {
Ok(cdk::types::ProofInfo {
proof: info.proof.try_into().map_err(|e: FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})?,
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(),
used_by_operation: info
.used_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
created_by_operation: info
.created_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
})
})
.collect();
cdk_result
}
async fn get_balance(
&self,
mint_url: Option<cdk::mint_url::MintUrl>,
unit: Option<cdk::nuts::CurrencyUnit>,
state: Option<Vec<cdk::nuts::State>>,
) -> Result<u64, 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());
self.ffi_db
.get_balance(ffi_mint_url, ffi_unit, ffi_state)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_transaction(
&self,
transaction_id: cdk::wallet::types::TransactionId,
) -> Result<Option<cdk::wallet::types::Transaction>, cdk::cdk_database::Error> {
let ffi_id = transaction_id.into();
let result = self
.ffi_db
.get_transaction(ffi_id)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
result
.map(|tx| tx.try_into())
.transpose()
.map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn list_transactions(
&self,
mint_url: Option<cdk::mint_url::MintUrl>,
direction: Option<cdk::wallet::types::TransactionDirection>,
unit: Option<cdk::nuts::CurrencyUnit>,
) -> Result<Vec<cdk::wallet::types::Transaction>, cdk::cdk_database::Error> {
let ffi_mint_url = mint_url.map(Into::into);
let ffi_direction = direction.map(Into::into);
let ffi_unit = unit.map(Into::into);
let result = self
.ffi_db
.list_transactions(ffi_mint_url, ffi_direction, ffi_unit)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
result
.into_iter()
.map(|tx| tx.try_into())
.collect::<Result<Vec<_>, FfiError>>()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn add_p2pk_key(
&self,
pubkey: &cdk::nuts::PublicKey,
derivation_path: DerivationPath,
derivation_index: u32,
) -> Result<(), cdk::cdk_database::Error> {
let ffi_pubkey: PublicKey = (*pubkey).into();
let ffi_derivation_path = derivation_path.to_string();
self.ffi_db
.add_p2pk_key(ffi_pubkey, ffi_derivation_path, derivation_index)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn list_p2pk_keys(
&self,
) -> Result<Vec<cdk_common::wallet::P2PKSigningKey>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.list_p2pk_keys()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.into_iter()
.map(|k| {
k.try_into().map_err(|e: crate::error::FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})
})
.collect::<Result<Vec<_>, _>>()?)
}
async fn latest_p2pk(
&self,
) -> Result<Option<cdk_common::wallet::P2PKSigningKey>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.latest_p2pk()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.map(|k| {
k.try_into().map_err(|e: crate::error::FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})
})
.transpose()?)
}
async fn get_p2pk_key(
&self,
pubkey: &cdk::nuts::PublicKey,
) -> Result<Option<cdk_common::wallet::P2PKSigningKey>, cdk::cdk_database::Error> {
let ffi_pubkey: PublicKey = (*pubkey).into();
let result = self
.ffi_db
.get_p2pk_key(ffi_pubkey)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(result
.map(|k| {
k.try_into().map_err(|e: crate::error::FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})
})
.transpose()?)
}
async fn update_proofs(
&self,
added: Vec<cdk::types::ProofInfo>,
removed_ys: Vec<cdk::nuts::PublicKey>,
) -> Result<(), cdk::cdk_database::Error> {
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 update_proofs_state(
&self,
ys: Vec<cdk::nuts::PublicKey>,
state: cdk::nuts::State,
) -> Result<(), cdk::cdk_database::Error> {
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()))
}
async fn add_transaction(
&self,
transaction: cdk::wallet::types::Transaction,
) -> Result<(), cdk::cdk_database::Error> {
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 update_mint_url(
&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 increment_keyset_counter(
&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_mint(
&self,
mint_url: cdk::mint_url::MintUrl,
mint_info: Option<cdk::nuts::MintInfo>,
) -> Result<(), cdk::cdk_database::Error> {
let ffi_mint_url = mint_url.into();
let ffi_mint_info = mint_info.map(Into::into);
self.ffi_db
.add_mint(ffi_mint_url, ffi_mint_info)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn remove_mint(
&self,
mint_url: cdk::mint_url::MintUrl,
) -> Result<(), cdk::cdk_database::Error> {
let ffi_mint_url = mint_url.into();
self.ffi_db
.remove_mint(ffi_mint_url)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn add_mint_keysets(
&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: 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 add_mint_quote(
&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(&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 add_melt_quote(
&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(&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(&self, keyset: cdk::nuts::KeySet) -> Result<(), cdk::cdk_database::Error> {
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 remove_keys(&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 remove_transaction(
&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 fn add_saga(&self, saga: WalletSaga) -> Result<(), cdk::cdk_database::Error> {
let json = serde_json::to_string(&saga)
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
self.ffi_db
.add_saga(json)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_saga(
&self,
id: &uuid::Uuid,
) -> Result<Option<WalletSaga>, cdk::cdk_database::Error> {
let json_opt = self
.ffi_db
.get_saga(id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
match json_opt {
Some(json) => {
let saga: WalletSaga = serde_json::from_str(&json)
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
Ok(Some(saga))
}
None => Ok(None),
}
}
async fn update_saga(&self, saga: WalletSaga) -> Result<bool, cdk::cdk_database::Error> {
let json = serde_json::to_string(&saga)
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
self.ffi_db
.update_saga(json)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn delete_saga(&self, id: &uuid::Uuid) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.delete_saga(id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_incomplete_sagas(&self) -> Result<Vec<WalletSaga>, cdk::cdk_database::Error> {
let json_vec = self
.ffi_db
.get_incomplete_sagas()
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
json_vec
.into_iter()
.map(|json| {
serde_json::from_str(&json)
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
})
.collect()
}
async fn reserve_proofs(
&self,
ys: Vec<cdk::nuts::PublicKey>,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
self.ffi_db
.reserve_proofs(ffi_ys, operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn release_proofs(
&self,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.release_proofs(operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn get_reserved_proofs(
&self,
operation_id: &uuid::Uuid,
) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
let result = self
.ffi_db
.get_reserved_proofs(operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
result
.into_iter()
.map(|info| {
Ok(cdk::types::ProofInfo {
proof: info.proof.try_into().map_err(|e: FfiError| {
cdk::cdk_database::Error::Database(e.to_string().into())
})?,
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(),
used_by_operation: info
.used_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
created_by_operation: info
.created_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?,
})
})
.collect()
}
async fn reserve_melt_quote(
&self,
quote_id: &str,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.reserve_melt_quote(quote_id.to_string(), operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn release_melt_quote(
&self,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.release_melt_quote(operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn reserve_mint_quote(
&self,
quote_id: &str,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.reserve_mint_quote(quote_id.to_string(), operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn release_mint_quote(
&self,
operation_id: &uuid::Uuid,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.release_mint_quote(operation_id.to_string())
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn kv_write(
&self,
primary_namespace: &str,
secondary_namespace: &str,
key: &str,
value: &[u8],
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.kv_write(
primary_namespace.to_string(),
secondary_namespace.to_string(),
key.to_string(),
value.to_vec(),
)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
async fn kv_remove(
&self,
primary_namespace: &str,
secondary_namespace: &str,
key: &str,
) -> Result<(), cdk::cdk_database::Error> {
self.ffi_db
.kv_remove(
primary_namespace.to_string(),
secondary_namespace.to_string(),
key.to_string(),
)
.await
.map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
}
}
pub(crate) struct FfiWalletDatabaseWrapper<T, E>
where
T: CdkWalletDatabase<E> + Send + Sync + 'static,
E: std::error::Error
+ Send
+ Sync
+ Into<cdk_common::database::Error>
+ From<cdk_common::database::Error>
+ 'static,
{
inner: T,
_phantom: std::marker::PhantomData<E>,
}
impl<T, E> std::fmt::Debug for FfiWalletDatabaseWrapper<T, E>
where
T: CdkWalletDatabase<E> + std::fmt::Debug + Send + Sync + 'static,
E: std::error::Error
+ Send
+ Sync
+ Into<cdk_common::database::Error>
+ From<cdk_common::database::Error>
+ 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FfiWalletDatabaseWrapper")
.field("inner", &self.inner)
.finish()
}
}
impl<T, E> FfiWalletDatabaseWrapper<T, E>
where
T: CdkWalletDatabase<E> + Send + Sync + 'static,
E: std::error::Error
+ Send
+ Sync
+ Into<cdk_common::database::Error>
+ From<cdk_common::database::Error>
+ 'static,
{
pub fn new(inner: T) -> Arc<Self> {
Arc::new(Self {
inner,
_phantom: std::marker::PhantomData,
})
}
#[cfg(feature = "supabase")]
pub fn inner(&self) -> &T {
&self.inner
}
}
#[async_trait::async_trait]
impl<T, E> WalletDatabase for FfiWalletDatabaseWrapper<T, E>
where
T: CdkWalletDatabase<E> + Send + Sync + 'static,
E: std::error::Error
+ Send
+ Sync
+ Into<cdk_common::database::Error>
+ From<cdk_common::database::Error>
+ 'static,
{
async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError> {
let cdk_ys: Vec<cdk::nuts::PublicKey> = ys
.into_iter()
.map(|y| y.try_into())
.collect::<Result<Vec<_>, FfiError>>()?;
let result = self
.inner
.get_proofs_by_ys(cdk_ys)
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(Into::into).collect())
}
async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
let cdk_mint_url = mint_url.try_into()?;
let result = self
.inner
.get_mint(cdk_mint_url)
.await
.map_err(FfiError::internal)?;
Ok(result.map(Into::into))
}
async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
let result = self.inner.get_mints().await.map_err(FfiError::internal)?;
Ok(result
.into_iter()
.map(|(k, v)| (k.into(), v.map(Into::into)))
.collect())
}
async fn get_mint_keysets(
&self,
mint_url: MintUrl,
) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
let cdk_mint_url = mint_url.try_into()?;
let result = self
.inner
.get_mint_keysets(cdk_mint_url)
.await
.map_err(FfiError::internal)?;
Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
}
async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
let cdk_id = keyset_id.try_into()?;
let result = self
.inner
.get_keyset_by_id(&cdk_id)
.await
.map_err(FfiError::internal)?;
Ok(result.map(Into::into))
}
async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
let result = self
.inner
.get_mint_quote("e_id)
.await
.map_err(FfiError::internal)?;
Ok(result.map(|q| q.into()))
}
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
let result = self
.inner
.get_mint_quotes()
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(|q| q.into()).collect())
}
async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
let result = self
.inner
.get_unissued_mint_quotes()
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(|q| q.into()).collect())
}
async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
let result = self
.inner
.get_melt_quote("e_id)
.await
.map_err(FfiError::internal)?;
Ok(result.map(|q| q.into()))
}
async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
let result = self
.inner
.get_melt_quotes()
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(|q| q.into()).collect())
}
async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
let cdk_id = id.try_into()?;
let result = self
.inner
.get_keys(&cdk_id)
.await
.map_err(FfiError::internal)?;
Ok(result.map(Into::into))
}
async fn get_proofs(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Vec<ProofInfo>, FfiError> {
let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
let cdk_unit = unit.map(Into::into);
let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
spending_conditions
.map(|sc| {
sc.into_iter()
.map(|c| c.try_into())
.collect::<Result<Vec<_>, FfiError>>()
})
.transpose()?;
let result = self
.inner
.get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(Into::into).collect())
}
async fn get_balance(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
) -> Result<u64, FfiError> {
let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
let cdk_unit = unit.map(Into::into);
let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
self.inner
.get_balance(cdk_mint_url, cdk_unit, cdk_state)
.await
.map_err(FfiError::internal)
}
async fn get_transaction(
&self,
transaction_id: TransactionId,
) -> Result<Option<Transaction>, FfiError> {
let cdk_id = transaction_id.try_into()?;
let result = self
.inner
.get_transaction(cdk_id)
.await
.map_err(FfiError::internal)?;
Ok(result.map(Into::into))
}
async fn list_transactions(
&self,
mint_url: Option<MintUrl>,
direction: Option<TransactionDirection>,
unit: Option<CurrencyUnit>,
) -> Result<Vec<Transaction>, FfiError> {
let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
let cdk_direction = direction.map(Into::into);
let cdk_unit = unit.map(Into::into);
let result = self
.inner
.list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(Into::into).collect())
}
async fn kv_read(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<Option<Vec<u8>>, FfiError> {
self.inner
.kv_read(&primary_namespace, &secondary_namespace, &key)
.await
.map_err(FfiError::internal)
}
async fn kv_list(
&self,
primary_namespace: String,
secondary_namespace: String,
) -> Result<Vec<String>, FfiError> {
self.inner
.kv_list(&primary_namespace, &secondary_namespace)
.await
.map_err(FfiError::internal)
}
async fn add_p2pk_key(
&self,
pubkey: PublicKey,
derivation_path: String,
derivation_index: u32,
) -> Result<(), FfiError> {
use std::str::FromStr;
use cdk_common::bitcoin::bip32::DerivationPath;
let cdk_pubkey: cdk::nuts::PublicKey = pubkey.try_into()?;
let cdk_derivation_path =
DerivationPath::from_str(&derivation_path).map_err(FfiError::internal)?;
self.inner
.add_p2pk_key(&cdk_pubkey, cdk_derivation_path, derivation_index)
.await
.map_err(FfiError::database)
}
async fn get_p2pk_key(&self, pubkey: PublicKey) -> Result<Option<P2PKSigningKey>, FfiError> {
let cdk_pubkey: cdk::nuts::PublicKey = pubkey.try_into()?;
let result = self
.inner
.get_p2pk_key(&cdk_pubkey)
.await
.map_err(FfiError::database)?;
Ok(result.map(Into::into))
}
async fn list_p2pk_keys(&self) -> Result<Vec<P2PKSigningKey>, FfiError> {
let result = self
.inner
.list_p2pk_keys()
.await
.map_err(FfiError::database)?;
Ok(result.into_iter().map(Into::into).collect())
}
async fn latest_p2pk(&self) -> Result<Option<P2PKSigningKey>, FfiError> {
let result = self.inner.latest_p2pk().await.map_err(FfiError::database)?;
Ok(result.map(Into::into))
}
async fn kv_write(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
value: Vec<u8>,
) -> Result<(), FfiError> {
self.inner
.kv_write(&primary_namespace, &secondary_namespace, &key, &value)
.await
.map_err(FfiError::internal)
}
async fn kv_remove(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<(), FfiError> {
self.inner
.kv_remove(&primary_namespace, &secondary_namespace, &key)
.await
.map_err(FfiError::internal)
}
async fn update_proofs(
&self,
added: Vec<ProofInfo>,
removed_ys: Vec<PublicKey>,
) -> Result<(), FfiError> {
let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
.into_iter()
.map(|info| {
Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
proof: info.proof.try_into()?,
y: info.y.try_into()?,
mint_url: info.mint_url.try_into()?,
state: info.state.into(),
spending_condition: info
.spending_condition
.map(|sc| sc.try_into())
.transpose()?,
unit: info.unit.into(),
used_by_operation: info
.used_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| FfiError::internal(e.to_string()))?,
created_by_operation: info
.created_by_operation
.map(|id| uuid::Uuid::parse_str(&id))
.transpose()
.map_err(|e| FfiError::internal(e.to_string()))?,
})
})
.collect();
let cdk_added = cdk_added?;
let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
removed_ys.into_iter().map(|pk| pk.try_into()).collect();
let cdk_removed_ys = cdk_removed_ys?;
self.inner
.update_proofs(cdk_added, cdk_removed_ys)
.await
.map_err(FfiError::internal)
}
async fn update_proofs_state(
&self,
ys: Vec<PublicKey>,
state: ProofState,
) -> Result<(), FfiError> {
let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
ys.into_iter().map(|pk| pk.try_into()).collect();
let cdk_ys = cdk_ys?;
let cdk_state = state.into();
self.inner
.update_proofs_state(cdk_ys, cdk_state)
.await
.map_err(FfiError::internal)
}
async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
self.inner
.add_transaction(cdk_transaction)
.await
.map_err(FfiError::internal)
}
async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
let cdk_id = transaction_id.try_into()?;
self.inner
.remove_transaction(cdk_id)
.await
.map_err(FfiError::internal)
}
async fn update_mint_url(
&self,
old_mint_url: MintUrl,
new_mint_url: MintUrl,
) -> Result<(), FfiError> {
let cdk_old = old_mint_url.try_into()?;
let cdk_new = new_mint_url.try_into()?;
self.inner
.update_mint_url(cdk_old, cdk_new)
.await
.map_err(FfiError::internal)
}
async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
let cdk_id = keyset_id.try_into()?;
self.inner
.increment_keyset_counter(&cdk_id, count)
.await
.map_err(FfiError::internal)
}
async fn add_mint(
&self,
mint_url: MintUrl,
mint_info: Option<MintInfo>,
) -> Result<(), FfiError> {
let cdk_mint_url = mint_url.try_into()?;
let cdk_mint_info = mint_info.map(TryInto::try_into).transpose()?;
self.inner
.add_mint(cdk_mint_url, cdk_mint_info)
.await
.map_err(FfiError::internal)
}
async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
let cdk_mint_url = mint_url.try_into()?;
self.inner
.remove_mint(cdk_mint_url)
.await
.map_err(FfiError::internal)
}
async fn add_mint_keysets(
&self,
mint_url: MintUrl,
keysets: Vec<KeySetInfo>,
) -> Result<(), FfiError> {
let cdk_mint_url = mint_url.try_into()?;
let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?;
self.inner
.add_mint_keysets(cdk_mint_url, cdk_keysets)
.await
.map_err(FfiError::internal)
}
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
let cdk_quote = quote.try_into()?;
self.inner
.add_mint_quote(cdk_quote)
.await
.map_err(FfiError::internal)
}
async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
self.inner
.remove_mint_quote("e_id)
.await
.map_err(FfiError::internal)
}
async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
let cdk_quote = quote.try_into()?;
self.inner
.add_melt_quote(cdk_quote)
.await
.map_err(FfiError::internal)
}
async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
self.inner
.remove_melt_quote("e_id)
.await
.map_err(FfiError::internal)
}
async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
self.inner
.add_keys(cdk_keyset)
.await
.map_err(FfiError::internal)
}
async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
let cdk_id = id.try_into()?;
self.inner
.remove_keys(&cdk_id)
.await
.map_err(FfiError::internal)
}
async fn add_saga(&self, saga_json: String) -> Result<(), FfiError> {
let saga: WalletSaga = serde_json::from_str(&saga_json).map_err(FfiError::internal)?;
self.inner.add_saga(saga).await.map_err(FfiError::internal)
}
async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError> {
let id = uuid::Uuid::parse_str(&id).map_err(FfiError::internal)?;
let result = self.inner.get_saga(&id).await.map_err(FfiError::internal)?;
match result {
Some(saga) => {
let json = serde_json::to_string(&saga).map_err(FfiError::internal)?;
Ok(Some(json))
}
None => Ok(None),
}
}
async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError> {
let saga: WalletSaga = serde_json::from_str(&saga_json).map_err(FfiError::internal)?;
self.inner
.update_saga(saga)
.await
.map_err(FfiError::internal)
}
async fn delete_saga(&self, id: String) -> Result<(), FfiError> {
let id = uuid::Uuid::parse_str(&id).map_err(FfiError::internal)?;
self.inner
.delete_saga(&id)
.await
.map_err(FfiError::internal)
}
async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError> {
let result = self
.inner
.get_incomplete_sagas()
.await
.map_err(FfiError::internal)?;
result
.into_iter()
.map(|saga| serde_json::to_string(&saga).map_err(FfiError::internal))
.collect()
}
async fn reserve_proofs(
&self,
ys: Vec<PublicKey>,
operation_id: String,
) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
ys.into_iter().map(|pk| pk.try_into()).collect();
let cdk_ys = cdk_ys?;
self.inner
.reserve_proofs(cdk_ys, &operation_id)
.await
.map_err(FfiError::internal)
}
async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
self.inner
.release_proofs(&operation_id)
.await
.map_err(FfiError::internal)
}
async fn get_reserved_proofs(&self, operation_id: String) -> Result<Vec<ProofInfo>, FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
let result = self
.inner
.get_reserved_proofs(&operation_id)
.await
.map_err(FfiError::internal)?;
Ok(result.into_iter().map(Into::into).collect())
}
async fn reserve_melt_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
self.inner
.reserve_melt_quote("e_id, &operation_id)
.await
.map_err(FfiError::internal)
}
async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
self.inner
.release_melt_quote(&operation_id)
.await
.map_err(FfiError::internal)
}
async fn reserve_mint_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
self.inner
.reserve_mint_quote("e_id, &operation_id)
.await
.map_err(FfiError::internal)
}
async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError> {
let operation_id = uuid::Uuid::parse_str(&operation_id).map_err(FfiError::internal)?;
self.inner
.release_mint_quote(&operation_id)
.await
.map_err(FfiError::internal)
}
}
#[macro_export]
macro_rules! impl_ffi_wallet_database {
($wrapper_type:ty) => {
#[uniffi::export(async_runtime = "tokio")]
#[async_trait::async_trait]
impl WalletDatabase for $wrapper_type {
async fn get_proofs_by_ys(
&self,
ys: Vec<PublicKey>,
) -> Result<Vec<ProofInfo>, FfiError> {
self.inner.get_proofs_by_ys(ys).await
}
async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
self.inner.get_mint(mint_url).await
}
async fn get_mints(
&self,
) -> Result<std::collections::HashMap<MintUrl, Option<MintInfo>>, FfiError> {
self.inner.get_mints().await
}
async fn get_mint_keysets(
&self,
mint_url: MintUrl,
) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
self.inner.get_mint_keysets(mint_url).await
}
async fn get_keyset_by_id(
&self,
keyset_id: Id,
) -> Result<Option<KeySetInfo>, FfiError> {
self.inner.get_keyset_by_id(keyset_id).await
}
async fn get_mint_quote(
&self,
quote_id: String,
) -> Result<Option<MintQuote>, FfiError> {
self.inner.get_mint_quote(quote_id).await
}
async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
self.inner.get_mint_quotes().await
}
async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
self.inner.get_unissued_mint_quotes().await
}
async fn get_melt_quote(
&self,
quote_id: String,
) -> Result<Option<MeltQuote>, FfiError> {
self.inner.get_melt_quote(quote_id).await
}
async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
self.inner.get_melt_quotes().await
}
async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
self.inner.get_keys(id).await
}
async fn get_proofs(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
spending_conditions: Option<Vec<SpendingConditions>>,
) -> Result<Vec<ProofInfo>, FfiError> {
self.inner
.get_proofs(mint_url, unit, state, spending_conditions)
.await
}
async fn get_balance(
&self,
mint_url: Option<MintUrl>,
unit: Option<CurrencyUnit>,
state: Option<Vec<ProofState>>,
) -> Result<u64, FfiError> {
self.inner.get_balance(mint_url, unit, state).await
}
async fn get_transaction(
&self,
transaction_id: TransactionId,
) -> Result<Option<Transaction>, FfiError> {
self.inner.get_transaction(transaction_id).await
}
async fn list_transactions(
&self,
mint_url: Option<MintUrl>,
direction: Option<TransactionDirection>,
unit: Option<CurrencyUnit>,
) -> Result<Vec<Transaction>, FfiError> {
self.inner
.list_transactions(mint_url, direction, unit)
.await
}
async fn kv_read(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<Option<Vec<u8>>, FfiError> {
self.inner
.kv_read(primary_namespace, secondary_namespace, key)
.await
}
async fn kv_list(
&self,
primary_namespace: String,
secondary_namespace: String,
) -> Result<Vec<String>, FfiError> {
self.inner
.kv_list(primary_namespace, secondary_namespace)
.await
}
async fn kv_write(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
value: Vec<u8>,
) -> Result<(), FfiError> {
self.inner
.kv_write(primary_namespace, secondary_namespace, key, value)
.await
}
async fn kv_remove(
&self,
primary_namespace: String,
secondary_namespace: String,
key: String,
) -> Result<(), FfiError> {
self.inner
.kv_remove(primary_namespace, secondary_namespace, key)
.await
}
async fn update_proofs(
&self,
added: Vec<ProofInfo>,
removed_ys: Vec<PublicKey>,
) -> Result<(), FfiError> {
self.inner.update_proofs(added, removed_ys).await
}
async fn update_proofs_state(
&self,
ys: Vec<PublicKey>,
state: ProofState,
) -> Result<(), FfiError> {
self.inner.update_proofs_state(ys, state).await
}
async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
self.inner.add_transaction(transaction).await
}
async fn remove_transaction(
&self,
transaction_id: TransactionId,
) -> Result<(), FfiError> {
self.inner.remove_transaction(transaction_id).await
}
async fn update_mint_url(
&self,
old_mint_url: MintUrl,
new_mint_url: MintUrl,
) -> Result<(), FfiError> {
self.inner.update_mint_url(old_mint_url, new_mint_url).await
}
async fn increment_keyset_counter(
&self,
keyset_id: Id,
count: u32,
) -> Result<u32, FfiError> {
self.inner.increment_keyset_counter(keyset_id, count).await
}
async fn add_mint(
&self,
mint_url: MintUrl,
mint_info: Option<MintInfo>,
) -> Result<(), FfiError> {
self.inner.add_mint(mint_url, mint_info).await
}
async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
self.inner.remove_mint(mint_url).await
}
async fn add_mint_keysets(
&self,
mint_url: MintUrl,
keysets: Vec<KeySetInfo>,
) -> Result<(), FfiError> {
self.inner.add_mint_keysets(mint_url, keysets).await
}
async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
self.inner.add_mint_quote(quote).await
}
async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
self.inner.remove_mint_quote(quote_id).await
}
async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
self.inner.add_melt_quote(quote).await
}
async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
self.inner.remove_melt_quote(quote_id).await
}
async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
self.inner.add_keys(keyset).await
}
async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
self.inner.remove_keys(id).await
}
async fn add_p2pk_key(
&self,
pubkey: PublicKey,
derivation_path: String,
derivation_index: u32,
) -> Result<(), FfiError> {
self.inner
.add_p2pk_key(pubkey, derivation_path, derivation_index)
.await
}
async fn get_p2pk_key(
&self,
pubkey: PublicKey,
) -> Result<Option<P2PKSigningKey>, FfiError> {
self.inner.get_p2pk_key(pubkey).await
}
async fn list_p2pk_keys(&self) -> Result<Vec<P2PKSigningKey>, FfiError> {
self.inner.list_p2pk_keys().await
}
async fn latest_p2pk(&self) -> Result<Option<P2PKSigningKey>, FfiError> {
self.inner.latest_p2pk().await
}
async fn add_saga(&self, saga_json: String) -> Result<(), FfiError> {
self.inner.add_saga(saga_json).await
}
async fn get_saga(&self, id: String) -> Result<Option<String>, FfiError> {
self.inner.get_saga(id).await
}
async fn update_saga(&self, saga_json: String) -> Result<bool, FfiError> {
self.inner.update_saga(saga_json).await
}
async fn delete_saga(&self, id: String) -> Result<(), FfiError> {
self.inner.delete_saga(id).await
}
async fn get_incomplete_sagas(&self) -> Result<Vec<String>, FfiError> {
self.inner.get_incomplete_sagas().await
}
async fn reserve_proofs(
&self,
ys: Vec<PublicKey>,
operation_id: String,
) -> Result<(), FfiError> {
self.inner.reserve_proofs(ys, operation_id).await
}
async fn release_proofs(&self, operation_id: String) -> Result<(), FfiError> {
self.inner.release_proofs(operation_id).await
}
async fn get_reserved_proofs(
&self,
operation_id: String,
) -> Result<Vec<ProofInfo>, FfiError> {
self.inner.get_reserved_proofs(operation_id).await
}
async fn reserve_melt_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError> {
self.inner.reserve_melt_quote(quote_id, operation_id).await
}
async fn release_melt_quote(&self, operation_id: String) -> Result<(), FfiError> {
self.inner.release_melt_quote(operation_id).await
}
async fn reserve_mint_quote(
&self,
quote_id: String,
operation_id: String,
) -> Result<(), FfiError> {
self.inner.reserve_mint_quote(quote_id, operation_id).await
}
async fn release_mint_quote(&self, operation_id: String) -> Result<(), FfiError> {
self.inner.release_mint_quote(operation_id).await
}
}
};
}
#[derive(uniffi::Enum, Clone)]
pub enum WalletDbBackend {
Sqlite {
path: String,
},
#[cfg(feature = "postgres")]
Postgres {
url: String,
},
}
#[derive(uniffi::Enum)]
pub enum WalletStore {
Sqlite {
path: String,
},
#[cfg(feature = "postgres")]
Postgres {
url: String,
},
Custom {
db: Arc<dyn WalletDatabase>,
},
}
#[uniffi::export]
pub fn sqlite_wallet_store(path: String) -> WalletStore {
WalletStore::Sqlite { path }
}
#[cfg(feature = "postgres")]
#[uniffi::export]
pub fn postgres_wallet_store(url: String) -> WalletStore {
WalletStore::Postgres { url }
}
#[uniffi::export]
pub fn custom_wallet_store(db: Arc<dyn WalletDatabase>) -> WalletStore {
WalletStore::Custom { db }
}
pub fn resolve_wallet_store(store: WalletStore) -> Result<Arc<dyn WalletDatabase>, FfiError> {
match store {
WalletStore::Sqlite { path } => {
let sqlite = WalletSqliteDatabase::new(path)?;
Ok(sqlite as Arc<dyn WalletDatabase>)
}
#[cfg(feature = "postgres")]
WalletStore::Postgres { url } => {
let pg = WalletPostgresDatabase::new(url)?;
Ok(pg as Arc<dyn WalletDatabase>)
}
WalletStore::Custom { db } => Ok(db),
}
}
#[uniffi::export]
pub fn create_wallet_db(backend: WalletDbBackend) -> Result<Arc<dyn WalletDatabase>, FfiError> {
match backend {
WalletDbBackend::Sqlite { path } => {
let sqlite = WalletSqliteDatabase::new(path)?;
Ok(sqlite as Arc<dyn WalletDatabase>)
}
#[cfg(feature = "postgres")]
WalletDbBackend::Postgres { url } => {
let pg = WalletPostgresDatabase::new(url)?;
Ok(pg as Arc<dyn WalletDatabase>)
}
}
}
pub fn create_cdk_database_from_ffi(
ffi_db: Arc<dyn WalletDatabase>,
) -> Arc<dyn CdkWalletDatabase<cdk::cdk_database::Error> + Send + Sync> {
Arc::new(WalletDatabaseBridge::new(ffi_db))
}