Skip to content

Commit 3f03515

Browse files
feat: add multicall support for the CLI
Signed-off-by: Tomas Fabrizio Orsi <tomas.orsi@lambdaclass.com>
1 parent 23a64aa commit 3f03515

File tree

6 files changed

+89
-18
lines changed

6 files changed

+89
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
* [BREAKING] Added genesis commitment header to `TonicRpcClient` requests (#1045).
2727
* Added authentication arguments support to `TransactionRequest` ([#1121](https://github.com/0xMiden/miden-client/pull/1121)).
2828
* Added bindings for retrieving storage `AccountDelta` in the web client ([#1098](https://github.com/0xMiden/miden-client/pull/1098)).
29-
29+
* Added `multicall` support for the CLI (#TBD)
30+
3031
## 0.10.1 (2025-07-26)
3132

3233
* Avoid passing unneeded nodes to `PartialMmr::from_parts` (#1081).

bin/miden-cli/src/commands/account.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use miden_client::{
99
use miden_objects::PrettyPrint;
1010

1111
use crate::{
12-
CLIENT_BINARY_NAME,
12+
client_binary_name,
1313
config::CliConfig,
1414
create_dynamic_table,
1515
errors::CliError,
@@ -324,7 +324,10 @@ pub(crate) fn maybe_set_default_account(
324324

325325
let account_id = account_id.to_bech32(current_config.rpc.endpoint.0.to_network_id()?);
326326
println!("Setting account {account_id} as the default account ID.");
327-
println!("You can unset it with `{CLIENT_BINARY_NAME} account --default none`.");
327+
println!(
328+
"You can unset it with `{} account --default none`.",
329+
client_binary_name().display()
330+
);
328331
current_config.default_account_id = Some(account_id);
329332

330333
Ok(())

bin/miden-cli/src/commands/new_account.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rand::RngCore;
2626
use tracing::debug;
2727

2828
use crate::{
29-
CLIENT_BINARY_NAME, CliKeyStore, commands::account::maybe_set_default_account,
29+
CliKeyStore, client_binary_name, commands::account::maybe_set_default_account,
3030
errors::CliError, utils::load_config_file,
3131
};
3232

@@ -133,7 +133,8 @@ impl NewWalletCmd {
133133

134134
println!("Successfully created new wallet.");
135135
println!(
136-
"To view account details execute {CLIENT_BINARY_NAME} account -s {account_address}",
136+
"To view account details execute {} account -s {account_address}",
137+
client_binary_name().display()
137138
);
138139

139140
maybe_set_default_account(&mut current_config, new_account.id())?;
@@ -196,7 +197,8 @@ impl NewAccountCmd {
196197

197198
println!("Successfully created new account.");
198199
println!(
199-
"To view account details execute {CLIENT_BINARY_NAME} account -s {account_address}"
200+
"To view account details execute {} account -s {account_address}",
201+
client_binary_name().display()
200202
);
201203

202204
Ok(())

bin/miden-cli/src/errors.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use miden_objects::{AccountError, AccountIdError, AssetError, NetworkIdError};
66
use miette::Diagnostic;
77
use thiserror::Error;
88

9-
use crate::CLIENT_BINARY_NAME;
10-
9+
use crate::client_binary_name;
1110
type SourceError = Box<dyn Error + Send + Sync>;
1211

1312
#[derive(Debug, Diagnostic, Error)]
@@ -31,7 +30,9 @@ pub enum CliError {
3130
#[diagnostic(
3231
code(cli::config_error),
3332
help(
34-
"Check if the configuration file exists and is well-formed. If it does not exist, run `{CLIENT_BINARY_NAME} init` command to create it."
33+
"Check if the configuration file exists and is well-formed. If it does not exist, run `{} init` command to create it.",
34+
client_binary_name().display()
35+
3536
)
3637
)]
3738
Config(#[source] SourceError, String),

bin/miden-cli/src/lib.rs

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::{env, sync::Arc};
1+
use std::{env, ffi::OsString, sync::Arc};
22

3-
use clap::Parser;
3+
use clap::{Parser, Subcommand};
44
use comfy_table::{Attribute, Cell, ContentArrangement, Table, presets};
55
use errors::CliError;
66
use miden_client::{
@@ -41,7 +41,24 @@ mod utils;
4141
const CLIENT_CONFIG_FILE_NAME: &str = "miden-client.toml";
4242

4343
/// Client binary name.
44-
pub const CLIENT_BINARY_NAME: &str = "miden-client";
44+
/// Note: If, for whatever reason, we fail to obtain the client's executable
45+
/// name, then we simply display the standard "miden-client".
46+
pub fn client_binary_name() -> OsString {
47+
std::env::current_exe()
48+
.map(|executable| {
49+
executable
50+
.file_name()
51+
.expect("ERROR: failed to obtain the executable's file name")
52+
.to_os_string()
53+
})
54+
.inspect_err(|e| {
55+
eprintln!(
56+
"WARNING: Couldn't obtain the name of the current executable because of {e}.\
57+
Defaulting to miden-client."
58+
);
59+
})
60+
.unwrap_or(OsString::from("miden-client"))
61+
}
4562

4663
/// Number of blocks that must elapse after a transaction’s reference block before it is marked
4764
/// stale and discarded.
@@ -55,14 +72,53 @@ const TX_GRACEFUL_BLOCK_DELTA: u32 = 20;
5572
version,
5673
rename_all = "kebab-case"
5774
)]
58-
pub struct Cli {
75+
#[command(multicall(true))]
76+
pub struct MidenClientCli {
5977
#[command(subcommand)]
60-
action: Command,
78+
behavior: Behavior,
79+
}
80+
81+
impl From<MidenClientCli> for Cli {
82+
fn from(value: MidenClientCli) -> Self {
83+
match value.behavior {
84+
Behavior::MidenClient { cli } => cli,
85+
Behavior::External(args) => Cli::parse_from(args).set_external(),
86+
}
87+
}
88+
}
6189

90+
#[derive(Debug, Subcommand)]
91+
#[command(rename_all = "kebab-case")]
92+
enum Behavior {
93+
/// The Miden toolchain installer
94+
MidenClient {
95+
#[command(flatten)]
96+
cli: Cli,
97+
},
98+
99+
/// Used when the Miden Client CLI is called under a different name, like it
100+
/// is the case in [Midenup](https://github.com/0xMiden/midenup).
101+
#[command(external_subcommand)]
102+
External(Vec<OsString>),
103+
}
104+
105+
#[derive(Parser, Debug)]
106+
#[command(name = "miden-client")]
107+
pub struct Cli {
62108
/// Activates the executor's debug mode, which enables debug output for scripts
63109
/// that were compiled and executed with this mode.
64110
#[arg(short, long, default_value_t = false)]
65111
debug: bool,
112+
113+
#[command(subcommand)]
114+
action: Command,
115+
116+
/// Indicates whether the client's CLI is being called directly, or
117+
/// externally under an alias (like in the case of
118+
/// [Midenup](https://github.com/0xMiden/midenup).
119+
#[arg(skip)]
120+
#[allow(unused)]
121+
external: bool,
66122
}
67123

68124
/// CLI actions.
@@ -153,6 +209,11 @@ impl Cli {
153209
Command::ConsumeNotes(consume_notes) => consume_notes.execute(client).await,
154210
}
155211
}
212+
213+
fn set_external(mut self) -> Self {
214+
self.external = true;
215+
self
216+
}
156217
}
157218

158219
pub fn create_dynamic_table(headers: &[&str]) -> Table {

bin/miden-cli/src/main.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
use miden_client_cli::Cli;
1+
use clap::FromArgMatches;
2+
use miden_client_cli::{Cli, MidenClientCli};
23

34
extern crate std;
45

56
#[tokio::main]
67
async fn main() -> miette::Result<()> {
7-
use clap::Parser;
8-
98
tracing_subscriber::fmt::init();
9+
1010
// read command-line args
11-
let cli = Cli::parse();
11+
let input = <MidenClientCli as clap::CommandFactory>::command();
12+
let matches = input.get_matches();
13+
let parsed = MidenClientCli::from_arg_matches(&matches).unwrap_or_else(|err| err.exit());
14+
let cli: Cli = parsed.into();
1215

1316
// execute cli action
1417
Ok(cli.execute().await?)

0 commit comments

Comments
 (0)