use std::sync::Arc;
use cdk::wallet::{BackupOptions, RestoreOptions, WalletRepositoryBuilder};
use cdk_sqlite::wallet::memory;
use rand::random;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::ERROR)
.init();
println!("NUT-XX Nostr Mint Backup Example");
println!("=================================\n");
let seed: [u8; 64] = random();
let localstore = Arc::new(memory::empty().await?);
let wallet = WalletRepositoryBuilder::new()
.localstore(localstore.clone())
.seed(seed)
.build()
.await?;
println!("Step 1: Adding mints to the wallet");
println!("-----------------------------------");
let mints = vec![
"https://fake.thesimplekid.dev",
"https://testnut.cashu.space",
];
for mint_url in &mints {
println!(" Adding mint: {}", mint_url);
match wallet.add_wallet(mint_url.parse()?).await {
Ok(_) => println!(" + Added successfully"),
Err(e) => println!(" x Failed to add: {}", e),
}
}
let wallets: Vec<cdk::Wallet> = wallet.get_wallets().await;
println!("\n Wallet now contains {} mint(s):", wallets.len());
for w in &wallets {
println!(" - {}", w.mint_url);
}
println!();
println!("Step 2: Deriving backup keys from seed");
println!("---------------------------------------");
let backup_keys = wallet.backup_keys()?;
println!(" Public key: {}", backup_keys.public_key().to_hex());
println!(" This key is deterministically derived from your wallet seed.");
println!(" Anyone with the same seed will derive the same keys.\n");
println!("Step 3: Backing up mint list to Nostr relays");
println!("---------------------------------------------");
let relays = vec!["wss://relay.damus.io", "wss://nos.lol"];
println!(" Relays: [{}]", relays.join(", "));
println!(" Publishing backup event...");
let backup_result = wallet
.backup_mints(
relays.clone(),
BackupOptions::new().client("nostr-backup-example"),
)
.await?;
println!(" + Backup published!");
println!(" Event ID: {}", backup_result.event_id);
println!(" Public Key: {}", backup_result.public_key.to_hex());
println!(" Mints backed up: {}", backup_result.mint_count);
println!();
println!("Step 4: Simulating restore on a new device");
println!("-------------------------------------------");
let new_localstore = Arc::new(memory::empty().await?);
let new_wallet = WalletRepositoryBuilder::new()
.localstore(new_localstore)
.seed(seed)
.build()
.await?;
let new_wallets: Vec<cdk::Wallet> = new_wallet.get_wallets().await;
println!(" New wallet starts with {} mint(s)", new_wallets.len());
let new_backup_keys = new_wallet.backup_keys()?;
println!(
" New wallet public key: {}",
new_backup_keys.public_key().to_hex()
);
println!(
" Keys match: {}",
backup_keys.public_key() == new_backup_keys.public_key()
);
println!();
println!("Step 5: Restoring mint list from Nostr relays");
println!("----------------------------------------------");
println!(" Fetching backup from relays...");
let restore_result = new_wallet
.restore_mints(relays.clone(), true, RestoreOptions::default())
.await?;
println!(" + Restore complete!");
println!(" Mints found in backup: {}", restore_result.mint_count);
println!(" Mints newly added: {}", restore_result.mints_added);
println!(" Backup timestamp: {}", restore_result.backup.timestamp);
println!("\n Mints in backup:");
for mint in &restore_result.backup.mints {
println!(" - {}", mint);
}
let restored_wallets: Vec<cdk::Wallet> = new_wallet.get_wallets().await;
println!(
"\n New wallet now contains {} mint(s):",
restored_wallets.len()
);
for w in &restored_wallets {
println!(" - {}", w.mint_url);
}
println!();
Ok(())
}