use std::collections::HashMap;
use std::sync::Arc;
use bip39::Mnemonic;
use cdk::wallet::wallet_repository::{
WalletRepository as CdkWalletRepository, WalletRepositoryBuilder,
};
use crate::error::FfiError;
use crate::types::*;
#[derive(uniffi::Object)]
pub struct WalletRepository {
inner: Arc<CdkWalletRepository>,
}
#[uniffi::export(async_runtime = "tokio")]
impl WalletRepository {
#[uniffi::constructor]
pub fn new(mnemonic: String, store: crate::database::WalletStore) -> Result<Self, FfiError> {
let db = crate::database::resolve_wallet_store(store)?;
let m = Mnemonic::parse(&mnemonic)
.map_err(|e| FfiError::internal(format!("Invalid mnemonic: {}", e)))?;
let seed = m.to_seed_normalized("");
let localstore = crate::database::create_cdk_database_from_ffi(db);
let rt = crate::runtime::RuntimeGuard::new().map_err(FfiError::internal)?;
let wallet = rt.block_on(async move {
WalletRepositoryBuilder::new()
.localstore(localstore)
.seed(seed)
.build()
.await
})?;
Ok(Self {
inner: Arc::new(wallet),
})
}
#[uniffi::constructor]
pub fn new_with_proxy(
mnemonic: String,
store: crate::database::WalletStore,
proxy_url: String,
) -> Result<Self, FfiError> {
let db = crate::database::resolve_wallet_store(store)?;
let m = Mnemonic::parse(&mnemonic)
.map_err(|e| FfiError::internal(format!("Invalid mnemonic: {}", e)))?;
let seed = m.to_seed_normalized("");
let localstore = crate::database::create_cdk_database_from_ffi(db);
let proxy_url = url::Url::parse(&proxy_url)
.map_err(|e| FfiError::internal(format!("Invalid URL: {}", e)))?;
let rt = crate::runtime::RuntimeGuard::new().map_err(FfiError::internal)?;
let wallet = rt.block_on(async move {
WalletRepositoryBuilder::new()
.localstore(localstore)
.seed(seed)
.proxy_url(proxy_url)
.build()
.await
})?;
Ok(Self {
inner: Arc::new(wallet),
})
}
pub async fn set_metadata_cache_ttl_for_mint(
&self,
mint_url: MintUrl,
ttl_secs: Option<u64>,
) -> Result<(), FfiError> {
let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
let wallets = self.inner.get_wallets().await;
if let Some(wallet) = wallets.iter().find(|w| w.mint_url == cdk_mint_url) {
let ttl = ttl_secs.map(std::time::Duration::from_secs);
wallet.set_metadata_cache_ttl(ttl);
Ok(())
} else {
Err(FfiError::internal(format!(
"Mint not found: {}",
cdk_mint_url
)))
}
}
pub async fn set_metadata_cache_ttl_for_all_mints(&self, ttl_secs: Option<u64>) {
let wallets = self.inner.get_wallets().await;
let ttl = ttl_secs.map(std::time::Duration::from_secs);
for wallet in wallets.iter() {
wallet.set_metadata_cache_ttl(ttl);
}
}
pub async fn create_wallet(
&self,
mint_url: MintUrl,
unit: Option<CurrencyUnit>,
target_proof_count: Option<u32>,
) -> Result<(), FfiError> {
let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
let config = target_proof_count.map(|count| {
cdk::wallet::wallet_repository::WalletConfig::new()
.with_target_proof_count(count as usize)
});
let unit_enum = unit.unwrap_or(CurrencyUnit::Sat);
self.inner
.create_wallet(cdk_mint_url, unit_enum.into(), config)
.await?;
Ok(())
}
pub async fn remove_wallet(
&self,
mint_url: MintUrl,
currency_unit: CurrencyUnit,
) -> Result<(), FfiError> {
let cdk_mint_url: cdk::mint_url::MintUrl = mint_url
.try_into()
.map_err(|_| FfiError::internal("invalid mint url"))?;
self.inner
.remove_wallet(cdk_mint_url, currency_unit.into())
.await
.map_err(|e| e.into()) }
pub async fn has_mint(&self, mint_url: MintUrl) -> bool {
if let Ok(cdk_mint_url) = mint_url.try_into() {
self.inner.has_mint(&cdk_mint_url).await
} else {
false
}
}
pub fn mint_backup_public_key(&self) -> Result<String, FfiError> {
let keys = self.inner.backup_keys()?;
Ok(keys.public_key().to_hex())
}
pub async fn backup_mints(
&self,
relays: Vec<String>,
options: BackupOptions,
) -> Result<BackupResult, FfiError> {
let result = self.inner.backup_mints(relays, options.into()).await?;
Ok(result.into())
}
pub async fn restore_mints(
&self,
relays: Vec<String>,
add_mints: bool,
options: RestoreOptions,
) -> Result<RestoreResult, FfiError> {
let result = self
.inner
.restore_mints(relays, add_mints, options.into())
.await?;
Ok(result.into())
}
pub async fn fetch_mint_backup(
&self,
relays: Vec<String>,
options: RestoreOptions,
) -> Result<MintBackup, FfiError> {
let backup = self.inner.fetch_backup(relays, options.into()).await?;
Ok(backup.into())
}
pub async fn get_balances(&self) -> Result<HashMap<WalletKey, Amount>, FfiError> {
let balances = self.inner.get_balances().await?;
let mut balance_map = HashMap::new();
for (wallet_key, amount) in balances {
balance_map.insert(wallet_key.into(), amount.into());
}
Ok(balance_map)
}
pub async fn get_wallets(&self) -> Vec<Arc<crate::wallet::Wallet>> {
let wallets = self.inner.get_wallets().await;
wallets
.into_iter()
.map(|w| Arc::new(crate::wallet::Wallet::from_inner(Arc::new(w))))
.collect()
}
pub async fn get_wallet(
&self,
mint_url: MintUrl,
unit: CurrencyUnit,
) -> Result<Arc<crate::wallet::Wallet>, FfiError> {
let cdk_mint_url: cdk::mint_url::MintUrl = mint_url.try_into()?;
let unit_cdk: cdk::nuts::CurrencyUnit = unit.into();
let wallet = self.inner.get_wallet(&cdk_mint_url, &unit_cdk).await?;
Ok(Arc::new(crate::wallet::Wallet::from_inner(Arc::new(
wallet,
))))
}
pub async fn get_token_data(
&self,
token: Arc<crate::token::Token>,
) -> Result<TokenData, FfiError> {
Ok(self.inner.get_token_data(&token.inner).await?.into())
}
}
#[derive(Debug, Clone, uniffi::Record)]
pub struct TokenData {
pub mint_url: MintUrl,
pub proofs: Vec<crate::types::Proof>,
pub memo: Option<String>,
pub value: Amount,
pub unit: CurrencyUnit,
pub redeem_fee: Option<Amount>,
}
impl From<cdk::wallet::TokenData> for TokenData {
fn from(data: cdk::wallet::TokenData) -> Self {
Self {
mint_url: data.mint_url.into(),
proofs: data.proofs.into_iter().map(Into::into).collect(),
memo: data.memo,
value: data.value.into(),
unit: data.unit.into(),
redeem_fee: data.redeem_fee.map(Into::into),
}
}
}