diff --git a/cli-output/src/display.rs b/cli-output/src/display.rs index 0eb2f34301..fdcf7e9e18 100644 --- a/cli-output/src/display.rs +++ b/cli-output/src/display.rs @@ -4,7 +4,7 @@ use { console::style, indicatif::{ProgressBar, ProgressStyle}, solana_sdk::{ - clock::UnixTimestamp, hash::Hash, native_token::lamports_to_sol, + clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol, program_utils::limited_deserialize, transaction::Transaction, }, solana_transaction_status::UiTransactionStatusMeta, @@ -125,6 +125,31 @@ pub fn println_signers( println!(); } +fn format_account_mode(message: &Message, index: usize) -> String { + format!( + "{}r{}{}", // accounts are always readable... + if message.is_signer(index) { + "s" // stands for signer + } else { + "-" + }, + if message.is_writable(index) { + "w" // comment for consistent rust fmt (no joking; lol) + } else { + "-" + }, + // account may be executable on-chain while not being + // designated as a program-id in the message + if message.maybe_executable(index) { + "x" + } else { + // programs to be executed via CPI cannot be identified as + // executable from the message + "-" + }, + ) +} + pub fn write_transaction( w: &mut W, transaction: &Transaction, @@ -167,16 +192,31 @@ pub fn write_transaction( prefix, signature_index, signature, sigverify_status, )?; } - writeln!(w, "{}{:?}", prefix, message.header)?; + let mut fee_payer_index = None; for (account_index, account) in message.account_keys.iter().enumerate() { - writeln!(w, "{}Account {}: {:?}", prefix, account_index, account)?; + if fee_payer_index.is_none() && message.is_non_loader_key(account, account_index) { + fee_payer_index = Some(account_index) + } + writeln!( + w, + "{}Account {}: {} {}{}", + prefix, + account_index, + format_account_mode(message, account_index), + account, + if Some(account_index) == fee_payer_index { + " (fee payer)" + } else { + "" + }, + )?; } for (instruction_index, instruction) in message.instructions.iter().enumerate() { let program_pubkey = message.account_keys[instruction.program_id_index as usize]; writeln!(w, "{}Instruction {}", prefix, instruction_index)?; writeln!( w, - "{} Program: {} ({})", + "{} Program: {} ({})", prefix, program_pubkey, instruction.program_id_index )?; for (account_index, account) in instruction.accounts.iter().enumerate() { diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 599df37e6b..d84a8a6682 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1987,6 +1987,17 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .takes_value(true) .required(true) .help("The transaction signature to confirm"), + ) + .after_help(// Formatted specifically for the manually-indented heredoc string + "Note: This will show more detailed information for finalized transactions with verbose mode (-v/--verbose).\ + \n\ + \nAccount modes:\ + \n |srwx|\ + \n s: signed\ + \n r: readable (always true)\ + \n w: writable\ + \n x: program account (inner instructions excluded)\ + " ), ) .subcommand( diff --git a/sdk/program/src/message.rs b/sdk/program/src/message.rs index b9afc2311a..ade4f12708 100644 --- a/sdk/program/src/message.rs +++ b/sdk/program/src/message.rs @@ -319,6 +319,10 @@ impl Message { .position(|&&pubkey| pubkey == self.account_keys[index]) } + pub fn maybe_executable(&self, i: usize) -> bool { + self.program_position(i).is_some() + } + pub fn is_writable(&self, i: usize) -> bool { i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts) as usize