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>>;