From 2263b9778a0a112eae4bea78b13b2d4a27047784 Mon Sep 17 00:00:00 2001 From: Tomas Fabrizio Orsi Date: Fri, 8 Aug 2025 14:22:22 -0300 Subject: [PATCH 1/6] feat: add `multicall` support for the CLI Signed-off-by: Tomas Fabrizio Orsi --- CHANGELOG.md | 3 +- bin/miden-cli/src/commands/account.rs | 7 ++- bin/miden-cli/src/commands/new_account.rs | 8 ++- bin/miden-cli/src/errors.rs | 7 ++- bin/miden-cli/src/lib.rs | 70 +++++++++++++++++++++-- bin/miden-cli/src/main.rs | 11 ++-- 6 files changed, 89 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d079256f..c723e13aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,8 @@ * [BREAKING] Added genesis commitment header to `TonicRpcClient` requests (#1045). * Added authentication arguments support to `TransactionRequest` ([#1121](https://github.com/0xMiden/miden-client/pull/1121)). * Added bindings for retrieving storage `AccountDelta` in the web client ([#1098](https://github.com/0xMiden/miden-client/pull/1098)). - +* Added `multicall` support for the CLI (#TBD) + ## 0.10.1 (2025-07-26) * Avoid passing unneeded nodes to `PartialMmr::from_parts` (#1081). diff --git a/bin/miden-cli/src/commands/account.rs b/bin/miden-cli/src/commands/account.rs index 8285c4425..ecffff190 100644 --- a/bin/miden-cli/src/commands/account.rs +++ b/bin/miden-cli/src/commands/account.rs @@ -9,7 +9,7 @@ use miden_objects::PrettyPrint; use crate::config::CliConfig; use crate::errors::CliError; use crate::utils::{load_config_file, load_faucet_details_map, parse_account_id, update_config}; -use crate::{CLIENT_BINARY_NAME, create_dynamic_table}; +use crate::{client_binary_name, create_dynamic_table}; // ACCOUNT COMMAND // ================================================================================================ @@ -319,7 +319,10 @@ pub(crate) fn maybe_set_default_account( let account_id = account_id.to_bech32(current_config.rpc.endpoint.0.to_network_id()?); println!("Setting account {account_id} as the default account ID."); - println!("You can unset it with `{CLIENT_BINARY_NAME} account --default none`."); + println!( + "You can unset it with `{} account --default none`.", + client_binary_name().display() + ); current_config.default_account_id = Some(account_id); Ok(()) diff --git a/bin/miden-cli/src/commands/new_account.rs b/bin/miden-cli/src/commands/new_account.rs index f56eaf303..35e0278f7 100644 --- a/bin/miden-cli/src/commands/new_account.rs +++ b/bin/miden-cli/src/commands/new_account.rs @@ -25,7 +25,7 @@ use tracing::debug; use crate::commands::account::maybe_set_default_account; use crate::errors::CliError; use crate::utils::load_config_file; -use crate::{CLIENT_BINARY_NAME, CliKeyStore}; +use crate::{CliKeyStore, client_binary_name}; // CLI TYPES // ================================================================================================ @@ -130,7 +130,8 @@ impl NewWalletCmd { println!("Successfully created new wallet."); println!( - "To view account details execute {CLIENT_BINARY_NAME} account -s {account_address}", + "To view account details execute {} account -s {account_address}", + client_binary_name().display() ); maybe_set_default_account(&mut current_config, new_account.id())?; @@ -193,7 +194,8 @@ impl NewAccountCmd { println!("Successfully created new account."); println!( - "To view account details execute {CLIENT_BINARY_NAME} account -s {account_address}" + "To view account details execute {} account -s {account_address}", + client_binary_name().display() ); Ok(()) diff --git a/bin/miden-cli/src/errors.rs b/bin/miden-cli/src/errors.rs index 869100b19..a4dfed756 100644 --- a/bin/miden-cli/src/errors.rs +++ b/bin/miden-cli/src/errors.rs @@ -7,8 +7,7 @@ use miden_objects::{AccountError, AccountIdError, AssetError, NetworkIdError}; use miette::Diagnostic; use thiserror::Error; -use crate::CLIENT_BINARY_NAME; - +use crate::client_binary_name; type SourceError = Box; #[derive(Debug, Diagnostic, Error)] @@ -32,7 +31,9 @@ pub enum CliError { #[diagnostic( code(cli::config_error), help( - "Check if the configuration file exists and is well-formed. If it does not exist, run `{CLIENT_BINARY_NAME} init` command to create it." + "Check if the configuration file exists and is well-formed. If it does not exist, run `{} init` command to create it.", + client_binary_name().display() + ) )] Config(#[source] SourceError, String), diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index 7d25ff973..e2a983da0 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -1,7 +1,8 @@ use std::env; +use std::ffi::OsString; use std::sync::Arc; -use clap::Parser; +use clap::{Parser, Subcommand}; use comfy_table::{Attribute, Cell, ContentArrangement, Table, presets}; use errors::CliError; use miden_client::account::AccountHeader; @@ -38,7 +39,24 @@ mod utils; const CLIENT_CONFIG_FILE_NAME: &str = "miden-client.toml"; /// Client binary name. -pub const CLIENT_BINARY_NAME: &str = "miden-client"; +/// Note: If, for whatever reason, we fail to obtain the client's executable +/// name, then we simply display the standard "miden-client". +pub fn client_binary_name() -> OsString { + std::env::current_exe() + .map(|executable| { + executable + .file_name() + .expect("ERROR: failed to obtain the executable's file name") + .to_os_string() + }) + .inspect_err(|e| { + eprintln!( + "WARNING: Couldn't obtain the name of the current executable because of {e}.\ + Defaulting to miden-client." + ); + }) + .unwrap_or(OsString::from("miden-client")) +} /// Number of blocks that must elapse after a transaction’s reference block before it is marked /// stale and discarded. @@ -52,14 +70,53 @@ const TX_GRACEFUL_BLOCK_DELTA: u32 = 20; version, rename_all = "kebab-case" )] -pub struct Cli { +#[command(multicall(true))] +pub struct MidenClientCli { #[command(subcommand)] - action: Command, + behavior: Behavior, +} + +impl From for Cli { + fn from(value: MidenClientCli) -> Self { + match value.behavior { + Behavior::MidenClient { cli } => cli, + Behavior::External(args) => Cli::parse_from(args).set_external(), + } + } +} +#[derive(Debug, Subcommand)] +#[command(rename_all = "kebab-case")] +enum Behavior { + /// The Miden toolchain installer + MidenClient { + #[command(flatten)] + cli: Cli, + }, + + /// Used when the Miden Client CLI is called under a different name, like it + /// is the case in [Midenup](https://github.com/0xMiden/midenup). + #[command(external_subcommand)] + External(Vec), +} + +#[derive(Parser, Debug)] +#[command(name = "miden-client")] +pub struct Cli { /// Activates the executor's debug mode, which enables debug output for scripts /// that were compiled and executed with this mode. #[arg(short, long, default_value_t = false)] debug: bool, + + #[command(subcommand)] + action: Command, + + /// Indicates whether the client's CLI is being called directly, or + /// externally under an alias (like in the case of + /// [Midenup](https://github.com/0xMiden/midenup). + #[arg(skip)] + #[allow(unused)] + external: bool, } /// CLI actions. @@ -150,6 +207,11 @@ impl Cli { Command::ConsumeNotes(consume_notes) => consume_notes.execute(client).await, } } + + fn set_external(mut self) -> Self { + self.external = true; + self + } } pub fn create_dynamic_table(headers: &[&str]) -> Table { diff --git a/bin/miden-cli/src/main.rs b/bin/miden-cli/src/main.rs index c7b7fda67..27ab8440c 100644 --- a/bin/miden-cli/src/main.rs +++ b/bin/miden-cli/src/main.rs @@ -1,14 +1,17 @@ -use miden_client_cli::Cli; +use clap::FromArgMatches; +use miden_client_cli::{Cli, MidenClientCli}; extern crate std; #[tokio::main] async fn main() -> miette::Result<()> { - use clap::Parser; - tracing_subscriber::fmt::init(); + // read command-line args - let cli = Cli::parse(); + let input = ::command(); + let matches = input.get_matches(); + let parsed = MidenClientCli::from_arg_matches(&matches).unwrap_or_else(|err| err.exit()); + let cli: Cli = parsed.into(); // execute cli action Ok(cli.execute().await?) From f49b5c4c16aa98c51eb5a55c080b158bcc7c7a1e Mon Sep 17 00:00:00 2001 From: Tomas Fabrizio Orsi Date: Thu, 7 Aug 2025 18:57:08 -0300 Subject: [PATCH 2/6] refactor: remove expect and leave only a single unwrap Suggested-by: Francisco Krause Arnim Signed-off-by: Tomas Fabrizio Orsi --- bin/miden-cli/src/lib.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index e2a983da0..4fc2238e4 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -43,18 +43,17 @@ const CLIENT_CONFIG_FILE_NAME: &str = "miden-client.toml"; /// name, then we simply display the standard "miden-client". pub fn client_binary_name() -> OsString { std::env::current_exe() - .map(|executable| { - executable - .file_name() - .expect("ERROR: failed to obtain the executable's file name") - .to_os_string() - }) .inspect_err(|e| { eprintln!( - "WARNING: Couldn't obtain the name of the current executable because of {e}.\ + "WARNING: Couldn't obtain the path of the current executable because of {e}.\ Defaulting to miden-client." ); }) + .and_then(|executable_path| { + executable_path.file_name().map(std::ffi::OsStr::to_os_string).ok_or( + std::io::Error::other("Couldn't obtain the file name of the current executable"), + ) + }) .unwrap_or(OsString::from("miden-client")) } From ff781bc4e0e40c0adc4bb3f3adae23e253593c8f Mon Sep 17 00:00:00 2001 From: Tomas Fabrizio Orsi Date: Mon, 11 Aug 2025 10:56:37 -0300 Subject: [PATCH 3/6] chore: update Miden Client CLI comment. Signed-off-by: Tomas Fabrizio Orsi --- bin/miden-cli/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index 4fc2238e4..543e0ca25 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -87,7 +87,7 @@ impl From for Cli { #[derive(Debug, Subcommand)] #[command(rename_all = "kebab-case")] enum Behavior { - /// The Miden toolchain installer + /// The Miden Client CLI. MidenClient { #[command(flatten)] cli: Cli, From 0d82f515aa66ecda1e11405bf0b60e9dff49a88b Mon Sep 17 00:00:00 2001 From: Tomas Fabrizio Orsi Date: Mon, 11 Aug 2025 11:04:36 -0300 Subject: [PATCH 4/6] chore: elaborate on what Vec holds. Signed-off-by: Tomas Fabrizio Orsi --- bin/miden-cli/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index 543e0ca25..b0a970c11 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -93,8 +93,10 @@ enum Behavior { cli: Cli, }, - /// Used when the Miden Client CLI is called under a different name, like it - /// is the case in [Midenup](https://github.com/0xMiden/midenup). + /// Used when the Miden Client CLI is called under a different name, like + /// when it is called from [Midenup](https://github.com/0xMiden/midenup). + /// NOTE: Vec holds the "raw" arguments passed to the command + /// line, analogous to `argv`. #[command(external_subcommand)] External(Vec), } From d8f1c749fc9d754762864721b5fce3f4522bcad5 Mon Sep 17 00:00:00 2001 From: Tomas Fabrizio Orsi Date: Mon, 11 Aug 2025 11:06:01 -0300 Subject: [PATCH 5/6] chore: update Changelog entry with PR number Signed-off-by: Tomas Fabrizio Orsi --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c723e13aa..4bb807b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ * [BREAKING] Added genesis commitment header to `TonicRpcClient` requests (#1045). * Added authentication arguments support to `TransactionRequest` ([#1121](https://github.com/0xMiden/miden-client/pull/1121)). * Added bindings for retrieving storage `AccountDelta` in the web client ([#1098](https://github.com/0xMiden/miden-client/pull/1098)). -* Added `multicall` support for the CLI (#TBD) +* Added `multicall` support for the CLI ([#1141](https://github.com/0xMiden/miden-client/pull/1141)) ## 0.10.1 (2025-07-26) From 727530cb5aaa8e46503724e2c78d10f42bdfe7fb Mon Sep 17 00:00:00 2001 From: igamigo Date: Mon, 11 Aug 2025 15:11:09 -0300 Subject: [PATCH 6/6] review: sytle-change doc-comments Signed-off-by: Tomas Fabrizio Orsi --- bin/miden-cli/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/miden-cli/src/lib.rs b/bin/miden-cli/src/lib.rs index b0a970c11..f18429079 100644 --- a/bin/miden-cli/src/lib.rs +++ b/bin/miden-cli/src/lib.rs @@ -39,8 +39,9 @@ mod utils; const CLIENT_CONFIG_FILE_NAME: &str = "miden-client.toml"; /// Client binary name. -/// Note: If, for whatever reason, we fail to obtain the client's executable -/// name, then we simply display the standard "miden-client". +/// +/// If, for whatever reason, we fail to obtain the client's executable name, +/// then we simply display the standard "miden-client". pub fn client_binary_name() -> OsString { std::env::current_exe() .inspect_err(|e| { @@ -95,8 +96,8 @@ enum Behavior { /// Used when the Miden Client CLI is called under a different name, like /// when it is called from [Midenup](https://github.com/0xMiden/midenup). - /// NOTE: Vec holds the "raw" arguments passed to the command - /// line, analogous to `argv`. + /// Vec holds the "raw" arguments passed to the command line, + /// analogous to `argv`. #[command(external_subcommand)] External(Vec), }