Skip to main content

decode_tx/
decode_tx.rs

1use base64::Engine;
2use solana_primitives::types::VersionedTransaction;
3
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // https://solscan.io/tx/24iGSgbnUUpL49Hp6ci46CrtujrMbDgZxJYcL6w3ySJudT3c5swGKnNygv4R1G3vYPwPYS9Emr3fimeEJQszDkzV
6    let tx = "ATU3t4TX00FNb4aqeojTYpIOZFQFED4pdoviPCBxUsEXwKHRbHBmmgsyEsDyjXREHGYAWlq13q4WzF5JDOvsBwKAAQAICwzd9xRZ1DCfrYBfTdgj38msCBn1maih1JKu7vCovzzacU2yFtM8/6xky5bNOdOL9vYNQWxOAtjDd8qJeKlPPJIXZyoBm/f1Y2xMsGa3gRLrL4a5Ba4vtFtLcOU1pWTT4wVKU1qZKSEGTSTocWDaOHx8NbXdvJK7geQfqEBBBUSNAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAGxoMMVxwvPwb8Y6KPKojQO9MxPeq3HTSRmeyRYk5jPwlU276eyWDJinopP+ITNpZv4YDRUa5LgXlWH4mFSlP2fhcCyQN7oqc4HZ+5zm3xXQ/FTXS7cxGfivEvGQwOFb4pA2InHkw7ace1KQX3P6I/rhhBsIayFI80eInx/pUv4R7U/fA1on6uX4cAPWh+6q5kflQbDzfTC/LJrf1AdS22d3DH2Q4dNNF2yrr6HjJmXJlYvanqTxxULDNngUZsJSq7g2zxeB7onMepv2C8TaiFnhOPYyeceZ5GEIyJx6r0FAYDADExNzQ2NzAxMDg2MTA0fDMzODYzMjQ4M3wxNzQ2NzAxMDg2MTEzfDMzODYzMjQ4M3xwBAAFApZUAgAEAAkDUMMAAAAAAAAFAgEAGFj16I5t+vX/F0jWAAAAAAAjixxoAAAAAAYMCwIABwgJCgwNDg8QDu7hX57jZwjCAQEBAQAABgwLAgAHCAkKDA0ODxBcPD8yewzFPL4CAAAAAQEAAACE1xcAAAAABAExPRcAAAABAAADAAEtixxoAAAAAAAAAAAAAAEBAQA0kqIPAAAAAPJZLkAXAAAAAQAAAwABLYscaAAAAAAAAAAAAAAB3E42Kl7pkP+vCgX6bK+0TFMtWHz2EUnYgJSxOMI1cDcABoNbXAMFAQ==";
7
8    // Decode base64 transaction
9    let tx_bytes = base64::engine::general_purpose::STANDARD.decode(tx)?;
10
11    // Print the transaction bytes structure
12    println!("Transaction bytes length: {}", tx_bytes.len());
13    println!("First byte (signature count): 0x{:02x}", tx_bytes[0]);
14
15    // Try to decode as versioned transaction using manual deserialization
16    match VersionedTransaction::deserialize_with_version(&tx_bytes) {
17        Ok(decoded_tx) => {
18            println!("✅ Successfully decoded transaction from wire format");
19            print_versioned_transaction(&decoded_tx);
20        }
21        Err(e) => {
22            println!("❌ Failed to decode transaction: {e}");
23        }
24    }
25
26    Ok(())
27}
28
29fn print_versioned_transaction(tx: &VersionedTransaction) {
30    match tx {
31        VersionedTransaction::Legacy {
32            signatures,
33            message,
34        } => {
35            println!("Transaction Type: Legacy");
36            println!("Number of signatures: {}", signatures.len());
37            for (i, sig) in signatures.iter().enumerate() {
38                println!("Signature {}: {}", i + 1, sig.to_base58());
39            }
40
41            println!("\nMessage header:");
42            println!(
43                "  Required signatures: {}",
44                message.header.num_required_signatures
45            );
46            println!(
47                "  Readonly signed accounts: {}",
48                message.header.num_readonly_signed_accounts
49            );
50            println!(
51                "  Readonly unsigned accounts: {}",
52                message.header.num_readonly_unsigned_accounts
53            );
54
55            println!("\nAccount keys: {}", message.account_keys.len());
56            for (i, key) in message.account_keys.iter().enumerate() {
57                println!("  Account {}: {}", i, key.to_base58());
58            }
59
60            println!(
61                "\nRecent blockhash: {}",
62                bs58::encode(&message.recent_blockhash).into_string()
63            );
64
65            println!("\nInstructions: {}", message.instructions.len());
66            for (i, instruction) in message.instructions.iter().enumerate() {
67                println!("\nInstruction {}:", i + 1);
68                println!("  Program ID Index: {}", instruction.program_id_index);
69                println!("  Account Indices: {:?}", instruction.accounts);
70                println!(
71                    "  Data (bs58): {}",
72                    bs58::encode(&instruction.data).into_string()
73                );
74                println!(
75                    "  Data (utf8): {}",
76                    String::from_utf8_lossy(&instruction.data)
77                );
78            }
79        }
80        VersionedTransaction::V0 {
81            signatures,
82            message,
83        } => {
84            println!("Transaction Type: V0");
85            println!("Number of signatures: {}", signatures.len());
86            for (i, sig) in signatures.iter().enumerate() {
87                println!("Signature {}: {}", i + 1, sig.to_base58());
88            }
89
90            println!("\nMessage header:");
91            println!(
92                "  Required signatures: {}",
93                message.header.num_required_signatures
94            );
95            println!(
96                "  Readonly signed accounts: {}",
97                message.header.num_readonly_signed_accounts
98            );
99            println!(
100                "  Readonly unsigned accounts: {}",
101                message.header.num_readonly_unsigned_accounts
102            );
103
104            println!("\nAccount keys: {}", message.account_keys.len());
105            for (i, key) in message.account_keys.iter().enumerate() {
106                println!("  Account {}: {}", i, key.to_base58());
107            }
108
109            println!(
110                "\nRecent blockhash: {}",
111                bs58::encode(&message.recent_blockhash).into_string()
112            );
113
114            println!("\nInstructions: {}", message.instructions.len());
115            for (i, instruction) in message.instructions.iter().enumerate() {
116                println!("\nInstruction {}:", i + 1);
117                println!("  Program ID Index: {}", instruction.program_id_index);
118                println!("  Account Indices: {:?}", instruction.accounts);
119                println!(
120                    "  Data (bs58): {}",
121                    bs58::encode(&instruction.data).into_string()
122                );
123                println!(
124                    "  Data (utf8): {}",
125                    String::from_utf8_lossy(&instruction.data)
126                );
127            }
128
129            if !message.address_table_lookups.is_empty() {
130                println!(
131                    "\nAddress Table Lookups: {}",
132                    message.address_table_lookups.len()
133                );
134                for (i, lookup) in message.address_table_lookups.iter().enumerate() {
135                    println!("\nLookup Table {}:", i + 1);
136                    println!("  Table Key: {}", lookup.account_key.to_base58());
137                    println!("  Writable Indexes: {:?}", lookup.writable_indexes);
138                    println!("  Readonly Indexes: {:?}", lookup.readonly_indexes);
139                }
140            }
141        }
142    }
143}