use { clap::{crate_description, crate_name, value_t_or_exit, ArgMatches}, console::style, solana_clap_utils::{ input_validators::normalize_to_url_if_moniker, keypair::{CliSigners, DefaultSigner}, DisplayError, }, solana_cli::{ clap_app::get_clap_app, cli::{parse_command, process_command, CliCommandInfo, CliConfig}, }, solana_cli_config::{Config, ConfigInput}, solana_cli_output::{ display::{println_name_value, println_name_value_or}, OutputFormat, }, solana_remote_wallet::remote_wallet::RemoteWalletManager, solana_rpc_client_api::config::RpcSendTransactionConfig, std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration}, }; fn parse_settings(matches: &ArgMatches<'_>) -> Result> { let parse_args = match matches.subcommand() { ("config", Some(matches)) => { let config_file = match matches.value_of("config_file") { None => { println!( "{} Either provide the `--config` arg or ensure home directory exists to use the default config location", style("No config file found.").bold() ); return Ok(false); } Some(config_file) => config_file, }; let mut config = Config::load(config_file).unwrap_or_default(); match matches.subcommand() { ("get", Some(subcommand_matches)) => { let (url_setting_type, json_rpc_url) = ConfigInput::compute_json_rpc_url_setting("", &config.json_rpc_url); let (ws_setting_type, websocket_url) = ConfigInput::compute_websocket_url_setting( "", &config.websocket_url, "", &config.json_rpc_url, ); let (keypair_setting_type, keypair_path) = ConfigInput::compute_keypair_path_setting("", &config.keypair_path); let (commitment_setting_type, commitment) = ConfigInput::compute_commitment_config("", &config.commitment); if let Some(field) = subcommand_matches.value_of("specific_setting") { let (field_name, value, setting_type) = match field { "json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type), "websocket_url" => ("WebSocket URL", websocket_url, ws_setting_type), "keypair" => ("Key Path", keypair_path, keypair_setting_type), "commitment" => ( "Commitment", commitment.commitment.to_string(), commitment_setting_type, ), _ => unreachable!(), }; println_name_value_or(&format!("{}:", field_name), &value, setting_type); } else { println_name_value("Config File:", config_file); println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type); println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type); println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type); println_name_value_or( "Commitment:", &commitment.commitment.to_string(), commitment_setting_type, ); } } ("set", Some(subcommand_matches)) => { if let Some(url) = subcommand_matches.value_of("json_rpc_url") { config.json_rpc_url = normalize_to_url_if_moniker(url); // Revert to a computed `websocket_url` value when `json_rpc_url` is // changed config.websocket_url = "".to_string(); } if let Some(url) = subcommand_matches.value_of("websocket_url") { config.websocket_url = url.to_string(); } if let Some(keypair) = subcommand_matches.value_of("keypair") { config.keypair_path = keypair.to_string(); } if let Some(commitment) = subcommand_matches.value_of("commitment") { config.commitment = commitment.to_string(); } config.save(config_file)?; let (url_setting_type, json_rpc_url) = ConfigInput::compute_json_rpc_url_setting("", &config.json_rpc_url); let (ws_setting_type, websocket_url) = ConfigInput::compute_websocket_url_setting( "", &config.websocket_url, "", &config.json_rpc_url, ); let (keypair_setting_type, keypair_path) = ConfigInput::compute_keypair_path_setting("", &config.keypair_path); let (commitment_setting_type, commitment) = ConfigInput::compute_commitment_config("", &config.commitment); println_name_value("Config File:", config_file); println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type); println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type); println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type); println_name_value_or( "Commitment:", &commitment.commitment.to_string(), commitment_setting_type, ); } ("import-address-labels", Some(subcommand_matches)) => { let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf); config.import_address_labels(&filename)?; config.save(config_file)?; println!("Address labels imported from {:?}", filename); } ("export-address-labels", Some(subcommand_matches)) => { let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf); config.export_address_labels(&filename)?; println!("Address labels exported to {:?}", filename); } _ => unreachable!(), } false } _ => true, }; Ok(parse_args) } pub fn parse_args<'a>( matches: &ArgMatches<'_>, wallet_manager: &mut Option>, ) -> Result<(CliConfig<'a>, CliSigners), Box> { let config = if let Some(config_file) = matches.value_of("config_file") { Config::load(config_file).unwrap_or_default() } else { Config::default() }; let (_, json_rpc_url) = ConfigInput::compute_json_rpc_url_setting( matches.value_of("json_rpc_url").unwrap_or(""), &config.json_rpc_url, ); let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64); let rpc_timeout = Duration::from_secs(rpc_timeout); let confirm_transaction_initial_timeout = value_t_or_exit!(matches, "confirm_transaction_initial_timeout", u64); let confirm_transaction_initial_timeout = Duration::from_secs(confirm_transaction_initial_timeout); let (_, websocket_url) = ConfigInput::compute_websocket_url_setting( matches.value_of("websocket_url").unwrap_or(""), &config.websocket_url, matches.value_of("json_rpc_url").unwrap_or(""), &config.json_rpc_url, ); let default_signer_arg_name = "keypair".to_string(); let (_, default_signer_path) = ConfigInput::compute_keypair_path_setting( matches.value_of(&default_signer_arg_name).unwrap_or(""), &config.keypair_path, ); let default_signer = DefaultSigner::new(default_signer_arg_name, &default_signer_path); let CliCommandInfo { command, mut signers, } = parse_command(matches, &default_signer, wallet_manager)?; if signers.is_empty() { if let Ok(signer_info) = default_signer.generate_unique_signers(vec![None], matches, wallet_manager) { signers.extend(signer_info.signers); } } let verbose = matches.is_present("verbose"); let output_format = OutputFormat::from_matches(matches, "output_format", verbose); let (_, commitment) = ConfigInput::compute_commitment_config( matches.value_of("commitment").unwrap_or(""), &config.commitment, ); let address_labels = if matches.is_present("no_address_labels") { HashMap::new() } else { config.address_labels }; Ok(( CliConfig { command, json_rpc_url, websocket_url, signers: vec![], keypair_path: default_signer_path, rpc_client: None, rpc_timeout, verbose, output_format, commitment, send_transaction_config: RpcSendTransactionConfig { preflight_commitment: Some(commitment.commitment), ..RpcSendTransactionConfig::default() }, confirm_transaction_initial_timeout, address_labels, }, signers, )) } fn main() -> Result<(), Box> { solana_logger::setup_with_default("off"); let matches = get_clap_app( crate_name!(), crate_description!(), solana_version::version!(), ) .get_matches(); do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into()) } fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box> { if parse_settings(matches)? { let mut wallet_manager = None; let (mut config, signers) = parse_args(matches, &mut wallet_manager)?; config.signers = signers.iter().map(|s| s.as_ref()).collect(); let result = process_command(&config)?; println!("{}", result); }; Ok(()) }