multiversx_chain_vm/host/context/
tx_result.rs

1use 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    /// Calls that need to be executed.
20    ///
21    /// Structure is emptied as soon as async calls are executed.
22    pub pending_calls: TxResultCalls,
23
24    /// All async calls launched from the tx (legacy async, promises, transfer-execute).
25    ///
26    /// Is never cleared of its contents.
27    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}