Skip to content

Commit 0bdf946

Browse files
CopilotSteake
andcommitted
Add wallet CLI binary and update release workflow to include bitcell-wallet
Co-authored-by: Steake <530040+Steake@users.noreply.github.com>
1 parent 8fd89f1 commit 0bdf946

3 files changed

Lines changed: 184 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
${{ runner.os }}-${{ matrix.target }}-cargo-
5353
5454
- name: Build release binaries
55-
run: cargo build --release --target ${{ matrix.target }} -p bitcell-node -p bitcell-admin
55+
run: cargo build --release --target ${{ matrix.target }} -p bitcell-node -p bitcell-admin -p bitcell-wallet
5656

5757
- name: Create artifact directory
5858
shell: bash
@@ -64,13 +64,15 @@ jobs:
6464
run: |
6565
cp target/${{ matrix.target }}/release/bitcell-node artifacts/
6666
cp target/${{ matrix.target }}/release/bitcell-admin artifacts/
67+
cp target/${{ matrix.target }}/release/bitcell-wallet artifacts/
6768
6869
- name: Copy binaries (Windows)
6970
if: runner.os == 'Windows'
7071
shell: bash
7172
run: |
7273
cp target/${{ matrix.target }}/release/bitcell-node.exe artifacts/
7374
cp target/${{ matrix.target }}/release/bitcell-admin.exe artifacts/
75+
cp target/${{ matrix.target }}/release/bitcell-wallet.exe artifacts/
7476
7577
- name: Create archive (Unix)
7678
if: runner.os != 'Windows'

crates/bitcell-wallet/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ license.workspace = true
88
repository.workspace = true
99
description = "Modular wallet for BitCell blockchain with multi-chain support"
1010

11+
[[bin]]
12+
name = "bitcell-wallet"
13+
path = "src/main.rs"
14+
1115
[dependencies]
1216
bitcell-crypto = { path = "../bitcell-crypto" }
1317
bitcell-state = { path = "../bitcell-state" }
@@ -36,6 +40,9 @@ bincode.workspace = true
3640
# Error handling
3741
thiserror.workspace = true
3842

43+
# CLI
44+
clap = { version = "4", features = ["derive"] }
45+
3946
# Utilities
4047
zeroize.workspace = true
4148
parking_lot.workspace = true

crates/bitcell-wallet/src/main.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)