openzeppelin_monitor/utils/tests/builders/solana/
transaction.rs1use crate::models::{
6 SolanaInstruction, SolanaParsedInstruction, SolanaTransaction, SolanaTransactionInfo,
7 SolanaTransactionMessage, SolanaTransactionMeta,
8};
9
10#[derive(Debug, Default)]
12pub struct TransactionBuilder {
13 signature: Option<String>,
14 slot: Option<u64>,
15 block_time: Option<i64>,
16 account_keys: Option<Vec<String>>,
17 recent_blockhash: Option<String>,
18 instructions: Option<Vec<SolanaInstruction>>,
19 fee: Option<u64>,
20 err: Option<serde_json::Value>,
21 pre_balances: Option<Vec<u64>>,
22 post_balances: Option<Vec<u64>>,
23 log_messages: Option<Vec<String>>,
24 compute_units_consumed: Option<u64>,
25}
26
27impl TransactionBuilder {
28 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn signature(mut self, signature: &str) -> Self {
35 self.signature = Some(signature.to_string());
36 self
37 }
38
39 pub fn slot(mut self, slot: u64) -> Self {
41 self.slot = Some(slot);
42 self
43 }
44
45 pub fn block_time(mut self, block_time: i64) -> Self {
47 self.block_time = Some(block_time);
48 self
49 }
50
51 pub fn account_keys(mut self, account_keys: Vec<String>) -> Self {
53 self.account_keys = Some(account_keys);
54 self
55 }
56
57 pub fn recent_blockhash(mut self, recent_blockhash: &str) -> Self {
59 self.recent_blockhash = Some(recent_blockhash.to_string());
60 self
61 }
62
63 pub fn instructions(mut self, instructions: Vec<SolanaInstruction>) -> Self {
65 self.instructions = Some(instructions);
66 self
67 }
68
69 pub fn add_instruction(mut self, instruction: SolanaInstruction) -> Self {
71 let mut instructions = self.instructions.unwrap_or_default();
72 instructions.push(instruction);
73 self.instructions = Some(instructions);
74 self
75 }
76
77 pub fn add_parsed_instruction(
79 mut self,
80 program_id: &str,
81 instruction_type: &str,
82 info: serde_json::Value,
83 ) -> Self {
84 let instruction = SolanaInstruction {
85 program_id_index: 0,
86 accounts: vec![],
87 data: String::new(),
88 parsed: Some(SolanaParsedInstruction {
89 instruction_type: instruction_type.to_string(),
90 info,
91 }),
92 program: Some(program_id.to_string()),
93 program_id: Some(program_id.to_string()),
94 };
95 let mut instructions = self.instructions.unwrap_or_default();
96 instructions.push(instruction);
97 self.instructions = Some(instructions);
98 self
99 }
100
101 pub fn fee(mut self, fee: u64) -> Self {
103 self.fee = Some(fee);
104 self
105 }
106
107 pub fn err(mut self, err: serde_json::Value) -> Self {
109 self.err = Some(err);
110 self
111 }
112
113 pub fn pre_balances(mut self, pre_balances: Vec<u64>) -> Self {
115 self.pre_balances = Some(pre_balances);
116 self
117 }
118
119 pub fn post_balances(mut self, post_balances: Vec<u64>) -> Self {
121 self.post_balances = Some(post_balances);
122 self
123 }
124
125 pub fn log_messages(mut self, log_messages: Vec<String>) -> Self {
127 self.log_messages = Some(log_messages);
128 self
129 }
130
131 pub fn add_log_message(mut self, log_message: &str) -> Self {
133 let mut log_messages = self.log_messages.unwrap_or_default();
134 log_messages.push(log_message.to_string());
135 self.log_messages = Some(log_messages);
136 self
137 }
138
139 pub fn compute_units_consumed(mut self, compute_units: u64) -> Self {
141 self.compute_units_consumed = Some(compute_units);
142 self
143 }
144
145 pub fn build(self) -> SolanaTransaction {
147 let transaction_info = SolanaTransactionInfo {
148 signature: self.signature.unwrap_or_else(|| {
149 "5wHu1qwD7q5ifaN5nwdcDqNFF53GJqa7nLp2BLPASe7FPYoWZL3YBrJmVL6nrMtwKjNFin1F"
150 .to_string()
151 }),
152 slot: self.slot.unwrap_or(123456789),
153 block_time: self.block_time,
154 transaction: SolanaTransactionMessage {
155 account_keys: self.account_keys.unwrap_or_else(|| {
156 vec![
157 "11111111111111111111111111111111".to_string(),
158 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".to_string(),
159 ]
160 }),
161 recent_blockhash: self
162 .recent_blockhash
163 .unwrap_or_else(|| "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZAMdL4VZHirAn".to_string()),
164 instructions: self.instructions.unwrap_or_default(),
165 address_table_lookups: vec![],
166 },
167 meta: Some(SolanaTransactionMeta {
168 err: self.err,
169 fee: self.fee.unwrap_or(5000),
170 pre_balances: self.pre_balances.unwrap_or_else(|| vec![1000000, 500000]),
171 post_balances: self.post_balances.unwrap_or_else(|| vec![995000, 500000]),
172 pre_token_balances: vec![],
173 post_token_balances: vec![],
174 inner_instructions: vec![],
175 log_messages: self.log_messages.unwrap_or_default(),
176 compute_units_consumed: self.compute_units_consumed,
177 loaded_addresses: None,
178 }),
179 };
180
181 SolanaTransaction::from(transaction_info)
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_default_transaction() {
191 let tx = TransactionBuilder::new().build();
192
193 assert_eq!(
194 tx.signature(),
195 "5wHu1qwD7q5ifaN5nwdcDqNFF53GJqa7nLp2BLPASe7FPYoWZL3YBrJmVL6nrMtwKjNFin1F"
196 );
197 assert_eq!(tx.slot(), 123456789);
198 }
199
200 #[test]
201 fn test_custom_transaction() {
202 let tx = TransactionBuilder::new()
203 .signature("custom_signature")
204 .slot(999)
205 .block_time(1234567890)
206 .fee(10000)
207 .build();
208
209 assert_eq!(tx.signature(), "custom_signature");
210 assert_eq!(tx.slot(), 999);
211 }
212
213 #[test]
214 fn test_transaction_with_instructions() {
215 let tx = TransactionBuilder::new()
216 .add_parsed_instruction(
217 "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
218 "transfer",
219 serde_json::json!({
220 "source": "source_account",
221 "destination": "dest_account",
222 "amount": "1000000"
223 }),
224 )
225 .build();
226
227 assert!(!tx.transaction.instructions.is_empty());
228 }
229
230 #[test]
231 fn test_transaction_with_logs() {
232 let tx = TransactionBuilder::new()
233 .log_messages(vec![
234 "Program log: Instruction: Transfer".to_string(),
235 "Program log: Success".to_string(),
236 ])
237 .build();
238
239 let meta = tx.meta.as_ref().unwrap();
240 assert_eq!(meta.log_messages.len(), 2);
241 }
242}