1use stellar_rpc_client::GetTransactionResponse;
2use stellar_xdr::curr::{ScVal, SorobanTransactionMeta, TransactionMeta, TransactionMetaV3};
3
4use crate::SorobanHelperError;
5
6#[derive(Debug, Clone)]
8pub struct SorobanTransactionResponse {
9 pub response: GetTransactionResponse,
11}
12
13impl From<GetTransactionResponse> for SorobanTransactionResponse {
14 fn from(response: GetTransactionResponse) -> Self {
15 Self { response }
16 }
17}
18
19impl SorobanTransactionResponse {
20 pub fn new(response: GetTransactionResponse) -> Self {
22 Self { response }
23 }
24
25 pub fn get_return_value(&self) -> Result<ScVal, SorobanHelperError> {
35 let result_meta = self.response.result_meta.as_ref().ok_or_else(|| {
37 SorobanHelperError::InvalidArgument("Transaction metadata not available".to_string())
38 })?;
39
40 match result_meta {
42 TransactionMeta::V3(meta_v3) => self.extract_soroban_return_value(meta_v3),
43 _ => Err(SorobanHelperError::InvalidArgument(
44 "Transaction metadata is not in V3 format (not a Soroban transaction)".to_string(),
45 )),
46 }
47 }
48
49 pub fn get_events(&self) -> Result<Vec<stellar_xdr::curr::ContractEvent>, SorobanHelperError> {
59 let result_meta = self.response.result_meta.as_ref().ok_or_else(|| {
61 SorobanHelperError::InvalidArgument("Transaction metadata not available".to_string())
62 })?;
63
64 match result_meta {
66 TransactionMeta::V3(meta_v3) => self.extract_soroban_events(meta_v3),
67 _ => Err(SorobanHelperError::InvalidArgument(
68 "Transaction metadata is not in V3 format (not a Soroban transaction)".to_string(),
69 )),
70 }
71 }
72
73 fn extract_soroban_return_value(
75 &self,
76 meta_v3: &TransactionMetaV3,
77 ) -> Result<ScVal, SorobanHelperError> {
78 let soroban_meta = meta_v3.soroban_meta.as_ref().ok_or_else(|| {
80 SorobanHelperError::InvalidArgument("Soroban metadata not available".to_string())
81 })?;
82
83 Ok(soroban_meta.return_value.clone())
85 }
86
87 fn extract_soroban_events(
89 &self,
90 meta_v3: &TransactionMetaV3,
91 ) -> Result<Vec<stellar_xdr::curr::ContractEvent>, SorobanHelperError> {
92 let soroban_meta = meta_v3.soroban_meta.as_ref().ok_or_else(|| {
94 SorobanHelperError::InvalidArgument("Soroban metadata not available".to_string())
95 })?;
96
97 let events = soroban_meta.events.to_vec();
99 Ok(events)
100 }
101
102 pub fn get_soroban_meta(&self) -> Result<SorobanTransactionMeta, SorobanHelperError> {
112 let result_meta = self.response.result_meta.as_ref().ok_or_else(|| {
114 SorobanHelperError::InvalidArgument("Transaction metadata not available".to_string())
115 })?;
116
117 match result_meta {
119 TransactionMeta::V3(meta_v3) => {
120 let soroban_meta = meta_v3.soroban_meta.as_ref().ok_or_else(|| {
122 SorobanHelperError::InvalidArgument(
123 "Soroban metadata not available".to_string(),
124 )
125 })?;
126
127 Ok(soroban_meta.clone())
129 }
130 _ => Err(SorobanHelperError::InvalidArgument(
131 "Transaction metadata is not in V3 format (not a Soroban transaction)".to_string(),
132 )),
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use crate::mock::create_mock_contract_event;
140
141 use super::*;
142 use stellar_xdr::curr::{
143 ContractEvent, ExtensionPoint, LedgerEntryChanges, SorobanTransactionMetaExt,
144 TransactionResult, TransactionResultExt, TransactionResultResult, VecM,
145 };
146
147 #[test]
148 fn test_get_return_value_success() {
149 let response = create_mock_response(Some(ScVal::U32(42)));
151 let soroban_response = SorobanTransactionResponse::new(response);
152
153 let return_value = soroban_response.get_return_value().unwrap();
155 assert_eq!(return_value, ScVal::U32(42));
156 }
157
158 #[test]
159 fn test_get_return_value_no_meta() {
160 let response = GetTransactionResponse {
162 status: "success".to_string(),
163 envelope: None,
164 result: None,
165 result_meta: None,
166 ledger: None,
167 events: stellar_rpc_client::GetTransactionEvents {
168 contract_events: vec![],
169 diagnostic_events: vec![],
170 transaction_events: vec![],
171 },
172 };
173 let soroban_response = SorobanTransactionResponse::new(response);
174
175 let result = soroban_response.get_return_value();
177 assert!(result.is_err());
178 }
179
180 fn create_mock_response(return_value: Option<ScVal>) -> GetTransactionResponse {
182 let soroban_meta = SorobanTransactionMeta {
184 ext: SorobanTransactionMetaExt::V0,
185 events: VecM::default(),
186 return_value: return_value.unwrap_or(ScVal::Void),
187 diagnostic_events: VecM::default(),
188 };
189
190 let meta_v3 = TransactionMetaV3 {
192 ext: ExtensionPoint::V0,
193 tx_changes_before: LedgerEntryChanges::default(),
194 operations: VecM::default(),
195 tx_changes_after: LedgerEntryChanges::default(),
196 soroban_meta: Some(soroban_meta),
197 };
198
199 let transaction_result = TransactionResult {
200 fee_charged: 0,
201 result: TransactionResultResult::TxSuccess(VecM::default()),
202 ext: TransactionResultExt::V0,
203 };
204
205 GetTransactionResponse {
207 status: "success".to_string(),
208 envelope: None,
209 result: Some(transaction_result),
210 result_meta: Some(TransactionMeta::V3(meta_v3)),
211 ledger: Some(123456), events: stellar_rpc_client::GetTransactionEvents {
213 contract_events: vec![],
214 diagnostic_events: vec![],
215 transaction_events: vec![],
216 },
217 }
218 }
219
220 #[test]
221 fn test_get_events_success() {
222 let event1 = create_mock_contract_event();
224 let event2 = create_mock_contract_event();
225
226 let events: VecM<ContractEvent> = vec![event1.clone(), event2.clone()].try_into().unwrap();
228
229 let response = create_mock_response_with_events(Some(ScVal::Void), events);
231 let soroban_response = SorobanTransactionResponse::new(response);
232
233 let extracted_events = soroban_response.get_events().unwrap();
235 assert_eq!(extracted_events.len(), 2);
236 }
237
238 #[test]
239 fn test_get_events_no_meta() {
240 let response = GetTransactionResponse {
242 status: "success".to_string(),
243 envelope: None,
244 result: None,
245 result_meta: None,
246 ledger: None,
247 events: stellar_rpc_client::GetTransactionEvents {
248 contract_events: vec![],
249 diagnostic_events: vec![],
250 transaction_events: vec![],
251 },
252 };
253 let soroban_response = SorobanTransactionResponse::new(response);
254
255 let result = soroban_response.get_events();
257 assert!(result.is_err());
258 }
259
260 #[test]
261 fn test_get_events_empty() {
262 let response = create_mock_response(Some(ScVal::Void));
264 let soroban_response = SorobanTransactionResponse::new(response);
265
266 let events = soroban_response.get_events().unwrap();
268 assert_eq!(events.len(), 0);
269 }
270
271 #[test]
272 fn test_get_soroban_meta_success() {
273 let return_value = ScVal::U32(42);
275 let response = create_mock_response(Some(return_value.clone()));
276 let soroban_response = SorobanTransactionResponse::new(response);
277
278 let soroban_meta = soroban_response.get_soroban_meta().unwrap();
280
281 assert_eq!(soroban_meta.return_value, return_value);
283 assert_eq!(soroban_meta.events.len(), 0);
284 assert_eq!(soroban_meta.diagnostic_events.len(), 0);
285 assert!(matches!(soroban_meta.ext, SorobanTransactionMetaExt::V0));
286 }
287
288 #[test]
289 fn test_get_soroban_meta_no_meta() {
290 let response = GetTransactionResponse {
292 status: "success".to_string(),
293 envelope: None,
294 result: None,
295 result_meta: None,
296 ledger: None,
297 events: stellar_rpc_client::GetTransactionEvents {
298 contract_events: vec![],
299 diagnostic_events: vec![],
300 transaction_events: vec![],
301 },
302 };
303 let soroban_response = SorobanTransactionResponse::new(response);
304
305 let result = soroban_response.get_soroban_meta();
307 assert!(result.is_err());
308 }
309
310 fn create_mock_response_with_events(
312 return_value: Option<ScVal>,
313 events: VecM<stellar_xdr::curr::ContractEvent>,
314 ) -> GetTransactionResponse {
315 let soroban_meta = SorobanTransactionMeta {
317 ext: SorobanTransactionMetaExt::V0,
318 events,
319 return_value: return_value.unwrap_or(ScVal::Void),
320 diagnostic_events: VecM::default(),
321 };
322
323 let meta_v3 = TransactionMetaV3 {
325 ext: ExtensionPoint::V0,
326 tx_changes_before: LedgerEntryChanges::default(),
327 operations: VecM::default(),
328 tx_changes_after: LedgerEntryChanges::default(),
329 soroban_meta: Some(soroban_meta),
330 };
331
332 let transaction_result = TransactionResult {
333 fee_charged: 0,
334 result: TransactionResultResult::TxSuccess(VecM::default()),
335 ext: TransactionResultExt::V0,
336 };
337
338 GetTransactionResponse {
340 status: "success".to_string(),
341 envelope: None,
342 result: Some(transaction_result),
343 result_meta: Some(TransactionMeta::V3(meta_v3)),
344 ledger: Some(123456), events: stellar_rpc_client::GetTransactionEvents {
346 contract_events: vec![],
347 diagnostic_events: vec![],
348 transaction_events: vec![],
349 },
350 }
351 }
352}