testsvm_core/tx_result.rs
1//! # Transaction Result Management
2//!
3//! Enhanced transaction result handling with detailed error reporting and analysis.
4//!
5//! This module provides rich transaction result types that extend LiteSVM's basic
6//! transaction metadata with additional debugging capabilities, colored output,
7//! and assertion helpers. It makes it easy to understand why transactions failed
8//! and provides useful information for successful transactions.
9//!
10//! ## Features
11//!
12//! - **Detailed Error Information**: Comprehensive error context including logs and instruction errors
13//! - **Colored Output**: Enhanced readability with color-coded transaction logs
14//! - **Assertion Helpers**: Built-in methods for testing expected outcomes
15//! - **Address Resolution**: Automatic replacement of addresses with labels from the address book
16//! - **Anchor Error Support**: Special handling for Anchor framework error codes
17
18use std::error::Error;
19use std::fmt::Display;
20
21use colored::Colorize;
22use litesvm::types::{FailedTransactionMetadata, TransactionMetadata};
23use solana_sdk::transaction::Transaction;
24
25use solana_address_book::AddressBook;
26
27/// Error type representing a failed transaction with detailed metadata.
28///
29/// Contains both the original transaction and the failure metadata from LiteSVM,
30/// allowing for comprehensive error analysis and debugging.
31#[derive(Debug)]
32pub struct TXError {
33 /// The transaction that failed
34 pub transaction: Transaction,
35 /// Underlying failed transaction metadata
36 pub metadata: FailedTransactionMetadata,
37 /// Address book at the time of transaction failure
38 pub address_book: AddressBook,
39}
40
41impl Error for TXError {}
42
43impl Display for TXError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(f, "Transaction failed: {}", self.metadata.err)
46 }
47}
48
49impl TXError {
50 /// Print the error details, formatted using an [AddressBook].
51 pub fn print_error(&self) {
52 println!(
53 "\n{} {}",
54 "❌".red(),
55 "Transaction failed with error:".red().bold()
56 );
57 println!(" {}", format!("{:?}", self.metadata.err).bright_red());
58
59 println!(
60 "\n{} {}",
61 "📜".yellow(),
62 "Transaction Logs:".yellow().bold()
63 );
64 println!(
65 "{}",
66 self.address_book
67 .replace_addresses_in_text(&self.metadata.meta.pretty_logs())
68 );
69
70 // Log each instruction for debugging
71 println!(
72 "\n{} {}",
73 "📋".blue(),
74 "Instructions in failed transaction:".blue().bold()
75 );
76 for (i, ix) in self.transaction.message.instructions.iter().enumerate() {
77 let program_id = self.transaction.message.account_keys[ix.program_id_index as usize];
78 println!(
79 " {} {}: {}",
80 "Instruction".dimmed(),
81 i.to_string().bold(),
82 self.address_book.format_address(&program_id)
83 );
84 println!(
85 " {} {}",
86 "Accounts:".dimmed(),
87 format!("{} total", ix.accounts.len()).cyan()
88 );
89
90 // Show account details with labels from address book
91 for (j, account_index) in ix.accounts.iter().enumerate() {
92 let account_key = self.transaction.message.account_keys[*account_index as usize];
93 let is_signer = self.transaction.message.is_signer(*account_index as usize);
94 let is_writable = self
95 .transaction
96 .message
97 .is_maybe_writable(*account_index as usize, None);
98
99 let mut flags = Vec::new();
100 if is_signer {
101 flags.push("signer".green().to_string());
102 }
103 if is_writable {
104 flags.push("writable".yellow().to_string());
105 }
106
107 let flags_str = if !flags.is_empty() {
108 format!(" [{}]", flags.join(", "))
109 } else {
110 String::new()
111 };
112
113 println!(
114 " {} {}: {}{}",
115 "Account".dimmed(),
116 j.to_string().bold(),
117 self.address_book.format_address(&account_key),
118 flags_str
119 );
120 }
121 }
122
123 // Print the address book for reference
124 }
125}
126
127/// A result type that represents the result of a transaction.
128pub type TXResult = Result<TransactionMetadata, Box<TXError>>;