use anyhow::{Result, anyhow};
use miden_client::note::NoteTag;
use crate::{
rpc, store,
util::{config, net, parse},
};
use super::{
Cli, Command, NoteTypeFilter, NtxCommand, ParseCommand, RpcCommand, StoreAccountCommand,
StoreCommand, StoreNoteCommand, StoreTagCommand, StoreTxCommand,
};
impl Cli {
pub fn execute(self) -> Result<()> {
match self.command {
Command::Inspect {
file_path,
verbose,
validate,
network,
endpoint,
} => {
let endpoint = if validate {
Some(config::resolve_endpoint_with_fallback(network, endpoint)?)
} else {
None
};
rpc::inspect::inspect(file_path, endpoint, verbose)
}
Command::Rpc { command } => execute_rpc(command),
Command::Store { command } => execute_store(command),
Command::Parse { command } => execute_parse(command),
Command::Ntx { command } => execute_ntx(command),
}
}
}
fn execute_rpc(command: RpcCommand) -> Result<()> {
match command {
RpcCommand::Status { network, endpoint } => {
let endpoint = config::resolve_endpoint_with_fallback(network, endpoint)?;
rpc::status::rpc_status(endpoint)
}
RpcCommand::Block {
block_num,
network,
endpoint,
} => {
let block_num = parse::block_number(&block_num)?;
let endpoint = config::resolve_endpoint_with_fallback(network, endpoint)?;
rpc::status::rpc_block(endpoint, block_num)
}
RpcCommand::Note {
note_id,
save,
network,
endpoint,
} => {
let note_id = parse::note_id(¬e_id)?;
let endpoint = config::resolve_endpoint_with_fallback(network, endpoint)?;
rpc::inspect::inspect_note(note_id, endpoint, save)
}
RpcCommand::Account {
account,
verbose,
network,
endpoint,
} => {
let (account_id, address_network_hint) = parse::account_id(&account)?;
let selected_network_id = network.clone().and_then(net::network_id_for_cli_network);
let endpoint = config::resolve_endpoint_with_fallback(network, endpoint)?;
rpc::account::inspect_account(
account_id,
address_network_hint,
selected_network_id,
verbose,
endpoint,
)
}
}
}
fn execute_store(command: StoreCommand) -> Result<()> {
match command {
StoreCommand::Path => store::inspect::print_default_store_path(),
StoreCommand::Info { store } => {
let store = config::resolve_store_path(store)?;
store::inspect::inspect_store(store)
}
StoreCommand::Account { command } => match command {
StoreAccountCommand::Get {
store,
account,
commitment,
nonce,
} => {
let store = config::resolve_store_path(store)?;
let query = match (account, commitment, nonce) {
(Some(account), None, None) => {
let (account_id, _) = parse::account_id(&account)?;
store::account::StoreAccountQuery::AccountId(account_id)
}
(None, Some(commitment), None) => {
store::account::StoreAccountQuery::Commitment(commitment)
}
(None, None, Some(nonce)) => store::account::StoreAccountQuery::Nonce(nonce),
_ => {
return Err(anyhow!(
"provide exactly one of --account, --commitment, or --nonce"
));
}
};
store::account::inspect_store_account(store, query)
}
StoreAccountCommand::List { store } => {
let store = config::resolve_store_path(store)?;
store::account::list_store_accounts(store)
}
},
StoreCommand::Note { command } => match command {
StoreNoteCommand::Get { store, note_id } => {
let store = config::resolve_store_path(store)?;
let note_id = parse::note_id(¬e_id)?;
store::note::inspect_store_note(store, note_id)
}
StoreNoteCommand::List {
store,
input,
output,
state,
tag,
note_type,
} => {
let store = config::resolve_store_path(store)?;
let tag = match tag {
Some(tag) => {
let value = parse::u64(&tag)?;
let raw: u32 = value
.try_into()
.map_err(|_| anyhow!("note tag must fit in u32"))?;
Some(NoteTag::from(raw))
}
None => None,
};
let note_type = note_type.map(NoteTypeFilter::into_note_type);
let filters = store::note::NoteListFilters {
include_input: input,
include_output: output,
states: state,
tag,
note_type,
};
store::note::list_store_notes(store, filters)
}
},
StoreCommand::Tag { command } => match command {
StoreTagCommand::List { store } => {
let store = config::resolve_store_path(store)?;
store::tags::list_store_tags(store)
}
},
StoreCommand::Tx { command } => match command {
StoreTxCommand::Inspect {
tx_id,
store,
verbose,
} => {
let store = config::resolve_store_path(store)?;
let tx_id = parse::transaction_id(&tx_id)?;
rpc::tx::inspect_transaction(store, tx_id, verbose)
}
StoreTxCommand::List { store } => {
let store = config::resolve_store_path(store)?;
rpc::tx::list_transactions(store)
}
},
StoreCommand::Tui { store } => {
let store = config::resolve_store_path(store)?;
store::tui::run_store_tui(store)
}
}
}
fn execute_parse(command: ParseCommand) -> Result<()> {
use miden_client::address::{Address, AddressId};
match command {
ParseCommand::Word { values } => {
let word = parse::word(&values)?;
rpc::word::build_word(word)
}
ParseCommand::AccountId { account, network } => {
let decoded_address = Address::decode(&account).ok();
let (account_id, network_hint) = parse::account_id(&account)?;
let selected_network_id = network.clone().and_then(net::network_id_for_cli_network);
println!("Account ID: {}", account_id);
println!("- account id (hex): {}", account_id.to_hex());
println!("- account type: {:?}", account_id.account_type());
println!("- storage mode: {}", account_id.storage_mode());
println!(
"- public state: {}",
if account_id.has_public_state() {
"yes"
} else {
"no"
}
);
println!("- account ID version: {:?}", account_id.version());
if let Some((address_network, address)) = decoded_address {
if let Some(expected) = selected_network_id.clone()
&& expected != address_network
{
println!(
"- warning: address network {address_network} does not match selected {expected}"
);
}
println!("- address: {}", address.encode(address_network));
} else if let Some(network_id) = selected_network_id {
let address = Address::new(account_id);
println!(
"- address ({network_id}): {}",
address.encode(network_id.clone())
);
}
if let Some(network_id) = network_hint {
println!("- address network: {}", network_id);
}
Ok(())
}
ParseCommand::NoteTag { tag } => {
let value = parse::u64(&tag)?;
let raw: u32 = value
.try_into()
.map_err(|_| anyhow!("note tag must fit in u32"))?;
let tag = NoteTag::from(raw);
println!("Note tag: {}", tag);
println!("- raw (hex): 0x{raw:08x}");
println!("- binary: {:032b}", raw);
let trailing_zeros = raw.trailing_zeros();
if trailing_zeros >= 18 {
let significant_bits = 32 - trailing_zeros;
println!(
"- likely account target: yes ({} high bits set, {} low bits zero)",
significant_bits, trailing_zeros
);
let prefix_bits = raw >> (32 - 16); println!(
"- matches account prefixes starting with: 0x{:04x}...",
prefix_bits
);
} else if trailing_zeros >= 16 {
println!(
"- possible account target: yes ({} low bits zero, default is 18)",
trailing_zeros
);
} else {
println!("- likely account target: no (use case tag or custom structure)");
}
Ok(())
}
ParseCommand::Address { address, network } => {
let selected_network_id = network.clone().and_then(net::network_id_for_cli_network);
if let Ok((network_id, decoded_address)) = Address::decode(&address) {
let account_id = match decoded_address.id() {
AddressId::AccountId(id) => id,
_ => return Err(anyhow!("unsupported address type")),
};
println!("Address: {}", address);
println!("- network: {}", network_id);
println!("- account id: {}", account_id);
println!("- account type: {:?}", account_id.account_type());
println!("- storage mode: {}", account_id.storage_mode());
println!("- note tag length: {}", decoded_address.note_tag_len());
println!(
"- note tag: {}",
crate::render::note::format_note_tag(decoded_address.to_note_tag())
);
if let Some(interface) = decoded_address.interface() {
println!("- interface: {}", interface);
}
println!("- bech32: {}", decoded_address.encode(network_id.clone()));
if let Some(expected) = selected_network_id
&& expected != network_id
{
println!(
"- warning: address network {} does not match selected {}",
network_id, expected
);
}
Ok(())
} else {
let (account_id, network_hint) = parse::account_id(&address)?;
let addr = Address::new(account_id);
if let Some(network_id) = selected_network_id {
let encoded = addr.encode(network_id.clone());
println!("Address: {}", encoded);
println!("- network: {}", network_id);
} else {
println!("Address (from account id):");
println!("- bech32: n/a (provide --network testnet|devnet)");
}
println!("- account id: {}", account_id);
println!("- account type: {:?}", account_id.account_type());
println!("- storage mode: {}", account_id.storage_mode());
println!("- note tag length: {}", addr.note_tag_len());
println!(
"- note tag: {}",
crate::render::note::format_note_tag(addr.to_note_tag())
);
if let Some(network_id) = network_hint {
println!("- address network: {}", network_id);
}
Ok(())
}
}
ParseCommand::TxInputs { file_path, top } => rpc::tx_inputs::inspect(file_path, top),
}
}
fn execute_ntx(command: NtxCommand) -> Result<()> {
match command {
NtxCommand::Debug {
account,
note_ids,
verbose,
network,
endpoint,
} => {
let (account_id, _) = parse::account_id(&account)?;
let note_ids: Vec<_> = note_ids
.iter()
.map(|id| parse::note_id(id))
.collect::<Result<_>>()?;
let endpoint = config::resolve_endpoint_with_fallback(network, endpoint)?;
rpc::ntx::debug_ntx(account_id, note_ids, endpoint, verbose)
}
}
}