solana_block_decoder/instruction/
inner_instruction.rs1
2use {
3 crate::{
4 instruction::CompiledInstruction,
5 errors::{
6 conversion_error::ConversionError,
7 }
8 },
9 serde_derive::{Deserialize, Serialize},
10 solana_transaction_status_client_types::{
11 UiCompiledInstruction,
12 UiInnerInstructions,
13 UiInstruction,
14 },
15 std::{
16 convert::TryFrom,
17 },
18};
19
20#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
21pub struct InnerInstructions {
22 pub index: u8,
24 pub instructions: Vec<InnerInstruction>,
26}
27
28impl TryFrom<UiInnerInstructions> for InnerInstructions {
29 type Error = ConversionError;
30
31 fn try_from(ui_inner_instructions: UiInnerInstructions) -> Result<Self, Self::Error> {
32 let instructions_result: Result<Vec<_>, _> = ui_inner_instructions
33 .instructions
34 .into_iter()
35 .map(|ix| match ix {
36 UiInstruction::Compiled(ui_compiled) => Ok(InnerInstruction::from(ui_compiled)),
37 _ => Err(ConversionError::UnsupportedInstructionFormat),
38 })
39 .collect();
40
41 match instructions_result {
42 Ok(instructions) => Ok(Self {
43 index: ui_inner_instructions.index,
44 instructions,
45 }),
46 Err(e) => Err(e),
47 }
48 }
49}
50
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub struct InnerInstruction {
53 pub instruction: CompiledInstruction,
55 pub stack_height: Option<u32>,
57}
58
59impl From<UiCompiledInstruction> for InnerInstruction {
60 fn from(ui_compiled_instruction: UiCompiledInstruction) -> Self {
61 Self {
62 instruction: CompiledInstruction::from(ui_compiled_instruction.clone()), stack_height: ui_compiled_instruction.stack_height,
64 }
65 }
66}
67
68impl From<InnerInstructions> for solana_transaction_status_client_types::InnerInstructions {
69 fn from(instr: InnerInstructions) -> Self {
70 Self {
71 index: instr.index,
72 instructions: instr.instructions.into_iter().map(Into::into).collect(),
73 }
74 }
75}
76
77impl From<InnerInstruction> for solana_transaction_status_client_types::InnerInstruction {
78 fn from(instr: InnerInstruction) -> Self {
79 Self {
80 instruction: instr.instruction.into(),
81 stack_height: instr.stack_height,
82 }
83 }
84}
85
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use solana_transaction_status_client_types::{
91 UiCompiledInstruction, UiInnerInstructions, UiInstruction, UiParsedInstruction
92 };
93 use crate::{instruction::CompiledInstruction, errors::conversion_error::ConversionError};
94
95 #[test]
96 fn test_ui_inner_instructions_to_inner_instructions_success() {
97 let ui_compiled_instruction = UiCompiledInstruction {
98 program_id_index: 1,
99 accounts: vec![2, 3],
100 data: "abcd".to_string(),
101 stack_height: Some(5),
102 };
103 let ui_inner_instructions = UiInnerInstructions {
104 index: 0,
105 instructions: vec![UiInstruction::Compiled(ui_compiled_instruction.clone())],
106 };
107
108 let result = InnerInstructions::try_from(ui_inner_instructions);
109 assert!(result.is_ok());
110 let inner_instructions = result.unwrap();
111 assert_eq!(inner_instructions.index, 0);
112 assert_eq!(inner_instructions.instructions.len(), 1);
113 assert_eq!(inner_instructions.instructions[0].stack_height, Some(5));
114 assert_eq!(inner_instructions.instructions[0].instruction.program_id_index, 1);
115 }
116
117 #[test]
118 fn test_ui_inner_instructions_to_inner_instructions_error() {
119 use solana_transaction_status_client_types::{UiParsedInstruction, ParsedInstruction};
120
121 let ui_inner_instructions = UiInnerInstructions {
122 index: 1,
123 instructions: vec![UiInstruction::Parsed(UiParsedInstruction::Parsed(ParsedInstruction {
124 program: "Unsupported".to_string(),
125 parsed: serde_json::Value::Null,
126 program_id: "unsupported_program".to_string(),
127 stack_height: None,
128 }))],
129 };
130
131 let result = InnerInstructions::try_from(ui_inner_instructions);
132 assert!(result.is_err());
133 assert!(matches!(result.unwrap_err(), ConversionError::UnsupportedInstructionFormat));
134 }
135
136 #[test]
137 fn test_ui_compiled_instruction_to_inner_instruction() {
138 use bs58;
139
140 let encoded_data = bs58::encode("data").into_string();
141
142 let ui_compiled_instruction = UiCompiledInstruction {
143 program_id_index: 2,
144 accounts: vec![4, 5, 6],
145 data: encoded_data,
146 stack_height: Some(3),
147 };
148
149 let inner_instruction = InnerInstruction::from(ui_compiled_instruction.clone());
150
151 assert_eq!(inner_instruction.stack_height, Some(3));
152 assert_eq!(inner_instruction.instruction.program_id_index, 2);
153 assert_eq!(inner_instruction.instruction.accounts, vec![4, 5, 6]);
154 assert_eq!(inner_instruction.instruction.data, b"data".to_vec());
155 }
156
157 #[test]
158 fn test_inner_instructions_to_solana_inner_instructions() {
159 let compiled_instruction = CompiledInstruction {
160 program_id_index: 3,
161 accounts: vec![7, 8],
162 data: vec![1, 2, 3],
163 };
164 let inner_instruction = InnerInstruction {
165 instruction: compiled_instruction.clone(),
166 stack_height: Some(10),
167 };
168 let inner_instructions = InnerInstructions {
169 index: 2,
170 instructions: vec![inner_instruction.clone()],
171 };
172
173 let solana_inner_instructions: solana_transaction_status_client_types::InnerInstructions = inner_instructions.into();
174 assert_eq!(solana_inner_instructions.index, 2);
175 assert_eq!(solana_inner_instructions.instructions.len(), 1);
176 assert_eq!(solana_inner_instructions.instructions[0].stack_height, Some(10));
177 }
178
179 #[test]
180 fn test_inner_instruction_to_solana_inner_instruction() {
181 let compiled_instruction = CompiledInstruction {
182 program_id_index: 4,
183 accounts: vec![9, 10],
184 data: vec![4, 5, 6],
185 };
186 let inner_instruction = InnerInstruction {
187 instruction: compiled_instruction.clone(),
188 stack_height: Some(7),
189 };
190
191 let solana_inner_instruction: solana_transaction_status_client_types::InnerInstruction = inner_instruction.into();
192 assert_eq!(solana_inner_instruction.stack_height, Some(7));
193 assert_eq!(solana_inner_instruction.instruction.program_id_index, 4);
194 }
195}
196
197
198