Sfoglia il codice sorgente

drk: wallet command use subcommand structure instead of flags

skoupidi 1 anno fa
parent
commit
5236c4516d

+ 16 - 20
bin/drk/src/cli_util.rs

@@ -128,38 +128,34 @@ pub fn generate_completions(shell: &str) -> Result<()> {
         .arg(shell_arg);
 
     // Wallet
-    let initialize =
-        Arg::with_name("initialize").long("initialize").help("Initialize wallet database");
+    let initialize = SubCommand::with_name("initialize").about("Initialize wallet database");
 
-    let keygen =
-        Arg::with_name("keygen").long("keygen").help("Generate a new keypair in the wallet");
+    let keygen = SubCommand::with_name("keygen").about("Generate a new keypair in the wallet");
 
-    let balance =
-        Arg::with_name("balance").long("balance").help("Query the wallet for known balances");
+    let balance = SubCommand::with_name("balance").about("Query the wallet for known balances");
 
-    let address =
-        Arg::with_name("address").long("address").help("Get the default address in the wallet");
+    let address = SubCommand::with_name("address").about("Get the default address in the wallet");
 
     let addresses =
-        Arg::with_name("addresses").long("addresses").help("Print all the addresses in the wallet");
+        SubCommand::with_name("addresses").about("Print all the addresses in the wallet");
 
-    let default_address = Arg::with_name("default-address")
-        .long("default-address")
-        .takes_value(true)
-        .help("Set the default address in the wallet");
+    let index = Arg::with_name("index").help("Identifier of the address");
+
+    let default_address = SubCommand::with_name("default-address")
+        .about("Set the default address in the wallet")
+        .arg(index);
 
     let secrets =
-        Arg::with_name("secrets").long("secrets").help("Print all the secret keys from the wallet");
+        SubCommand::with_name("secrets").about("Print all the secret keys from the wallet");
 
-    let import_secrets = Arg::with_name("import-secrets")
-        .long("import-secrets")
-        .help("Import secret keys from stdin into the wallet, separated by newlines");
+    let import_secrets = SubCommand::with_name("import-secrets")
+        .about("Import secret keys from stdin into the wallet, separated by newlines");
 
-    let tree = Arg::with_name("tree").long("tree").help("Print the Merkle tree in the wallet");
+    let tree = SubCommand::with_name("tree").about("Print the Merkle tree in the wallet");
 
-    let coins = Arg::with_name("coins").long("coins").help("Print all the coins in the wallet");
+    let coins = SubCommand::with_name("coins").about("Print all the coins in the wallet");
 
-    let wallet = SubCommand::with_name("wallet").about("Wallet operations").args(&vec![
+    let wallet = SubCommand::with_name("wallet").about("Wallet operations").subcommands(vec![
         initialize,
         keygen,
         balance,

+ 236 - 233
bin/drk/src/interactive.rs

@@ -111,16 +111,16 @@ fn completion(buf: &str, lc: &mut Vec<String>) {
 
     if buf.starts_with("w") {
         lc.push("wallet".to_string());
-        lc.push("wallet --initialize".to_string());
-        lc.push("wallet --keygen".to_string());
-        lc.push("wallet --balance".to_string());
-        lc.push("wallet --address".to_string());
-        lc.push("wallet --addresses".to_string());
-        lc.push("wallet --default-address".to_string());
-        lc.push("wallet --secrets".to_string());
-        lc.push("wallet --import-secrets".to_string());
-        lc.push("wallet --tree".to_string());
-        lc.push("wallet --coins".to_string());
+        lc.push("wallet initialize".to_string());
+        lc.push("wallet keygen".to_string());
+        lc.push("wallet balance".to_string());
+        lc.push("wallet address".to_string());
+        lc.push("wallet addresses".to_string());
+        lc.push("wallet default-address".to_string());
+        lc.push("wallet secrets".to_string());
+        lc.push("wallet import-secrets".to_string());
+        lc.push("wallet tree".to_string());
+        lc.push("wallet coins".to_string());
         return
     }
 
@@ -210,10 +210,8 @@ fn completion(buf: &str, lc: &mut Vec<String>) {
 fn hints(buf: &str) -> Option<(String, i32, bool)> {
     match buf {
         "completions " => Some(("<shell>".to_string(), 35, false)), // 35 = magenta
-        "wallet " => Some(("--(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
-        "wallet -" => Some(("-(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
-        "wallet --" => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
-        "wallet --default-address " => Some(("<address_id>".to_string(), 35, false)),
+        "wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
+        "wallet default-address " => Some(("<index>".to_string(), 35, false)),
         "unspend " => Some(("<coin>".to_string(), 35, false)),
         "transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), 35, false)),
         "otc " => Some(("(init|join|inspect|sign)".to_string(), 35, false)),
@@ -445,279 +443,284 @@ fn handle_completions(parts: &[&str]) {
 /// Auxiliary function to define the wallet command handling.
 async fn handle_wallet(drk: &DrkPtr, parts: &[&str]) {
     // Check correct command structure
-    if parts.len() != 2 && parts.len() != 3 {
+    if parts.len() < 2 {
         println!("Malformed `wallet` command");
-        println!("Usage: wallet --(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
+        println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
         return
     }
 
-    // Handle command flag
-    if parts[1] == "--initialize" {
-        let lock = drk.read().await;
-        if let Err(e) = lock.initialize_wallet().await {
-            println!("Error initializing wallet: {e:?}");
-            return
-        }
-        if let Err(e) = lock.initialize_money().await {
-            println!("Failed to initialize Money: {e:?}");
-            return
-        }
-        if let Err(e) = lock.initialize_dao().await {
-            println!("Failed to initialize DAO: {e:?}");
-            return
-        }
-        if let Err(e) = lock.initialize_deployooor() {
-            println!("Failed to initialize Deployooor: {e:?}");
+    // Handle subcommand
+    match parts[1] {
+        "initialize" => handle_wallet_initialize(drk).await,
+        "keygen" => handle_wallet_keygen(drk).await,
+        "balance" => handle_wallet_balance(drk).await,
+        "address" => handle_wallet_address(drk).await,
+        "addresses" => handle_wallet_addresses(drk).await,
+        "default-address" => handle_wallet_default_address(drk, parts).await,
+        "secrets" => handle_wallet_secrets(drk).await,
+        "import-secrets" => handle_wallet_import_secrets(drk).await,
+        "tree" => handle_wallet_tree(drk).await,
+        "coins" => handle_wallet_coins(drk).await,
+        _ => {
+            println!("Unreconized wallet subcommand: {}", parts[1]);
+            println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
         }
-        return
     }
+}
 
-    if parts[1] == "--keygen" {
-        if let Err(e) = drk.read().await.money_keygen().await {
-            println!("Failed to generate keypair: {e:?}");
-        }
+/// Auxiliary function to define the wallet initialize subcommand handling.
+async fn handle_wallet_initialize(drk: &DrkPtr) {
+    let lock = drk.read().await;
+    if let Err(e) = lock.initialize_wallet().await {
+        println!("Error initializing wallet: {e:?}");
+        return
+    }
+    if let Err(e) = lock.initialize_money().await {
+        println!("Failed to initialize Money: {e:?}");
         return
     }
+    if let Err(e) = lock.initialize_dao().await {
+        println!("Failed to initialize DAO: {e:?}");
+        return
+    }
+    if let Err(e) = lock.initialize_deployooor() {
+        println!("Failed to initialize Deployooor: {e:?}");
+    }
+}
 
-    if parts[1] == "--balance" {
-        let lock = drk.read().await;
-        let balmap = match lock.money_balance().await {
-            Ok(m) => m,
-            Err(e) => {
-                println!("Failed to fetch balances map: {e:?}");
-                return
-            }
-        };
+/// Auxiliary function to define the wallet keygen subcommand handling.
+async fn handle_wallet_keygen(drk: &DrkPtr) {
+    if let Err(e) = drk.read().await.money_keygen().await {
+        println!("Failed to generate keypair: {e:?}");
+    }
+}
 
-        let aliases_map = match lock.get_aliases_mapped_by_token().await {
-            Ok(m) => m,
-            Err(e) => {
-                println!("Failed to fetch aliases map: {e:?}");
-                return
-            }
+/// Auxiliary function to define the wallet balance subcommand handling.
+async fn handle_wallet_balance(drk: &DrkPtr) {
+    let lock = drk.read().await;
+    let balmap = match lock.money_balance().await {
+        Ok(m) => m,
+        Err(e) => {
+            println!("Failed to fetch balances map: {e:?}");
+            return
+        }
+    };
+
+    let aliases_map = match lock.get_aliases_mapped_by_token().await {
+        Ok(m) => m,
+        Err(e) => {
+            println!("Failed to fetch aliases map: {e:?}");
+            return
+        }
+    };
+
+    // Create a prettytable with the new data:
+    let mut table = Table::new();
+    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+    table.set_titles(row!["Token ID", "Aliases", "Balance"]);
+    for (token_id, balance) in balmap.iter() {
+        let aliases = match aliases_map.get(token_id) {
+            Some(a) => a,
+            None => "-",
         };
 
-        // Create a prettytable with the new data:
-        let mut table = Table::new();
-        table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-        table.set_titles(row!["Token ID", "Aliases", "Balance"]);
-        for (token_id, balance) in balmap.iter() {
-            let aliases = match aliases_map.get(token_id) {
-                Some(a) => a,
-                None => "-",
-            };
+        table.add_row(row![token_id, aliases, encode_base10(*balance, BALANCE_BASE10_DECIMALS)]);
+    }
 
-            table.add_row(row![
-                token_id,
-                aliases,
-                encode_base10(*balance, BALANCE_BASE10_DECIMALS)
-            ]);
-        }
+    if table.is_empty() {
+        println!("No unspent balances found");
+    } else {
+        println!("{table}");
+    }
+}
 
-        if table.is_empty() {
-            println!("No unspent balances found");
-        } else {
-            println!("{table}");
-        }
-        return
+/// Auxiliary function to define the wallet address subcommand handling.
+async fn handle_wallet_address(drk: &DrkPtr) {
+    match drk.read().await.default_address().await {
+        Ok(address) => println!("{address}"),
+        Err(e) => println!("Failed to fetch default address: {e:?}"),
     }
+}
 
-    if parts[1] == "--address" {
-        match drk.read().await.default_address().await {
-            Ok(address) => println!("{address}"),
-            Err(e) => println!("Failed to fetch default address: {e:?}"),
+/// Auxiliary function to define the wallet addresses subcommand handling.
+async fn handle_wallet_addresses(drk: &DrkPtr) {
+    let addresses = match drk.read().await.addresses().await {
+        Ok(a) => a,
+        Err(e) => {
+            println!("Failed to fetch addresses: {e:?}");
+            return
         }
-        return
-    }
+    };
 
-    if parts[1] == "--addresses" {
-        let addresses = match drk.read().await.addresses().await {
-            Ok(a) => a,
-            Err(e) => {
-                println!("Failed to fetch addresses: {e:?}");
-                return
-            }
+    // Create a prettytable with the new data:
+    let mut table = Table::new();
+    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+    table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
+    for (key_id, public_key, secret_key, is_default) in addresses {
+        let is_default = match is_default {
+            1 => "*",
+            _ => "",
         };
+        table.add_row(row![key_id, public_key, secret_key, is_default]);
+    }
 
-        // Create a prettytable with the new data:
-        let mut table = Table::new();
-        table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-        table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
-        for (key_id, public_key, secret_key, is_default) in addresses {
-            let is_default = match is_default {
-                1 => "*",
-                _ => "",
-            };
-            table.add_row(row![key_id, public_key, secret_key, is_default]);
-        }
+    if table.is_empty() {
+        println!("No addresses found");
+    } else {
+        println!("{table}");
+    }
+}
 
-        if table.is_empty() {
-            println!("No addresses found");
-        } else {
-            println!("{table}");
-        }
+/// Auxiliary function to define the wallet default address subcommand handling.
+async fn handle_wallet_default_address(drk: &DrkPtr, parts: &[&str]) {
+    if parts.len() != 3 {
+        println!("Malformed `wallet default-address` subcommand");
+        println!("Usage: wallet default-address <index>");
         return
     }
 
-    if parts[1] == "--default-address" {
-        if parts.len() != 3 {
-            println!("Malformed `wallet` command");
-            println!("Usage: wallet --default-address <address_id>");
+    let index = match usize::from_str(parts[2]) {
+        Ok(i) => i,
+        Err(e) => {
+            println!("Invalid address id: {e:?}");
             return
         }
+    };
 
-        let idx = match usize::from_str(parts[2]) {
-            Ok(i) => i,
-            Err(e) => {
-                println!("Invalid address id: {e:?}");
-                return
-            }
-        };
-
-        if let Err(e) = drk.read().await.set_default_address(idx) {
-            println!("Failed to set default address: {e:?}");
-        }
-        return
+    if let Err(e) = drk.read().await.set_default_address(index) {
+        println!("Failed to set default address: {e:?}");
     }
+}
 
-    if parts[1] == "--secrets" {
-        match drk.read().await.get_money_secrets().await {
-            Ok(secrets) => {
-                for secret in secrets {
-                    println!("{secret}");
-                }
+/// Auxiliary function to define the wallet secrets subcommand handling.
+async fn handle_wallet_secrets(drk: &DrkPtr) {
+    match drk.read().await.get_money_secrets().await {
+        Ok(secrets) => {
+            for secret in secrets {
+                println!("{secret}");
             }
-            Err(e) => println!("Failed to fetch secrets: {e:?}"),
         }
-        return
+        Err(e) => println!("Failed to fetch secrets: {e:?}"),
     }
+}
 
-    if parts[1] == "--import-secrets" {
-        let mut secrets = vec![];
-        // TODO: read from a file here not stdin
-        let lines = stdin().lines();
-        for (i, line) in lines.enumerate() {
-            if let Ok(line) = line {
-                let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
-                    println!("Warning: Failed to decode secret on line {i}");
-                    continue
-                };
-                let Ok(secret) = deserialize_async(&bytes).await else {
-                    println!("Warning: Failed to deserialize secret on line {i}");
-                    continue
-                };
-                secrets.push(secret);
-            }
+/// Auxiliary function to define the wallet import secrets subcommand handling.
+async fn handle_wallet_import_secrets(drk: &DrkPtr) {
+    let mut secrets = vec![];
+    // TODO: read from a file here not stdin
+    let lines = stdin().lines();
+    for (i, line) in lines.enumerate() {
+        if let Ok(line) = line {
+            let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
+                println!("Warning: Failed to decode secret on line {i}");
+                continue
+            };
+            let Ok(secret) = deserialize_async(&bytes).await else {
+                println!("Warning: Failed to deserialize secret on line {i}");
+                continue
+            };
+            secrets.push(secret);
         }
+    }
 
-        match drk.read().await.import_money_secrets(secrets).await {
-            Ok(pubkeys) => {
-                for key in pubkeys {
-                    println!("{key}");
-                }
+    match drk.read().await.import_money_secrets(secrets).await {
+        Ok(pubkeys) => {
+            for key in pubkeys {
+                println!("{key}");
             }
-            Err(e) => println!("Failed to import secrets: {e:?}"),
         }
-        return
+        Err(e) => println!("Failed to import secrets: {e:?}"),
     }
+}
 
-    if parts[1] == "--tree" {
-        // TODO: write to a file here not stdout
-        match drk.read().await.get_money_tree().await {
-            Ok(tree) => println!("{tree:#?}"),
-            Err(e) => println!("Failed to fetch tree: {e:?}"),
+/// Auxiliary function to define the wallet tree subcommand handling.
+async fn handle_wallet_tree(drk: &DrkPtr) {
+    // TODO: write to a file here not stdout
+    match drk.read().await.get_money_tree().await {
+        Ok(tree) => println!("{tree:#?}"),
+        Err(e) => println!("Failed to fetch tree: {e:?}"),
+    }
+}
+
+/// Auxiliary function to define the wallet coins subcommand handling.
+async fn handle_wallet_coins(drk: &DrkPtr) {
+    let lock = drk.read().await;
+    let coins = match lock.get_coins(true).await {
+        Ok(c) => c,
+        Err(e) => {
+            println!("Failed to fetch coins: {e:?}");
+            return
         }
+    };
+
+    if coins.is_empty() {
         return
     }
 
-    if parts[1] == "--coins" {
-        let lock = drk.read().await;
-        let coins = match lock.get_coins(true).await {
-            Ok(c) => c,
-            Err(e) => {
-                println!("Failed to fetch coins: {e:?}");
-                return
-            }
-        };
-
-        if coins.is_empty() {
+    let aliases_map = match lock.get_aliases_mapped_by_token().await {
+        Ok(m) => m,
+        Err(e) => {
+            println!("Failed to fetch aliases map: {e:?}");
             return
         }
+    };
 
-        let aliases_map = match lock.get_aliases_mapped_by_token().await {
-            Ok(m) => m,
-            Err(e) => {
-                println!("Failed to fetch aliases map: {e:?}");
-                return
-            }
+    let mut table = Table::new();
+    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+    table.set_titles(row![
+        "Coin",
+        "Token ID",
+        "Aliases",
+        "Value",
+        "Spend Hook",
+        "User Data",
+        "Creation Height",
+        "Spent",
+        "Spent Height",
+        "Spent TX"
+    ]);
+    for coin in coins {
+        let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
+            Some(a) => a,
+            None => "-",
         };
 
-        let mut table = Table::new();
-        table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-        table.set_titles(row![
-            "Coin",
-            "Token ID",
-            "Aliases",
-            "Value",
-            "Spend Hook",
-            "User Data",
-            "Creation Height",
-            "Spent",
-            "Spent Height",
-            "Spent TX"
-        ]);
-        for coin in coins {
-            let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
-                Some(a) => a,
-                None => "-",
-            };
-
-            let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
-                format!("{}", coin.0.note.spend_hook)
-            } else {
-                String::from("-")
-            };
-
-            let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
-                bs58::encode(&serialize_async(&coin.0.note.user_data).await)
-                    .into_string()
-                    .to_string()
-            } else {
-                String::from("-")
-            };
-
-            let spent_height = match coin.3 {
-                Some(spent_height) => spent_height.to_string(),
-                None => String::from("-"),
-            };
+        let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
+            format!("{}", coin.0.note.spend_hook)
+        } else {
+            String::from("-")
+        };
 
-            table.add_row(row![
-                bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
-                    .into_string()
-                    .to_string(),
-                coin.0.note.token_id,
-                aliases,
-                format!(
-                    "{} ({})",
-                    coin.0.note.value,
-                    encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
-                ),
-                spend_hook,
-                user_data,
-                coin.1,
-                coin.2,
-                spent_height,
-                coin.4,
-            ]);
-        }
+        let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
+            bs58::encode(&serialize_async(&coin.0.note.user_data).await).into_string().to_string()
+        } else {
+            String::from("-")
+        };
 
-        println!("{table}");
+        let spent_height = match coin.3 {
+            Some(spent_height) => spent_height.to_string(),
+            None => String::from("-"),
+        };
 
-        return
+        table.add_row(row![
+            bs58::encode(&serialize_async(&coin.0.coin.inner()).await).into_string().to_string(),
+            coin.0.note.token_id,
+            aliases,
+            format!(
+                "{} ({})",
+                coin.0.note.value,
+                encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
+            ),
+            spend_hook,
+            user_data,
+            coin.1,
+            coin.2,
+            spent_height,
+            coin.4,
+        ]);
     }
 
-    println!("Malformed `wallet` command");
-    println!("Usage: wallet --(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
+    println!("{table}");
 }
 
 /// Auxiliary function to define the spend command handling.

+ 204 - 254
bin/drk/src/main.rs

@@ -119,45 +119,9 @@ enum Subcmd {
 
     /// Wallet operations
     Wallet {
-        #[structopt(long)]
-        /// Initialize wallet database
-        initialize: bool,
-
-        #[structopt(long)]
-        /// Generate a new keypair in the wallet
-        keygen: bool,
-
-        #[structopt(long)]
-        /// Query the wallet for known balances
-        balance: bool,
-
-        #[structopt(long)]
-        /// Get the default address in the wallet
-        address: bool,
-
-        #[structopt(long)]
-        /// Print all the addresses in the wallet
-        addresses: bool,
-
-        #[structopt(long)]
-        /// Set the default address in the wallet
-        default_address: Option<usize>,
-
-        #[structopt(long)]
-        /// Print all the secret keys from the wallet
-        secrets: bool,
-
-        #[structopt(long)]
-        /// Import secret keys from stdin into the wallet, separated by newlines
-        import_secrets: bool,
-
-        #[structopt(long)]
-        /// Print the Merkle tree in the wallet
-        tree: bool,
-
-        #[structopt(long)]
-        /// Print all the coins in the wallet
-        coins: bool,
+        #[structopt(subcommand)]
+        /// Sub command to execute
+        command: WalletSubcmd,
     },
 
     /// Read a transaction from stdin and mark its input coins as spent
@@ -250,6 +214,42 @@ enum Subcmd {
     },
 }
 
+#[derive(Clone, Debug, Deserialize, StructOpt)]
+enum WalletSubcmd {
+    /// Initialize wallet database
+    Initialize,
+
+    /// Generate a new keypair in the wallet
+    Keygen,
+
+    /// Query the wallet for known balances
+    Balance,
+
+    /// Get the default address in the wallet
+    Address,
+
+    /// Print all the addresses in the wallet
+    Addresses,
+
+    /// Set the default address in the wallet
+    DefaultAddress {
+        /// Identifier of the address
+        index: usize,
+    },
+
+    /// Print all the secret keys from the wallet
+    Secrets,
+
+    /// Import secret keys from stdin into the wallet, separated by newlines
+    ImportSecrets,
+
+    /// Print the Merkle tree in the wallet
+    Tree,
+
+    /// Print all the coins in the wallet
+    Coins,
+}
+
 #[derive(Clone, Debug, Deserialize, StructOpt)]
 enum OtcSubcmd {
     /// Initialize the first half of the atomic swap
@@ -693,34 +693,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
 
         Subcmd::Completions { shell } => generate_completions(&shell),
 
-        Subcmd::Wallet {
-            initialize,
-            keygen,
-            balance,
-            address,
-            addresses,
-            default_address,
-            secrets,
-            import_secrets,
-            tree,
-            coins,
-        } => {
-            if !initialize &&
-                !keygen &&
-                !balance &&
-                !address &&
-                !addresses &&
-                default_address.is_none() &&
-                !secrets &&
-                !tree &&
-                !coins &&
-                !import_secrets
-            {
-                eprintln!("Error: You must use at least one flag for this subcommand");
-                eprintln!("Run with \"wallet -h\" to see the subcommand usage.");
-                exit(2);
-            }
-
+        Subcmd::Wallet { command } => {
             let drk = new_wallet(
                 blockchain_config.cache_path,
                 blockchain_config.wallet_path,
@@ -731,232 +704,209 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
             )
             .await;
 
-            if initialize {
-                if let Err(e) = drk.initialize_wallet().await {
-                    eprintln!("Error initializing wallet: {e:?}");
-                    exit(2);
-                }
-                if let Err(e) = drk.initialize_money().await {
-                    eprintln!("Failed to initialize Money: {e:?}");
-                    exit(2);
-                }
-                if let Err(e) = drk.initialize_dao().await {
-                    eprintln!("Failed to initialize DAO: {e:?}");
-                    exit(2);
-                }
-                if let Err(e) = drk.initialize_deployooor() {
-                    eprintln!("Failed to initialize Deployooor: {e:?}");
-                    exit(2);
+            match command {
+                WalletSubcmd::Initialize => {
+                    if let Err(e) = drk.initialize_wallet().await {
+                        eprintln!("Error initializing wallet: {e:?}");
+                        exit(2);
+                    }
+                    if let Err(e) = drk.initialize_money().await {
+                        eprintln!("Failed to initialize Money: {e:?}");
+                        exit(2);
+                    }
+                    if let Err(e) = drk.initialize_dao().await {
+                        eprintln!("Failed to initialize DAO: {e:?}");
+                        exit(2);
+                    }
+                    if let Err(e) = drk.initialize_deployooor() {
+                        eprintln!("Failed to initialize Deployooor: {e:?}");
+                        exit(2);
+                    }
                 }
-                return Ok(())
-            }
 
-            if keygen {
-                if let Err(e) = drk.money_keygen().await {
-                    eprintln!("Failed to generate keypair: {e:?}");
-                    exit(2);
+                WalletSubcmd::Keygen => {
+                    if let Err(e) = drk.money_keygen().await {
+                        eprintln!("Failed to generate keypair: {e:?}");
+                        exit(2);
+                    }
                 }
-                return Ok(())
-            }
 
-            if balance {
-                let balmap = drk.money_balance().await?;
+                WalletSubcmd::Balance => {
+                    let balmap = drk.money_balance().await?;
 
-                let aliases_map = drk.get_aliases_mapped_by_token().await?;
+                    let aliases_map = drk.get_aliases_mapped_by_token().await?;
 
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Token ID", "Aliases", "Balance"]);
-                for (token_id, balance) in balmap.iter() {
-                    let aliases = match aliases_map.get(token_id) {
-                        Some(a) => a,
-                        None => "-",
-                    };
+                    // Create a prettytable with the new data:
+                    let mut table = Table::new();
+                    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+                    table.set_titles(row!["Token ID", "Aliases", "Balance"]);
+                    for (token_id, balance) in balmap.iter() {
+                        let aliases = match aliases_map.get(token_id) {
+                            Some(a) => a,
+                            None => "-",
+                        };
 
-                    table.add_row(row![
-                        token_id,
-                        aliases,
-                        encode_base10(*balance, BALANCE_BASE10_DECIMALS)
-                    ]);
-                }
+                        table.add_row(row![
+                            token_id,
+                            aliases,
+                            encode_base10(*balance, BALANCE_BASE10_DECIMALS)
+                        ]);
+                    }
 
-                if table.is_empty() {
-                    println!("No unspent balances found");
-                } else {
-                    println!("{table}");
+                    if table.is_empty() {
+                        println!("No unspent balances found");
+                    } else {
+                        println!("{table}");
+                    }
                 }
 
-                return Ok(())
-            }
-
-            if address {
-                let address = match drk.default_address().await {
-                    Ok(a) => a,
+                WalletSubcmd::Address => match drk.default_address().await {
+                    Ok(address) => println!("{address}"),
                     Err(e) => {
                         eprintln!("Failed to fetch default address: {e:?}");
                         exit(2);
                     }
-                };
-
-                println!("{address}");
-
-                return Ok(())
-            }
+                },
 
-            if addresses {
-                let addresses = drk.addresses().await?;
+                WalletSubcmd::Addresses => {
+                    let addresses = drk.addresses().await?;
+
+                    // Create a prettytable with the new data:
+                    let mut table = Table::new();
+                    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+                    table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
+                    for (key_id, public_key, secret_key, is_default) in addresses {
+                        let is_default = match is_default {
+                            1 => "*",
+                            _ => "",
+                        };
+                        table.add_row(row![key_id, public_key, secret_key, is_default]);
+                    }
 
-                // Create a prettytable with the new data:
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
-                for (key_id, public_key, secret_key, is_default) in addresses {
-                    let is_default = match is_default {
-                        1 => "*",
-                        _ => "",
-                    };
-                    table.add_row(row![key_id, public_key, secret_key, is_default]);
+                    if table.is_empty() {
+                        println!("No addresses found");
+                    } else {
+                        println!("{table}");
+                    }
                 }
 
-                if table.is_empty() {
-                    println!("No addresses found");
-                } else {
-                    println!("{table}");
+                WalletSubcmd::DefaultAddress { index } => {
+                    if let Err(e) = drk.set_default_address(index) {
+                        eprintln!("Failed to set default address: {e:?}");
+                        exit(2);
+                    }
                 }
 
-                return Ok(())
-            }
-
-            if let Some(idx) = default_address {
-                if let Err(e) = drk.set_default_address(idx) {
-                    eprintln!("Failed to set default address: {e:?}");
-                    exit(2);
+                WalletSubcmd::Secrets => {
+                    for secret in drk.get_money_secrets().await? {
+                        println!("{secret}");
+                    }
                 }
-                return Ok(())
-            }
 
-            if secrets {
-                let v = drk.get_money_secrets().await?;
-
-                for i in v {
-                    println!("{i}");
-                }
+                WalletSubcmd::ImportSecrets => {
+                    let mut secrets = vec![];
+                    let lines = stdin().lines();
+                    for (i, line) in lines.enumerate() {
+                        if let Ok(line) = line {
+                            let bytes = bs58::decode(&line.trim()).into_vec()?;
+                            let Ok(secret) = deserialize_async(&bytes).await else {
+                                println!("Warning: Failed to deserialize secret on line {i}");
+                                continue
+                            };
+                            secrets.push(secret);
+                        }
+                    }
 
-                return Ok(())
-            }
+                    let pubkeys = match drk.import_money_secrets(secrets).await {
+                        Ok(p) => p,
+                        Err(e) => {
+                            eprintln!("Failed to import secret keys into wallet: {e:?}");
+                            exit(2);
+                        }
+                    };
 
-            if import_secrets {
-                let mut secrets = vec![];
-                let lines = stdin().lines();
-                for (i, line) in lines.enumerate() {
-                    if let Ok(line) = line {
-                        let bytes = bs58::decode(&line.trim()).into_vec()?;
-                        let Ok(secret) = deserialize_async(&bytes).await else {
-                            println!("Warning: Failed to deserialize secret on line {i}");
-                            continue
-                        };
-                        secrets.push(secret);
+                    for key in pubkeys {
+                        println!("{key}");
                     }
                 }
 
-                let pubkeys = match drk.import_money_secrets(secrets).await {
-                    Ok(p) => p,
-                    Err(e) => {
-                        eprintln!("Failed to import secret keys into wallet: {e:?}");
-                        exit(2);
-                    }
-                };
-
-                for key in pubkeys {
-                    println!("{key}");
+                WalletSubcmd::Tree => {
+                    println!("{:#?}", drk.get_money_tree().await?);
                 }
 
-                return Ok(())
-            }
-
-            if tree {
-                let tree = drk.get_money_tree().await?;
-
-                println!("{tree:#?}");
-
-                return Ok(())
-            }
+                WalletSubcmd::Coins => {
+                    let coins = drk.get_coins(true).await?;
 
-            if coins {
-                let coins = drk.get_coins(true).await?;
-
-                if coins.is_empty() {
-                    return Ok(())
-                }
+                    if coins.is_empty() {
+                        return Ok(())
+                    }
 
-                let aliases_map = drk.get_aliases_mapped_by_token().await?;
+                    let aliases_map = drk.get_aliases_mapped_by_token().await?;
+
+                    let mut table = Table::new();
+                    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+                    table.set_titles(row![
+                        "Coin",
+                        "Token ID",
+                        "Aliases",
+                        "Value",
+                        "Spend Hook",
+                        "User Data",
+                        "Creation Height",
+                        "Spent",
+                        "Spent Height",
+                        "Spent TX"
+                    ]);
+                    for coin in coins {
+                        let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
+                            Some(a) => a,
+                            None => "-",
+                        };
 
-                let mut table = Table::new();
-                table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
-                table.set_titles(row![
-                    "Coin",
-                    "Token ID",
-                    "Aliases",
-                    "Value",
-                    "Spend Hook",
-                    "User Data",
-                    "Creation Height",
-                    "Spent",
-                    "Spent Height",
-                    "Spent TX"
-                ]);
-                for coin in coins {
-                    let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
-                        Some(a) => a,
-                        None => "-",
-                    };
+                        let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
+                            format!("{}", coin.0.note.spend_hook)
+                        } else {
+                            String::from("-")
+                        };
 
-                    let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
-                        format!("{}", coin.0.note.spend_hook)
-                    } else {
-                        String::from("-")
-                    };
+                        let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
+                            bs58::encode(&serialize_async(&coin.0.note.user_data).await)
+                                .into_string()
+                                .to_string()
+                        } else {
+                            String::from("-")
+                        };
 
-                    let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
-                        bs58::encode(&serialize_async(&coin.0.note.user_data).await)
-                            .into_string()
-                            .to_string()
-                    } else {
-                        String::from("-")
-                    };
+                        let spent_height = match coin.3 {
+                            Some(spent_height) => spent_height.to_string(),
+                            None => String::from("-"),
+                        };
 
-                    let spent_height = match coin.3 {
-                        Some(spent_height) => spent_height.to_string(),
-                        None => String::from("-"),
-                    };
+                        table.add_row(row![
+                            bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
+                                .into_string()
+                                .to_string(),
+                            coin.0.note.token_id,
+                            aliases,
+                            format!(
+                                "{} ({})",
+                                coin.0.note.value,
+                                encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
+                            ),
+                            spend_hook,
+                            user_data,
+                            coin.1,
+                            coin.2,
+                            spent_height,
+                            coin.4,
+                        ]);
+                    }
 
-                    table.add_row(row![
-                        bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
-                            .into_string()
-                            .to_string(),
-                        coin.0.note.token_id,
-                        aliases,
-                        format!(
-                            "{} ({})",
-                            coin.0.note.value,
-                            encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
-                        ),
-                        spend_hook,
-                        user_data,
-                        coin.1,
-                        coin.2,
-                        spent_height,
-                        coin.4,
-                    ]);
+                    println!("{table}");
                 }
-
-                println!("{table}");
-
-                return Ok(())
             }
 
-            unreachable!()
+            Ok(())
         }
 
         Subcmd::Spend => {

+ 6 - 6
contrib/localnet/darkfid-single-node/README.md

@@ -37,13 +37,13 @@ of the guide can be added for future regressions.
 
 | #  | Description                  | Command                                                         | Status |
 |----|------------------------------|-----------------------------------------------------------------|--------|
-| 0  | Initialization               | wallet --initialize                                             | Pass   |
-| 1  | Key generation               | wallet --keygen                                                 | Pass   |
-| 2  | Set default wallet           | wallet --default-address {ADDR_ID}                              | Pass   |
-| 3  | Default address retrieval    | wallet --address                                                | Pass   |
+| 0  | Initialization               | wallet initialize                                               | Pass   |
+| 1  | Key generation               | wallet keygen                                                   | Pass   |
+| 2  | Set default wallet           | wallet default-address {ADDR_ID}                                | Pass   |
+| 3  | Default address retrieval    | wallet address                                                  | Pass   |
 | 4  | Block scanning               | scan                                                            | Pass   |
 | 5  | Block subscribing            | subscribe                                                       | Pass   |
-| 6  | Balance retrieval            | wallet --balance                                                | Pass   |
+| 6  | Balance retrieval            | wallet balance                                                  | Pass   |
 | 7  | Aliases retrieval            | alias show                                                      | Pass   |
 | 8  | Mint auth generation         | token generate-mint                                             | Pass   |
 | 9  | Mint auths retrieval         | token list                                                      | Pass   |
@@ -52,7 +52,7 @@ of the guide can be added for future regressions.
 | 12 | Mint generation              | token mint {ALIAS} {AMOUNT} {ADDR}                              | Pass   |
 | 13 | Token freeze                 | token freeze {ALIAS}                                            | Pass   |
 | 14 | Transfer                     | transfer {AMOUNT} {ALIAS} {ADDR}                                | Pass   |
-| 15 | Coins retrieval              | wallet --coins                                                  | Pass   |
+| 15 | Coins retrieval              | wallet coins                                                    | Pass   |
 | 16 | OTC initialization           | otc init -v {AMOUNT}:{AMOUNT} -t {ALIAS}:{ALIAS}                | Pass   |
 | 17 | OTC join                     | otc join                                                        | Pass   |
 | 18 | OTC sign                     | otc sign                                                        | Pass   |

+ 4 - 4
contrib/localnet/darkfid-single-node/init-wallet.sh

@@ -3,8 +3,8 @@
 # Path to `drk` binary
 DRK="../../../drk -c drk.toml"
 
-$DRK wallet --initialize
-$DRK wallet --keygen
-$DRK wallet --default-address 1
-wallet=$($DRK wallet --address)
+$DRK wallet initialize
+$DRK wallet keygen
+$DRK wallet default-address 1
+wallet=$($DRK wallet address)
 sed -i -e "s|9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U|$wallet|g" darkfid.toml

+ 3 - 3
contrib/localnet/darkfid-single-node/run-dao-test.sh

@@ -28,7 +28,7 @@ mint_tokens() {
     $DRK alias add WCKD "$TOKEN_ID1"
     $DRK alias add MLDY "$TOKEN_ID2"
 
-    ADDR="$($DRK wallet --address)"
+    ADDR="$($DRK wallet address)"
 
     $DRK token mint WCKD 42 "$ADDR" | tee /tmp/mint-wkcd.tx | $DRK broadcast
     $DRK token mint MLDY 20 "$ADDR" | tee /tmp/mint-mldy.tx | $DRK broadcast
@@ -37,7 +37,7 @@ mint_tokens() {
 }
 
 token_balance() {
-    BALANCE="$($DRK wallet --balance 2>/dev/null)"
+    BALANCE="$($DRK wallet balance 2>/dev/null)"
 
     # No tokens received at all yet
     if echo "$BALANCE" | grep -q "No unspent balances found"; then
@@ -110,7 +110,7 @@ wait_dao_treasury() {
 }
 
 propose() {
-    MY_ADDR=$($DRK wallet --address)
+    MY_ADDR=$($DRK wallet address)
     PROPOSAL="$($DRK dao propose-transfer MiladyMakerDAO 1 5 WCKD "$MY_ADDR" | cut -d' ' -f3)"
     $DRK dao proposal "$PROPOSAL" --mint-proposal > /tmp/propose.tx
     $DRK broadcast < /tmp/propose.tx

+ 1 - 1
contrib/localnet/darkfid-single-node/wallet-balance.sh

@@ -11,4 +11,4 @@ while true; do
 done
 
 $DRK scan
-$DRK wallet --balance
+$DRK wallet balance

+ 3 - 3
doc/src/testnet/atomic-swap.md

@@ -14,7 +14,7 @@ with. Note that the coin overview might look very different depending
 on your activity:
 
 ```shell
-$ ./drk wallet --coins
+$ ./drk wallet coins
 
  Coin            | Spent | Token ID        | Aliases | Value                    | Spend Hook | User Data | Spent TX
 -----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------
@@ -88,7 +88,7 @@ also be in the mempool, so you should wait again until it's confirmed.
 After a while you should see the change in balances in your wallet:
 
 ```shell
-$ ./drk wallet --balance
+$ ./drk wallet balance
 
  Token ID                                     | Aliases | Balance
 ----------------------------------------------+---------+-------------
@@ -102,7 +102,7 @@ unchanged. We can confirm it actually happened successfully by checking
 our coins:
 
 ```shell
-$ ./drk wallet --coins
+$ ./drk wallet coins
 
  Coin            | Spent | Token ID        | Aliases | Value                    | Spend Hook | User Data | Spent TX
 -----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------

+ 2 - 2
doc/src/testnet/dao.md

@@ -264,7 +264,7 @@ Now that the DAO has something in its treasury, we can generate a
 transfer proposal to send it somewhere, that will be up to vote
 for 1 block period, if we hold the DAO proposer key. Let's propose
 to send 5 of the 10 tokens to our address (we can find that with
-`drk wallet --address`):
+`drk wallet address`):
 
 ```shell
 $ ./drk dao propose-transfer AnonDAO 1 5 DAWN {YOUR_ADDRESS}
@@ -511,7 +511,7 @@ $ ./drk dao balance AnonDAO
 ```
 
 ```shell
-$ ./drk wallet --balance
+$ ./drk wallet balance
 
  Token ID                                     | Aliases | Balance
 ----------------------------------------------+---------+-------------

+ 7 - 7
doc/src/testnet/node.md

@@ -108,7 +108,7 @@ Config file created in "~/.config/darkfi/darkfid_config.toml". Please review it
 ```
 
 ```shell
-$ ./drk wallet --address
+$ ./drk wallet address
 
 Config file created in "~/.config/darkfi/drk_config.toml". Please review it and try again.
 ```
@@ -150,7 +150,7 @@ initialize a wallet, and create a keypair. The wallet address shown in
 the outputs is examplatory and will different from the one you get.
 
 ```shell
-$ ./drk wallet --initialize
+$ ./drk wallet initialize
 
 Initializing Money Merkle tree
 Successfully initialized Merkle tree for the Money contract
@@ -160,7 +160,7 @@ Successfully initialized Merkle trees for the DAO contract
 ```
 
 ```shell
-$ ./drk wallet --keygen
+$ ./drk wallet keygen
 
 Generating a new keypair
 New address:
@@ -168,7 +168,7 @@ CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
 ```
 
 ```shell
-$ ./drk wallet --default-address 1
+$ ./drk wallet default-address 1
 ```
 
 The second command will print out your new DarkFi address where you
@@ -176,7 +176,7 @@ can receive payments. Take note of it. Alternatively, you can always
 retrieve your default address using:
 
 ```shell
-$ ./drk wallet --address
+$ ./drk wallet address
 
 CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
 ```
@@ -252,7 +252,7 @@ like this:
 ```toml
 # Put your `minerd` endpoint here (default for testnet is in this example)
 minerd_endpoint = "tcp://127.0.0.1:28467"
-# Put the address from `drk wallet --address` here
+# Put the address from `drk wallet address` here
 recipient = "YOUR_WALLET_ADDRESS_HERE"
 ```
 
@@ -263,7 +263,7 @@ is `~/.config/darkfi/minerd_config.toml`). Finally, replace the
 you can retrieve as follows:
 
 ```shell
-$ ./drk wallet --address
+$ ./drk wallet address
 
 CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
 ```

+ 2 - 2
doc/src/testnet/payment.md

@@ -32,7 +32,7 @@ will receive the tokens you've sent.
 We can see the spent coin in our wallet.
 
 ```shell
-$ ./drk wallet --coins
+$ ./drk wallet coins
 
  Coin            | Spent | Token ID        | Aliases | Value                    | Spend Hook | User Data | Spent TX
 -----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------
@@ -47,7 +47,7 @@ We have to wait until the next block to see our change balance reappear
 in our wallet.
 
 ```shell
-$ ./drk wallet --balance
+$ ./drk wallet balance
 
  Token ID                                     | Aliases | Balance
 ----------------------------------------------+---------+-------------

+ 3 - 3
doc/src/testnet/token.md

@@ -20,7 +20,7 @@ then after some time your new balance should be in your wallet.
 You can check your wallet balance using `drk`:
 
 ```shell
-$ ./drk wallet --balance
+$ ./drk wallet balance
 
  Token ID                                     | Aliases | Balance
 ----------------------------------------------+---------+---------
@@ -130,7 +130,7 @@ Now let's mint some tokens for ourselves. First grab your wallet address,
 and then create the token mint transaction, and finally - broadcast it:
 
 ```shell
-$ ./drk wallet --address
+$ ./drk wallet address
 
 {YOUR_ADDRESS}
 ```
@@ -171,7 +171,7 @@ then when the transaction is confirmed, your wallet should have your
 new tokens listed when you run:
 
 ```shell
-$ ./drk wallet --balance
+$ ./drk wallet balance
 
  Token ID                                     | Aliases | Balance
 ----------------------------------------------+---------+-------------