solana_tx_decoding/tx/
analyze_tx.rs1use crate::tx::top_level_instructions_loop::top_level_instructions_loop;
2use crate::types::tx_format::TxFormat;
3use bumpalo::Bump;
4use solana_central::Instruction;
5use solana_central::SwapTx;
6use solana_central::TokenCreation;
7use solana_sdk::pubkey::Pubkey;
8use solana_sdk::signature::Signature;
9use solana_transaction_status_client_types::UiInstruction;
10use std::collections::HashMap;
11use std::collections::HashSet;
12use tokio::sync::broadcast::Sender;
13
14pub fn analyze_tx(
21 tx: &TxFormat,
22 swap_tx_sender: &Sender<SwapTx>,
23 token_create_sender: &Sender<TokenCreation>,
24 block_time: u64,
25 slot: u64,
26 index: u64,
27) {
28 let mut account_keys;
29 let mut top_level_instructions: Vec<Instruction> = Vec::new();
30 let mut inner_instructions: HashMap<u8, Vec<Instruction>> = HashMap::new();
32 let mut ta_mint: HashMap<u8, Pubkey> = HashMap::new();
34 let mut running_token_balances: HashMap<u8, u64> = HashMap::new();
36 let num_required_signatures;
37 let signature;
38 let arena;
39
40 match tx {
41 TxFormat::Archive(tx) => {
43 if tx.meta.err.is_some() {
45 return;
46 }
47 let account_keys_length = tx.tx.message.static_account_keys().len()
48 + tx.meta.loaded_writable_addresses.len()
49 + tx.meta.loaded_readonly_addresses.len();
50 account_keys = Vec::with_capacity(account_keys_length);
51 account_keys.extend_from_slice(tx.tx.message.static_account_keys());
52
53 for bytes in &tx.meta.loaded_writable_addresses {
55 account_keys.push(
56 bytes
57 .as_slice()
58 .try_into()
59 .map(Pubkey::new_from_array)
60 .unwrap(),
61 );
62 }
63 for bytes in &tx.meta.loaded_readonly_addresses {
65 account_keys.push(
66 bytes
67 .as_slice()
68 .try_into()
69 .map(Pubkey::new_from_array)
70 .unwrap(),
71 );
72 }
73 let mut atomic_instruction_index = 0;
74 for raw_inst in tx.tx.message.instructions() {
75 let inst = Instruction {
76 tx_account_keys: &account_keys,
77 accounts: &raw_inst.accounts,
78 data: &raw_inst.data,
79 program_id_index: raw_inst.program_id_index,
80 };
81 top_level_instructions.push(inst);
82 atomic_instruction_index += 1;
83 }
84
85 for inner_inst_set in &tx.meta.inner_instructions {
86 for inner_inst_raw in &inner_inst_set.instructions {
87 let inner_inst = Instruction {
88 tx_account_keys: &account_keys,
89 accounts: &inner_inst_raw.accounts,
90 data: &inner_inst_raw.data,
91 program_id_index: inner_inst_raw.program_id_index as u8,
92 };
93 inner_instructions
94 .entry(inner_inst_set.index as u8)
95 .or_insert(Vec::new())
96 .push(inner_inst);
97 atomic_instruction_index += 1;
98 }
99 }
100
101 for pre_token_balance in &tx.meta.pre_token_balances {
102 ta_mint.insert(
103 pre_token_balance.account_index as u8,
104 Pubkey::from_str_const(&pre_token_balance.mint),
105 );
106 running_token_balances.insert(
107 pre_token_balance.account_index as u8,
108 pre_token_balance
109 .ui_token_amount
110 .as_ref()
111 .unwrap()
112 .amount
113 .parse::<u64>()
114 .unwrap(),
115 );
116 }
117 for post_token_balance in &tx.meta.post_token_balances {
118 ta_mint.insert(
119 post_token_balance.account_index as u8,
120 Pubkey::from_str_const(&post_token_balance.mint),
121 );
122 }
123 num_required_signatures = tx.tx.message.header().num_required_signatures;
124 signature = Signature::from(tx.tx.signatures[0]);
125 }
126
127 TxFormat::Grpc(tx) => {
129 if tx.meta.err.is_some() {
131 return;
132 }
133 let message = tx.tx.message.as_ref().unwrap();
134 let account_keys_length = message.account_keys.len()
135 + tx.meta.loaded_writable_addresses.len()
136 + tx.meta.loaded_readonly_addresses.len();
137 account_keys = Vec::with_capacity(account_keys_length);
138 for bytes in &message.account_keys {
139 account_keys.push(
140 bytes
141 .as_slice()
142 .try_into()
143 .map(Pubkey::new_from_array)
144 .unwrap(),
145 );
146 }
147
148 for bytes in &tx.meta.loaded_writable_addresses {
150 account_keys.push(
151 bytes
152 .as_slice()
153 .try_into()
154 .map(Pubkey::new_from_array)
155 .unwrap(),
156 );
157 }
158 for bytes in &tx.meta.loaded_readonly_addresses {
160 account_keys.push(
161 bytes
162 .as_slice()
163 .try_into()
164 .map(Pubkey::new_from_array)
165 .unwrap(),
166 );
167 }
168 let mut atomic_instruction_index = 0;
169 for raw_inst in &message.instructions {
170 let inst = Instruction {
171 tx_account_keys: &account_keys,
172 accounts: &raw_inst.accounts,
173 data: &raw_inst.data,
174 program_id_index: raw_inst.program_id_index as u8,
175 };
176 top_level_instructions.push(inst);
177 atomic_instruction_index += 1;
178 }
179
180 for inner_inst_set in &tx.meta.inner_instructions {
181 for inner_inst_raw in &inner_inst_set.instructions {
182 let inner_inst = Instruction {
183 tx_account_keys: &account_keys,
184 accounts: &inner_inst_raw.accounts,
185 data: &inner_inst_raw.data,
186 program_id_index: inner_inst_raw.program_id_index as u8,
187 };
188 inner_instructions
189 .entry(inner_inst_set.index as u8)
190 .or_insert(Vec::new())
191 .push(inner_inst);
192 atomic_instruction_index += 1;
193 }
194 }
195
196 for pre_token_balance in &tx.meta.pre_token_balances {
197 ta_mint.insert(
198 pre_token_balance.account_index as u8,
199 Pubkey::from_str_const(&pre_token_balance.mint),
200 );
201 running_token_balances.insert(
202 pre_token_balance.account_index as u8,
203 pre_token_balance
204 .ui_token_amount
205 .as_ref()
206 .unwrap()
207 .amount
208 .parse::<u64>()
209 .unwrap(),
210 );
211 }
212 for post_token_balance in &tx.meta.post_token_balances {
213 ta_mint.insert(
214 post_token_balance.account_index as u8,
215 Pubkey::from_str_const(&post_token_balance.mint),
216 );
217 }
218 num_required_signatures = message.header.unwrap().num_required_signatures as u8;
219 signature = Signature::from(
220 <[u8; 64]>::try_from(tx.tx.signatures[0].as_slice())
221 .expect("analyze_tx: Signature should be 64 bytes"),
222 );
223 }
224
225 TxFormat::JsonRpc(tx) => {
226 if tx.meta.err.is_some() {
228 return;
230 }
231 arena = Bump::new();
232
233 let loaded_addresses = tx.meta.loaded_addresses.as_ref().unwrap();
234
235 let account_keys_length = tx.tx.message.static_account_keys().len()
236 + loaded_addresses.writable.len()
237 + loaded_addresses.readonly.len();
238 account_keys = Vec::with_capacity(account_keys_length);
239 account_keys.extend_from_slice(tx.tx.message.static_account_keys());
240
241 for base58_string in &loaded_addresses.writable {
243 account_keys.push(Pubkey::from_str_const(base58_string));
244 }
245 for base58_string in &loaded_addresses.readonly {
247 account_keys.push(Pubkey::from_str_const(base58_string));
248 }
249
250 let mut atomic_instruction_index = 0;
251 for raw_inst in tx.tx.message.instructions() {
252 let inst = Instruction {
253 tx_account_keys: &account_keys,
254 accounts: &raw_inst.accounts,
255 data: &raw_inst.data,
256 program_id_index: raw_inst.program_id_index,
257 };
258 top_level_instructions.push(inst);
259 atomic_instruction_index += 1;
260 }
261
262 for inner_inst_set in tx.meta.inner_instructions.as_ref().unwrap() {
263 for inner_inst_raw in &inner_inst_set.instructions {
264 let inner_inst;
265 match inner_inst_raw {
266 UiInstruction::Compiled(inner_inst_raw) => {
267 let data = bs58::decode(&inner_inst_raw.data).into_vec().unwrap();
269 let data = arena.alloc_slice_copy(&data);
270 inner_inst = Instruction {
271 tx_account_keys: &account_keys,
272 accounts: &inner_inst_raw.accounts,
273 data,
274 program_id_index: inner_inst_raw.program_id_index as u8,
275 };
276 }
277 UiInstruction::Parsed(_) => {
278 panic!("We should not be getting parsed instructions here");
279 }
280 }
281 inner_instructions
282 .entry(inner_inst_set.index as u8)
283 .or_insert(Vec::new())
284 .push(inner_inst);
285 atomic_instruction_index += 1;
286 }
287 }
288
289 let pre_token_balances = tx.meta.pre_token_balances.as_ref().unwrap();
290 for pre_token_balance in pre_token_balances {
291 ta_mint.insert(
292 pre_token_balance.account_index as u8,
293 Pubkey::from_str_const(&pre_token_balance.mint),
294 );
295 running_token_balances.insert(
296 pre_token_balance.account_index as u8,
297 pre_token_balance
298 .ui_token_amount
299 .amount
300 .parse::<u64>()
301 .unwrap(),
302 );
303 }
304 for post_token_balance in tx.meta.post_token_balances.as_ref().unwrap() {
305 ta_mint.insert(
306 post_token_balance.account_index as u8,
307 Pubkey::from_str_const(&post_token_balance.mint),
308 );
309 }
310 num_required_signatures = tx.tx.message.header().num_required_signatures;
311 signature = Signature::from(tx.tx.signatures[0]);
312 }
313 }
314
315 let mut signers = HashSet::new();
316 for i in 0..num_required_signatures {
317 signers.insert(account_keys[i as usize]);
318 }
319
320 top_level_instructions_loop(
321 &top_level_instructions,
322 &inner_instructions,
323 &account_keys,
324 &ta_mint,
325 &mut running_token_balances,
326 swap_tx_sender,
327 token_create_sender,
328 block_time,
329 slot,
330 index,
331 &signers,
332 &signature,
333 );
334}