# Building a Single Mint Wallet with CDK
This tutorial will guide you through creating a simple Cashu wallet that interacts with a single mint. You'll learn how to:
- Set up a Rust project with CDK
- Create a wallet with a random seed
- Request a mint quote
- Mint tokens after payment
- Send tokens to another wallet
# 1. Project Setup
First, let's create a new Rust project:
cargo new cdk-tutorial
cd cdk-tutorial
Next, add the required dependencies to your Cargo.toml
file:
[dependencies]
cdk = { version = "*", default-features = false, features = ["wallet"] }
cdk-sqlite = { version = "*", features = ["wallet"] }
tokio = { version = "1", features = ["full"] }
rand = "0.8"
The cdk
crate provides the core Cashu functionality, while cdk-sqlite
gives us storage capabilities. We'll use tokio
for async runtime and rand
for generating random seeds.
# 2. Implementing the Wallet
Create a new file src/main.rs
with the following code:
use std::sync::Arc;
use std::time::Duration;
use cdk::amount::SplitTarget;
use cdk::nuts::nut00::ProofsMethods;
use cdk::nuts::{CurrencyUnit, MintQuoteState};
use cdk::wallet::{SendOptions, Wallet};
use cdk::Amount;
use cdk_sqlite::wallet::memory;
use rand::random;
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Step 1: Generate a random seed for the wallet
let seed = random::<[u8; 32]>();
println!("Created wallet with random seed");
// Step 2: Configure mint URL and amount
let mint_url = "https://testnut.cashu.space";
let unit = CurrencyUnit::Sat;
let amount = Amount::from(10);
println!("Using mint: {}", mint_url);
println!("Amount to mint: {} {}", amount, unit);
// Step 3: Initialize the memory store and create wallet
let localstore = Arc::new(memory::empty().await?);
let wallet = Wallet::new(mint_url, unit, localstore, &seed, None)?;
println!("Wallet initialized successfully");
// Step 4: Request a mint quote
let quote = wallet.mint_quote(amount, None).await?;
println!("\nPay this Lightning invoice to mint tokens:");
println!("{}", quote.request);
println!("\nWaiting for payment confirmation...");
// Step 5: Check the quote state until paid or timeout
let timeout = Duration::from_secs(120);
let start = std::time::Instant::now();
loop {
let status = wallet.mint_quote_state("e.id).await?;
match status.state {
MintQuoteState::Paid => {
println!("Payment received!");
break;
}
MintQuoteState::Expired => {
return Err("Quote expired".into());
}
_ => {
print!(".");
}
}
if start.elapsed() >= timeout {
return Err("Timeout waiting for payment".into());
}
sleep(Duration::from_secs(2)).await;
}
// Step 6: Mint the tokens
println!("\nMinting tokens...");
let proofs = wallet.mint("e.id, SplitTarget::default(), None).await?;
let receive_amount = proofs.total_amount()?;
println!("Successfully minted {} {}", receive_amount, unit);
// Step 7: Send tokens
println!("\nPreparing to send tokens...");
let prepared_send = wallet.prepare_send(amount, SendOptions::default()).await?;
let token = wallet.send(prepared_send, None).await?;
println!("\nToken to send to recipient:");
println!("{}", token);
println!("\nThe recipient can redeem this token using a Cashu wallet");
Ok(())
}
# 3. Running the Application
Build and run your application with:
cargo run
This will:
- Create a new wallet with a random seed
- Generate a Lightning invoice for 10 sats
- Wait for payment confirmation
- Mint tokens once payment is confirmed
- Create a token that can be sent to another user
# 4. Next Steps
- Try modifying the code to use a persistent SQLite database instead of memory storage
- Implement a function to receive and redeem tokens
- Add error handling for network issues
- Create a simple CLI interface with user commands
For more advanced usage, check out the CDK documentation (opens new window) and other tutorials in this series.