multiversx_sc_scenario/scenario/model/step/
sc_call_step.rs

1use multiversx_sc::types::H256;
2
3use crate::{
4    api::StaticApi,
5    scenario::model::{AddressValue, BigUintValue, BytesValue, TxCall, TxESDT, TxExpect, U64Value},
6    scenario_model::TxResponse,
7};
8
9use crate::multiversx_sc::types::ManagedArgBuffer;
10
11#[derive(Debug, Clone)]
12pub struct ScCallStep {
13    pub id: String,
14    pub tx_id: Option<String>,
15    pub explicit_tx_hash: Option<H256>,
16    pub comment: Option<String>,
17    pub tx: Box<TxCall>,
18    pub expect: Option<TxExpect>,
19    pub response: Option<TxResponse>,
20}
21
22impl Default for ScCallStep {
23    fn default() -> Self {
24        Self {
25            id: Default::default(),
26            tx_id: Default::default(),
27            explicit_tx_hash: Default::default(),
28            comment: Default::default(),
29            tx: Default::default(),
30            expect: Some(TxExpect::ok()),
31            response: Default::default(),
32        }
33    }
34}
35
36impl ScCallStep {
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    pub fn from<A>(mut self, address: A) -> Self
42    where
43        AddressValue: From<A>,
44    {
45        self.tx.from = AddressValue::from(address);
46        self
47    }
48
49    pub fn to<A>(mut self, address: A) -> Self
50    where
51        AddressValue: From<A>,
52    {
53        self.tx.to = AddressValue::from(address);
54        self
55    }
56
57    pub fn egld_value<A>(mut self, amount: A) -> Self
58    where
59        BigUintValue: From<A>,
60    {
61        if !self.tx.esdt_value.is_empty() && self.tx.egld_value.value > 0u32.into() {
62            panic!("Cannot transfer both EGLD and ESDT");
63        }
64
65        self.tx.egld_value = BigUintValue::from(amount);
66        self
67    }
68
69    pub fn esdt_transfer<T, N, A>(mut self, token_id: T, token_nonce: N, amount: A) -> Self
70    where
71        BytesValue: From<T>,
72        U64Value: From<N>,
73        BigUintValue: From<A>,
74    {
75        if self.tx.egld_value.value > 0u32.into() {
76            panic!("Cannot transfer both EGLD and ESDT");
77        }
78
79        self.tx.esdt_value.push(TxESDT {
80            esdt_token_identifier: BytesValue::from(token_id),
81            nonce: U64Value::from(token_nonce),
82            esdt_value: BigUintValue::from(amount),
83        });
84
85        self
86    }
87
88    pub fn multi_esdt_transfer<T>(mut self, tokens: T) -> Self
89    where
90        T: IntoIterator<Item = TxESDT>,
91    {
92        if self.tx.egld_value.value > 0u32.into() {
93            panic!("Cannot transfer both EGLD and ESDT");
94        }
95
96        self.tx.esdt_value.extend(tokens);
97
98        self
99    }
100
101    pub fn function(mut self, expr: &str) -> Self {
102        self.tx.function = expr.to_string();
103        self
104    }
105
106    pub fn tx_hash<T>(mut self, tx_hash_expr: T) -> Self
107    where
108        H256: From<T>,
109    {
110        self.explicit_tx_hash = Some(tx_hash_expr.into());
111        self
112    }
113
114    pub fn argument<A>(mut self, expr: A) -> Self
115    where
116        BytesValue: From<A>,
117    {
118        self.tx.arguments.push(BytesValue::from(expr));
119        self
120    }
121
122    pub fn gas_limit<V>(mut self, value: V) -> Self
123    where
124        U64Value: From<V>,
125    {
126        self.tx.gas_limit = U64Value::from(value);
127        self
128    }
129
130    /// Adds a custom expect section to the tx.
131    pub fn expect(mut self, expect: TxExpect) -> Self {
132        self.expect = Some(expect);
133        self
134    }
135
136    /// Explicitly states that no tx expect section should be added and no checks should be performed.
137    ///
138    /// Note: by default a basic `TxExpect::ok()` is added, which checks that status is 0 and nothing else.
139    pub fn no_expect(mut self) -> Self {
140        self.expect = None;
141        self
142    }
143
144    /// Unwraps the response, if available.
145    pub fn response(&self) -> &TxResponse {
146        self.response
147            .as_ref()
148            .expect("SC call response not yet available")
149    }
150
151    pub fn save_response(&mut self, mut tx_response: TxResponse) {
152        if let Some(expect) = &mut self.expect {
153            if expect.build_from_response {
154                expect.update_from_response(&tx_response)
155            }
156        }
157        if tx_response.tx_hash.is_none() {
158            tx_response.tx_hash = self
159                .explicit_tx_hash
160                .as_ref()
161                .map(|vm_hash| vm_hash.as_array().into());
162        }
163        self.response = Some(tx_response);
164    }
165}
166
167impl AsMut<ScCallStep> for ScCallStep {
168    fn as_mut(&mut self) -> &mut ScCallStep {
169        self
170    }
171}
172
173pub fn convert_call_args(arg_buffer: &ManagedArgBuffer<StaticApi>) -> Vec<String> {
174    arg_buffer
175        .to_raw_args_vec()
176        .iter()
177        .map(|arg| format!("0x{}", hex::encode(arg)))
178        .collect()
179}