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}
38
39impl Error for TXError {}
40
41impl Display for TXError {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "Transaction failed: {}", self.metadata.err)
44    }
45}
46
47impl TXError {
48    /// Print the error details, formatted using an [AddressBook].
49    pub fn print_error(&self, address_book: &AddressBook) {
50        println!(
51            "\n{} {}",
52            "❌".red(),
53            "Transaction failed with error:".red().bold()
54        );
55        println!("   {}", format!("{:?}", self.metadata.err).bright_red());
56
57        println!(
58            "\n{} {}",
59            "📜".yellow(),
60            "Transaction Logs:".yellow().bold()
61        );
62        println!(
63            "{}",
64            address_book.replace_addresses_in_text(&self.metadata.meta.pretty_logs())
65        );
66
67        // Log each instruction for debugging
68        println!(
69            "\n{} {}",
70            "📋".blue(),
71            "Instructions in failed transaction:".blue().bold()
72        );
73        for (i, ix) in self.transaction.message.instructions.iter().enumerate() {
74            let program_id = self.transaction.message.account_keys[ix.program_id_index as usize];
75            println!(
76                "   {} {}: {}",
77                "Instruction".dimmed(),
78                i.to_string().bold(),
79                address_book.format_address(&program_id)
80            );
81            println!(
82                "   {} {}",
83                "Accounts:".dimmed(),
84                format!("{} total", ix.accounts.len()).cyan()
85            );
86
87            // Show account details with labels from address book
88            for (j, account_index) in ix.accounts.iter().enumerate() {
89                let account_key = self.transaction.message.account_keys[*account_index as usize];
90                let is_signer = self.transaction.message.is_signer(*account_index as usize);
91                let is_writable = self
92                    .transaction
93                    .message
94                    .is_maybe_writable(*account_index as usize, None);
95
96                let mut flags = Vec::new();
97                if is_signer {
98                    flags.push("signer".green().to_string());
99                }
100                if is_writable {
101                    flags.push("writable".yellow().to_string());
102                }
103
104                let flags_str = if !flags.is_empty() {
105                    format!(" [{}]", flags.join(", "))
106                } else {
107                    String::new()
108                };
109
110                println!(
111                    "     {} {}: {}{}",
112                    "Account".dimmed(),
113                    j.to_string().bold(),
114                    address_book.format_address(&account_key),
115                    flags_str
116                );
117            }
118        }
119
120        // Print the address book for reference
121    }
122}
123
124/// A result type that represents the result of a transaction.
125pub type TXResult = Result<TransactionMetadata, Box<TXError>>;