Solana Wallet Manager Challenge (Enums + Pattern Matching)
Build a Solana-style wallet manager using enums and pattern matching to handle account types, transaction states, and robust error handling.
Challenge Mission: Wallet Management System
Build a comprehensive wallet management system that handles different account types, transaction states, and error conditions. This challenge will test your mastery of enums, pattern matching, and error handling!
🎯 Challenge Overview
Create a Solana wallet management system that can:
- Handle different types of accounts
- Track transaction states
- Manage wallet operations with proper error handling
- Process different transaction types
- Validate operations using pattern matching
🏗️ Part 1: Core Enums and Structs
Start by implementing the foundational types:
use std::collections::HashMap;
// Account types in Solana ecosystem
#[derive(Debug, Clone, PartialEq)]
enum AccountType {
SystemAccount,
TokenAccount {
mint: String,
decimals: u8
},
ProgramAccount {
executable: bool,
owner: String
},
NFTAccount {
collection: String,
metadata_uri: String
},
}
// Account status
#[derive(Debug, Clone, PartialEq)]
enum AccountStatus {
Active,
Frozen { reason: String, until_slot: Option<u64> },
Suspended { violation: String },
}
// Transaction types
#[derive(Debug, Clone)]
enum TransactionType {
Transfer { amount: u64, token_mint: Option<String> },
Mint { amount: u64, recipient: String },
Burn { amount: u64 },
Stake { amount: u64, validator: String },
CreateAccount { account_type: AccountType },
}
// Transaction status
#[derive(Debug, Clone, PartialEq)]
enum TxStatus {
Pending,
Confirmed { slot: u64, signature: String },
Failed { error: String, slot: u64 },
}
// Wallet operation errors
#[derive(Debug)]
enum WalletError {
AccountNotFound(String),
InsufficientBalance { requested: u64, available: u64 },
AccountFrozen { address: String, reason: String },
InvalidTransaction { reason: String },
NetworkError(String),
UnauthorizedOperation,
}
// Account struct
#[derive(Debug, Clone)]
struct Account {
address: String,
balance: u64,
account_type: AccountType,
status: AccountStatus,
created_at: u64, // slot number
}
// Transaction struct
#[derive(Debug, Clone)]
struct Transaction {
id: String,
from: String,
to: Option<String>,
tx_type: TransactionType,
status: TxStatus,
created_at: u64,
}🔧 Part 2: Account Implementation
Implement the Account with methods for different operations:
impl Account {
fn new(address: String, account_type: AccountType, created_at: u64) -> Self {
Self {
address,
balance: 0,
account_type,
status: AccountStatus::Active,
created_at,
}
}
// TODO: Implement these methods using pattern matching
fn is_active(&self) -> bool {
// Return true only if account is active
todo!()
}
fn can_send(&self, amount: u64) -> Result<(), WalletError> {
// Check if account can send the requested amount
// Consider account status and balance
todo!()
}
fn get_account_info(&self) -> String {
// Return formatted account information based on account type
// Use match to handle different account types
todo!()
}
fn freeze(&mut self, reason: String, until_slot: Option<u64>) -> Result<(), WalletError> {
// Freeze account if it's currently active
todo!()
}
fn unfreeze(&mut self, current_slot: u64) -> Result<(), WalletError> {
// Unfreeze account based on conditions
todo!()
}
fn debit(&mut self, amount: u64) -> Result<(), WalletError> {
// Remove amount from balance with proper validation
todo!()
}
fn credit(&mut self, amount: u64) -> Result<(), WalletError> {
// Add amount to balance if account can receive
todo!()
}
}🚀 Part 3: Transaction Implementation
Implement the Transaction with state management:
impl Transaction {
fn new(
id: String,
from: String,
to: Option<String>,
tx_type: TransactionType,
created_at: u64
) -> Self {
Self {
id,
from,
to,
tx_type,
status: TxStatus::Pending,
created_at,
}
}
// TODO: Implement these methods
fn confirm(&mut self, slot: u64, signature: String) {
// Update status to confirmed
todo!()
}
fn fail(&mut self, error: String, slot: u64) {
// Update status to failed
todo!()
}
fn get_amount(&self) -> Option<u64> {
// Extract amount from transaction type using pattern matching
todo!()
}
fn is_pending(&self) -> bool {
// Check if transaction is still pending
todo!()
}
fn get_status_info(&self) -> String {
// Return formatted status information
todo!()
}
fn involves_account(&self, address: &str) -> bool {
// Check if transaction involves the given account
todo!()
}
}💼 Part 4: Wallet Manager Implementation
Create the main wallet manager:
struct WalletManager {
accounts: Vec<Account>,
transactions: Vec<Transaction>,
current_slot: u64,
next_tx_id: u64,
}
impl WalletManager {
fn new() -> Self {
Self {
accounts: Vec::new(),
transactions: Vec::new(),
current_slot: 1,
next_tx_id: 1,
}
}
// TODO: Implement these core methods
fn create_account(&mut self, address: String, account_type: AccountType) -> Result<(), WalletError> {
// Create new account with validation
todo!()
}
fn get_account(&self, address: &str) -> Result<&Account, WalletError> {
// Find account by address
todo!()
}
fn get_account_mut(&mut self, address: &str) -> Result<&mut Account, WalletError> {
// Find mutable account by address
todo!()
}
fn transfer(
&mut self,
from: &str,
to: &str,
amount: u64,
token_mint: Option<String>
) -> Result<String, WalletError> {
// Execute transfer between accounts
// Return transaction ID
todo!()
}
fn mint_tokens(
&mut self,
to: &str,
amount: u64,
mint_authority: &str
) -> Result<String, WalletError> {
// Mint new tokens to account
todo!()
}
fn burn_tokens(&mut self, from: &str, amount: u64) -> Result<String, WalletError> {
// Burn tokens from account
todo!()
}
fn get_balance(&self, address: &str) -> Result<u64, WalletError> {
// Get account balance
todo!()
}
fn list_accounts_by_type(&self, account_type: &AccountType) -> Vec<&Account> {
// Filter accounts by type using pattern matching
todo!()
}
fn get_pending_transactions(&self) -> Vec<&Transaction> {
// Get all pending transactions
todo!()
}
fn confirm_transaction(&mut self, tx_id: &str, signature: String) -> Result<(), WalletError> {
// Confirm a pending transaction
todo!()
}
fn get_account_transactions(&self, address: &str) -> Vec<&Transaction> {
// Get all transactions involving an account
todo!()
}
fn freeze_account(&mut self, address: &str, reason: String) -> Result<(), WalletError> {
// Freeze account for violations
todo!()
}
fn advance_slot(&mut self) {
// Simulate blockchain progression
self.current_slot += 1;
}
fn get_network_stats(&self) -> NetworkStats {
// Generate network statistics using pattern matching
todo!()
}
}
// Network statistics
#[derive(Debug)]
struct NetworkStats {
total_accounts: usize,
active_accounts: usize,
frozen_accounts: usize,
total_transactions: usize,
pending_transactions: usize,
confirmed_transactions: usize,
failed_transactions: usize,
total_volume: u64,
}🧪 Part 5: Testing Your Implementation
Create a comprehensive test suite:
fn main() {
let mut wallet_manager = WalletManager::new();
// Test 1: Create different account types
println!("=== Test 1: Creating Accounts ===");
// Create system account
wallet_manager.create_account(
"11111111111111111111111111111112".to_string(),
AccountType::SystemAccount,
).expect("Failed to create system account");
// Create token account
wallet_manager.create_account(
"TokenAccount1111111111111111111111".to_string(),
AccountType::TokenAccount {
mint: "SOL".to_string(),
decimals: 9,
},
).expect("Failed to create token account");
// Create NFT account
wallet_manager.create_account(
"NFTAccount11111111111111111111111".to_string(),
AccountType::NFTAccount {
collection: "SolanaMonkeys".to_string(),
metadata_uri: "https://example.com/metadata/1".to_string(),
},
).expect("Failed to create NFT account");
// Test 2: Mint some tokens
println!("\n=== Test 2: Minting Tokens ===");
match wallet_manager.mint_tokens(
"TokenAccount1111111111111111111111",
1000000000, // 1 SOL
"mint_authority",
) {
Ok(tx_id) => println!("Minted tokens, transaction: {}", tx_id),
Err(e) => println!("Mint failed: {:?}", e),
}
// Test 3: Transfer tokens
println!("\n=== Test 3: Transfer Operations ===");
// First create another account to transfer to
wallet_manager.create_account(
"ReceiverAccount11111111111111111111".to_string(),
AccountType::TokenAccount {
mint: "SOL".to_string(),
decimals: 9,
},
).expect("Failed to create receiver account");
match wallet_manager.transfer(
"TokenAccount1111111111111111111111",
"ReceiverAccount11111111111111111111",
500000000, // 0.5 SOL
Some("SOL".to_string()),
) {
Ok(tx_id) => println!("Transfer successful, transaction: {}", tx_id),
Err(e) => println!("Transfer failed: {:?}", e),
}
// Test 4: Account freezing
println!("\n=== Test 4: Account Management ===");
match wallet_manager.freeze_account(
"TokenAccount1111111111111111111111",
"Suspicious activity detected".to_string(),
) {
Ok(()) => println!("Account frozen successfully"),
Err(e) => println!("Failed to freeze account: {:?}", e),
}
// Try to transfer from frozen account
match wallet_manager.transfer(
"TokenAccount1111111111111111111111",
"ReceiverAccount11111111111111111111",
100000000,
Some("SOL".to_string()),
) {
Ok(tx_id) => println!("Unexpected success: {}", tx_id),
Err(e) => println!("Expected failure from frozen account: {:?}", e),
}
// Test 5: Transaction confirmation
println!("\n=== Test 5: Transaction Processing ===");
let pending_txs = wallet_manager.get_pending_transactions();
println!("Pending transactions: {}", pending_txs.len());
for tx in pending_txs {
println!("Transaction {}: {}", tx.id, tx.get_status_info());
// Simulate confirming transaction
wallet_manager.advance_slot();
if let Err(e) = wallet_manager.confirm_transaction(&tx.id, "signature123".to_string()) {
println!("Failed to confirm transaction {}: {:?}", tx.id, e);
}
}
// Test 6: Generate statistics
println!("\n=== Test 6: Network Statistics ===");
let stats = wallet_manager.get_network_stats();
println!("Network Statistics: {:#?}", stats);
// Test 7: Account filtering
println!("\n=== Test 7: Account Filtering ===");
let token_accounts = wallet_manager.list_accounts_by_type(&AccountType::TokenAccount {
mint: "SOL".to_string(),
decimals: 9,
});
println!("SOL Token accounts: {}", token_accounts.len());
for account in token_accounts {
println!("- {}: {} ({})",
account.address,
account.balance,
account.get_account_info()
);
}
}🎯 Challenge Requirements
Your implementation must:
- Use Pattern Matching: All enum handling must use
matchexpressions orif let - Handle All Variants: Every enum must have complete pattern coverage
- Error Handling: Use
Result<T, E>for all fallible operations - State Validation: Accounts and transactions must validate state changes
- Memory Safety: No panics - handle all error conditions gracefully
🏆 Bonus Challenges
If you complete the basic implementation, try these extensions:
Bonus 1: Transaction History
impl WalletManager {
fn get_transaction_history(&self, address: &str, limit: usize) -> Vec<&Transaction> {
// Return recent transactions for an account
todo!()
}
fn calculate_transaction_fees(&self, tx_type: &TransactionType) -> u64 {
// Calculate fees based on transaction type
todo!()
}
}Bonus 2: Account Recovery
#[derive(Debug)]
enum RecoveryMethod {
SeedPhrase { phrase: String },
PrivateKey { key: String },
MultiSig { required_signatures: u8, signers: Vec<String> },
}
impl Account {
fn initiate_recovery(&mut self, method: RecoveryMethod) -> Result<String, WalletError> {
// Implement account recovery
todo!()
}
}Bonus 3: Staking System
#[derive(Debug)]
struct StakeAccount {
delegated_to: String,
stake_amount: u64,
rewards_earned: u64,
activation_epoch: u64,
}
impl WalletManager {
fn stake_tokens(&mut self, from: &str, validator: &str, amount: u64) -> Result<String, WalletError> {
// Implement token staking
todo!()
}
fn unstake_tokens(&mut self, stake_account: &str) -> Result<String, WalletError> {
// Implement unstaking with cooldown
todo!()
}
}✅ Success Criteria
Your solution should demonstrate:
- Complete enum coverage with no unreachable patterns
- Proper error propagation using the
?operator - State machine logic for account and transaction status
- Pattern matching expertise for all data extraction
- Memory safety with no unwrap() calls on user input
Challenge Complete!
Once you've implemented this wallet manager, you'll have mastered the core patterns used in Solana program development. These same concepts appear in every Anchor program - from account validation to instruction processing!
Ready for tomorrow? Day 5 will cover Collections and String Handling - the tools you need to efficiently manage data structures in blockchain applications!