Wallet Implementation

Building a complete cryptocurrency wallet in Rust.

Advanced⏱️ 55 min📚 Prerequisites: 1

Wallet Implementation

A wallet manages private keys, signs transactions, and tracks balances. Let's build a complete wallet.

Wallet Structure

RUST
struct Wallet {
    keypair: KeyPair,
    address: String,
    balance: u64,
    nonce: u64,
    transactions: Vec<Transaction>,
}

struct KeyPair {
    private_key: String,  // In production: use proper crypto types
    public_key: String,
}

Wallet Operations

Creating a Wallet

RUST
impl Wallet {
    fn new() -> Self {
        let keypair = generate_keypair();
        let address = derive_address(&keypair.public_key);
        
        Wallet {
            keypair,
            address,
            balance: 0,
            nonce: 0,
            transactions: Vec::new(),
        }
    }
}

Creating Transactions

RUST
fn create_transaction(&mut self, to: String, amount: u64, fee: u64) -> Result<Transaction, String> {
    if self.balance < amount + fee {
        return Err(String::from("Insufficient balance"));
    }
    
    let tx = Transaction {
        from: self.address.clone(),
        to,
        amount,
        fee,
        nonce: self.nonce,
        signature: None,
    };
    
    // Sign transaction
    let signed_tx = self.sign_transaction(tx)?;
    self.nonce += 1;
    
    Ok(signed_tx)
}

Signing Transactions

RUST
fn sign_transaction(&self, mut tx: Transaction) -> Result<Transaction, String> {
    // Create message to sign
    let message = format!("{}{}{}{}", tx.from, tx.to, tx.amount, tx.nonce);
    
    // Sign with private key (simplified)
    tx.signature = Some(format!("sig_{}", message));
    
    Ok(tx)
}

Wallet Features

  • Key Management: Generate and store keys securely
  • Transaction Creation: Build and sign transactions
  • Balance Tracking: Monitor account balance
  • Transaction History: Keep record of all transactions
  • Nonce Management: Track transaction nonces

Security Considerations

  • Private Key Storage: Must be encrypted
  • Key Derivation: Use proper key derivation functions
  • Secure Random: Use cryptographically secure RNG
  • Backup: Provide key backup/recovery

HD Wallets

Hierarchical Deterministic (HD) wallets:

  • BIP32: Key derivation
  • BIP39: Mnemonic phrases
  • BIP44: Multi-account structure

Implementation Example

RUST
struct Wallet {
    address: String,
    balance: u64,
    nonce: u64,
}

impl Wallet {
    fn new() -> Self {
        Wallet {
            address: String::from("0xWallet123"),
            balance: 0,
            nonce: 0,
        }
    }
    
    fn send(&mut self, to: String, amount: u64) -> Result<(), String> {
        if self.balance < amount {
            return Err(String::from("Insufficient balance"));
        }
        
        self.balance -= amount;
        self.nonce += 1;
        Ok(())
    }
}

Code Examples

Wallet Structure

Complete wallet implementation

RUST
struct KeyPair {
    private_key: String,
    public_key: String,
    address: String,
}
struct Wallet {
    keypair: KeyPair,
    balance: u64,
    nonce: u64,
}
impl Wallet {
    fn new() -> Self {
        let keypair = KeyPair {
            private_key: String::from("private_key_secret"),
            public_key: String::from("public_key_abc123"),
            address: String::from("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"),
        };
        Wallet {
            keypair,
            balance: 0,
            nonce: 0,
        }
    }
    fn address(&self) -> &str {
        &self.keypair.address
    }
    fn get_balance(&self) -> u64 {
        self.balance
    }
    fn update_balance(&mut self, new_balance: u64) {
        self.balance = new_balance;
    }
    fn increment_nonce(&mut self) {
        self.nonce += 1;
    }
}
fn main() {
    let mut wallet = Wallet::new();
    println!("Wallet address: {}", wallet.address());
    println!("Initial balance: {}", wallet.get_balance());
    wallet.update_balance(1000);
    wallet.increment_nonce();
    println!("Updated balance: {}", wallet.get_balance());
    println!("Nonce: {}", wallet.nonce);
}

Explanation:

A wallet manages keys, balance, and nonce. It provides methods to access wallet information and update state. In production, private keys must be stored securely (encrypted).

Transaction Creation

Creating and signing transactions

RUST
struct Transaction {
    from: String,
    to: String,
    amount: u64,
    fee: u64,
    nonce: u64,
    signature: Option<String>,
}
struct Wallet {
    address: String,
    balance: u64,
    nonce: u64,
}
impl Wallet {
    fn create_transaction(&mut self, to: String, amount: u64, fee: u64) -> Result<Transaction, String> {
        if self.balance < amount + fee {
            return Err(String::from("Insufficient balance"));
        }
        let tx = Transaction {
            from: self.address.clone(),
            to,
            amount,
            fee,
            nonce: self.nonce,
            signature: None,
        };
        let signed_tx = self.sign(tx)?;
        self.nonce += 1;
        Ok(signed_tx)
    }
    fn sign(&self, mut tx: Transaction) -> Result<Transaction, String> {
        let message = format!("{}{}{}{}", tx.from, tx.to, tx.amount, tx.nonce);
        tx.signature = Some(format!("sig_{}", message));
        Ok(tx)
    }
}
fn main() {
    let mut wallet = Wallet {
        address: String::from("0xAlice"),
        balance: 1000,
        nonce: 0,
    };
    match wallet.create_transaction(String::from("0xBob"), 200, 10) {
        Ok(tx) => {
            println!("Transaction created!");
            println!("From: {}, To: {}, Amount: {}", tx.from, tx.to, tx.amount);
            println!("Nonce: {}, Signed: {}", tx.nonce, tx.signature.is_some());
        },
        Err(e) => println!("Error: {}", e),
    }
}

Explanation:

Wallets create and sign transactions. The nonce increments with each transaction to prevent replay attacks. Signatures prove transaction authorization.

Exercises

Create Wallet

Create a wallet that can track balance and nonce!

Medium

Starter Code:

RUST
struct Wallet {
    address: String,
    balance: u64,
    nonce: u64,
}
fn main() {
    let mut wallet = Wallet::new();
    wallet.balance = 1000;
    wallet.send(200);
    println!("Balance: {}, Nonce: {}", wallet.get_balance(), wallet.nonce);
}