multiversx_chain_vm/host/context/
tx_result.rs1use std::fmt;
2
3use multiversx_chain_core::types::ReturnCode;
4
5use crate::vm_err_msg;
6
7use super::{AsyncCallTxData, GasUsed, TxLog, TxPanic, TxResultCalls};
8
9#[derive(Clone, Debug)]
10#[must_use]
11pub struct TxResult {
12 pub result_status: ReturnCode,
13 pub result_message: String,
14 pub result_values: Vec<Vec<u8>>,
15 pub result_logs: Vec<TxLog>,
16
17 pub gas_used: GasUsed,
18
19 pub pending_calls: TxResultCalls,
23
24 pub all_calls: Vec<AsyncCallTxData>,
28}
29
30impl Default for TxResult {
31 fn default() -> Self {
32 TxResult {
33 result_status: ReturnCode::Success,
34 result_message: String::new(),
35 result_values: Vec::new(),
36 result_logs: Vec::new(),
37 gas_used: GasUsed::Unknown,
38 pending_calls: TxResultCalls::empty(),
39 all_calls: Vec::new(),
40 }
41 }
42}
43
44impl TxResult {
45 pub fn empty() -> TxResult {
46 TxResult::default()
47 }
48
49 pub fn print(&self) {
50 println!("{self}");
51 }
52
53 pub fn from_panic_obj(panic_obj: &TxPanic) -> Self {
54 TxResult::from_error(panic_obj.status, &panic_obj.message)
55 }
56
57 pub fn from_panic_string(s: &str) -> Self {
58 TxResult::from_error(ReturnCode::UserError, s)
59 }
60
61 pub fn from_unknown_panic() -> Self {
62 Self::from_panic_string("")
63 }
64
65 pub fn from_error<S>(return_code: ReturnCode, result_message: S) -> Self
66 where
67 S: Into<String>,
68 {
69 TxResult {
70 result_status: return_code,
71 result_message: result_message.into(),
72 ..Default::default()
73 }
74 }
75
76 pub fn from_vm_error<S>(result_message: S) -> Self
77 where
78 S: Into<String>,
79 {
80 TxResult::from_error(ReturnCode::ExecutionFailed, result_message)
81 }
82
83 pub fn from_function_not_found() -> Self {
84 TxResult::from_error(ReturnCode::FunctionNotFound, vm_err_msg::FUNCTION_NOT_FOUND)
85 }
86
87 pub fn merge_after_sync_call(&mut self, sync_call_result: &TxResult) {
88 self.result_values
89 .extend_from_slice(sync_call_result.result_values.as_slice());
90 self.result_logs
91 .extend_from_slice(sync_call_result.result_logs.as_slice());
92 if let Some(sync_result_async) = &sync_call_result.pending_calls.async_call {
93 assert!(
94 self.pending_calls.async_call.is_none(),
95 "Multiple async calls not supported"
96 );
97 self.pending_calls.async_call = Some(sync_result_async.clone());
98 }
99 }
100
101 pub fn assert_ok(&self) {
102 assert!(
103 self.result_status.is_success(),
104 "Tx success expected, but failed. Status: {}, message: \"{}\"",
105 self.result_status,
106 self.result_message.as_str()
107 );
108 }
109
110 pub fn assert_error(&self, expected_status: u64, expected_message: &str) {
111 assert!(
112 self.result_message.as_str() == expected_message,
113 "Tx error message mismatch. Want status {}, message \"{}\". Have status {}, message \"{}\"",
114 expected_status,
115 expected_message,
116 self.result_status,
117 self.result_message.as_str()
118 );
119 assert!(
120 self.result_status.as_u64() == expected_status,
121 "Tx error status mismatch. Want status {}, message \"{}\". Have status {}, message \"{}\"",
122 expected_status,
123 expected_message,
124 self.result_status,
125 self.result_message.as_str()
126 );
127 }
128
129 pub fn assert_user_error(&self, expected_message: &str) {
130 self.assert_error(ReturnCode::UserError.as_u64(), expected_message);
131 }
132}
133
134impl fmt::Display for TxResult {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 let results_hex: Vec<String> = self
137 .result_values
138 .iter()
139 .map(|r| format!("0x{}", hex::encode(r)))
140 .collect();
141 write!(
142 f,
143 "TxResult {{\n\tresult_status: {},\n\tresult_values:{results_hex:?}\n}}",
144 self.result_status
145 )
146 }
147}
148
149impl TxResult {
150 pub fn result_values_to_string(&self) -> String {
151 result_values_to_string(&self.result_values)
152 }
153}
154
155pub fn result_values_to_string(values: &[Vec<u8>]) -> String {
156 itertools::join(
157 values.iter().map(|val| format!("0x{}", hex::encode(val))),
158 ", ",
159 )
160}