|
| 1 | +//! BitCell Wallet CLI |
| 2 | +//! |
| 3 | +//! Command-line interface for the BitCell wallet. |
| 4 | +
|
| 5 | +use bitcell_wallet::{Chain, Mnemonic, Wallet, WalletConfig}; |
| 6 | +use clap::{Parser, Subcommand}; |
| 7 | + |
| 8 | +#[derive(Parser)] |
| 9 | +#[command(name = "bitcell-wallet")] |
| 10 | +#[command(about = "BitCell blockchain wallet", long_about = None)] |
| 11 | +struct Cli { |
| 12 | + #[command(subcommand)] |
| 13 | + command: Commands, |
| 14 | +} |
| 15 | + |
| 16 | +#[derive(Subcommand)] |
| 17 | +enum Commands { |
| 18 | + /// Create a new wallet with a fresh mnemonic |
| 19 | + Create { |
| 20 | + /// Wallet name |
| 21 | + #[arg(short, long, default_value = "Default Wallet")] |
| 22 | + name: String, |
| 23 | + }, |
| 24 | + /// Restore a wallet from a mnemonic phrase |
| 25 | + Restore { |
| 26 | + /// Mnemonic phrase (24 words) |
| 27 | + #[arg(short, long)] |
| 28 | + mnemonic: String, |
| 29 | + /// Optional passphrase |
| 30 | + #[arg(short, long, default_value = "")] |
| 31 | + passphrase: String, |
| 32 | + }, |
| 33 | + /// Generate a new address |
| 34 | + Address { |
| 35 | + /// Chain to generate address for |
| 36 | + #[arg(short, long, default_value = "bitcell")] |
| 37 | + chain: String, |
| 38 | + }, |
| 39 | + /// Show wallet balance |
| 40 | + Balance { |
| 41 | + /// Chain to show balance for |
| 42 | + #[arg(short, long)] |
| 43 | + chain: Option<String>, |
| 44 | + }, |
| 45 | + /// Show version information |
| 46 | + Version, |
| 47 | +} |
| 48 | + |
| 49 | +fn parse_chain(chain: &str) -> Result<Chain, String> { |
| 50 | + match chain.to_lowercase().as_str() { |
| 51 | + "bitcell" | "cell" => Ok(Chain::BitCell), |
| 52 | + "bitcoin" | "btc" => Ok(Chain::Bitcoin), |
| 53 | + "bitcoin-testnet" | "btc-testnet" => Ok(Chain::BitcoinTestnet), |
| 54 | + "ethereum" | "eth" => Ok(Chain::Ethereum), |
| 55 | + "ethereum-sepolia" | "eth-sepolia" => Ok(Chain::EthereumSepolia), |
| 56 | + _ => Err(format!("Unknown chain: {}", chain)), |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +fn main() { |
| 61 | + let cli = Cli::parse(); |
| 62 | + |
| 63 | + match cli.command { |
| 64 | + Commands::Create { name } => { |
| 65 | + println!("💰 BitCell Wallet"); |
| 66 | + println!("================="); |
| 67 | + println!(); |
| 68 | + |
| 69 | + let config = WalletConfig { |
| 70 | + name: name.clone(), |
| 71 | + ..WalletConfig::default() |
| 72 | + }; |
| 73 | + |
| 74 | + let (wallet, mnemonic) = Wallet::create_new(config); |
| 75 | + |
| 76 | + println!("✅ Wallet '{}' created successfully!", name); |
| 77 | + println!(); |
| 78 | + println!("⚠️ IMPORTANT: Write down your recovery phrase and store it safely!"); |
| 79 | + println!(" Anyone with this phrase can access your funds."); |
| 80 | + println!(); |
| 81 | + println!("Recovery Phrase ({} words):", mnemonic.word_count()); |
| 82 | + println!("─────────────────────────────────────────────────────────"); |
| 83 | + for (i, word) in mnemonic.words().iter().enumerate() { |
| 84 | + print!("{:2}. {:<12}", i + 1, word); |
| 85 | + if (i + 1) % 4 == 0 { |
| 86 | + println!(); |
| 87 | + } |
| 88 | + } |
| 89 | + println!("─────────────────────────────────────────────────────────"); |
| 90 | + println!(); |
| 91 | + |
| 92 | + // Show generated addresses |
| 93 | + println!("Generated Addresses:"); |
| 94 | + for addr in wallet.all_addresses() { |
| 95 | + println!(" {:?}: {}", addr.chain(), addr.to_string_formatted()); |
| 96 | + } |
| 97 | + } |
| 98 | + Commands::Restore { |
| 99 | + mnemonic, |
| 100 | + passphrase, |
| 101 | + } => { |
| 102 | + println!("💰 BitCell Wallet - Restore"); |
| 103 | + println!("==========================="); |
| 104 | + println!(); |
| 105 | + |
| 106 | + match Mnemonic::from_phrase(&mnemonic) { |
| 107 | + Ok(mnemonic) => { |
| 108 | + let wallet = |
| 109 | + Wallet::from_mnemonic(&mnemonic, &passphrase, WalletConfig::default()); |
| 110 | + |
| 111 | + println!("✅ Wallet restored successfully!"); |
| 112 | + println!(); |
| 113 | + println!("Generated Addresses:"); |
| 114 | + for addr in wallet.all_addresses() { |
| 115 | + println!(" {:?}: {}", addr.chain(), addr.to_string_formatted()); |
| 116 | + } |
| 117 | + } |
| 118 | + Err(e) => { |
| 119 | + eprintln!("❌ Error: Invalid mnemonic phrase - {}", e); |
| 120 | + std::process::exit(1); |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + Commands::Address { chain } => { |
| 125 | + match parse_chain(&chain) { |
| 126 | + Ok(chain) => { |
| 127 | + // For demo purposes, create a temporary wallet |
| 128 | + let (mut wallet, _) = Wallet::create_new(WalletConfig::default()); |
| 129 | + match wallet.next_address(chain) { |
| 130 | + Ok(addr) => { |
| 131 | + println!("New {:?} address: {}", chain, addr.to_string_formatted()); |
| 132 | + } |
| 133 | + Err(e) => { |
| 134 | + eprintln!("❌ Error generating address: {}", e); |
| 135 | + std::process::exit(1); |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + Err(e) => { |
| 140 | + eprintln!("❌ Error: {}", e); |
| 141 | + std::process::exit(1); |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | + Commands::Balance { chain } => { |
| 146 | + println!("💰 BitCell Wallet - Balance"); |
| 147 | + println!("==========================="); |
| 148 | + println!(); |
| 149 | + |
| 150 | + // For demo purposes, show zero balances |
| 151 | + let chains = if let Some(chain_str) = chain { |
| 152 | + match parse_chain(&chain_str) { |
| 153 | + Ok(c) => vec![c], |
| 154 | + Err(e) => { |
| 155 | + eprintln!("❌ Error: {}", e); |
| 156 | + std::process::exit(1); |
| 157 | + } |
| 158 | + } |
| 159 | + } else { |
| 160 | + vec![Chain::BitCell, Chain::Bitcoin, Chain::Ethereum] |
| 161 | + }; |
| 162 | + |
| 163 | + for chain in chains { |
| 164 | + println!("{:?}: 0.00", chain); |
| 165 | + } |
| 166 | + println!(); |
| 167 | + println!("Note: Connect to a node to fetch actual balances."); |
| 168 | + } |
| 169 | + Commands::Version => { |
| 170 | + println!("bitcell-wallet v0.1.0"); |
| 171 | + println!("BitCell blockchain wallet"); |
| 172 | + } |
| 173 | + } |
| 174 | +} |
0 commit comments