multiversx_chain_vm/vm_hooks/vh_handler/
vh_send.rs

1use crate::{
2    chain_core::builtin_func_names::{
3        ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME, ESDT_TRANSFER_FUNC_NAME,
4        UPGRADE_CONTRACT_FUNC_NAME,
5    },
6    tx_mock::{AsyncCallTxData, Promise, TxFunctionName, TxTokenTransfer},
7    types::{top_encode_big_uint, top_encode_u64, RawHandle, VMAddress, VMCodeMetadata},
8    vm_err_msg,
9    vm_hooks::VMHooksHandlerSource,
10};
11use num_traits::Zero;
12
13fn append_endpoint_name_and_args(
14    args: &mut Vec<Vec<u8>>,
15    endpoint_name: TxFunctionName,
16    arg_buffer: Vec<Vec<u8>>,
17) {
18    if !endpoint_name.is_empty() {
19        args.push(endpoint_name.into_bytes());
20        args.extend(arg_buffer);
21    }
22}
23
24pub trait VMHooksSend: VMHooksHandlerSource {
25    fn perform_transfer_execute_esdt(
26        &self,
27        to: VMAddress,
28        token: Vec<u8>,
29        amount: num_bigint::BigUint,
30        _gas_limit: u64,
31        func_name: TxFunctionName,
32        arguments: Vec<Vec<u8>>,
33    ) {
34        let mut args = vec![token, amount.to_bytes_be()];
35        append_endpoint_name_and_args(&mut args, func_name, arguments);
36
37        self.perform_transfer_execute(
38            to,
39            num_bigint::BigUint::zero(),
40            ESDT_TRANSFER_FUNC_NAME.into(),
41            args,
42        );
43    }
44
45    #[allow(clippy::too_many_arguments)]
46    fn perform_transfer_execute_nft(
47        &self,
48        to: VMAddress,
49        token: Vec<u8>,
50        nonce: u64,
51        amount: num_bigint::BigUint,
52        _gas_limit: u64,
53        func_name: TxFunctionName,
54        arguments: Vec<Vec<u8>>,
55    ) {
56        let contract_address = self.current_address().clone();
57
58        let mut args = vec![
59            token,
60            top_encode_u64(nonce),
61            top_encode_big_uint(&amount),
62            to.to_vec(),
63        ];
64
65        append_endpoint_name_and_args(&mut args, func_name, arguments);
66
67        self.perform_transfer_execute(
68            contract_address,
69            num_bigint::BigUint::zero(),
70            ESDT_NFT_TRANSFER_FUNC_NAME.into(),
71            args,
72        );
73    }
74
75    fn perform_transfer_execute_multi(
76        &self,
77        to: VMAddress,
78        payments: Vec<TxTokenTransfer>,
79        _gas_limit: u64,
80        endpoint_name: TxFunctionName,
81        arguments: Vec<Vec<u8>>,
82    ) {
83        let contract_address = self.current_address().clone();
84
85        let mut args = vec![to.to_vec(), top_encode_u64(payments.len() as u64)];
86
87        for payment in payments.into_iter() {
88            let token_bytes = payment.token_identifier;
89            args.push(token_bytes);
90            let nonce_bytes = top_encode_u64(payment.nonce);
91            args.push(nonce_bytes);
92            let amount_bytes = top_encode_big_uint(&payment.value);
93            args.push(amount_bytes);
94        }
95
96        append_endpoint_name_and_args(&mut args, endpoint_name, arguments);
97
98        self.perform_transfer_execute(
99            contract_address,
100            num_bigint::BigUint::zero(),
101            ESDT_MULTI_TRANSFER_FUNC_NAME.into(),
102            args,
103        );
104    }
105
106    fn perform_upgrade_contract(
107        &self,
108        to: VMAddress,
109        egld_value: num_bigint::BigUint,
110        contract_code: Vec<u8>,
111        code_metadata: VMCodeMetadata,
112        args: Vec<Vec<u8>>,
113    ) -> ! {
114        let mut arguments = vec![contract_code, code_metadata.to_vec()];
115        arguments.extend(args);
116        self.perform_async_call(to, egld_value, UPGRADE_CONTRACT_FUNC_NAME.into(), arguments)
117    }
118
119    fn transfer_value_execute(
120        &self,
121        to_handle: RawHandle,
122        amount_handle: RawHandle,
123        _gas_limit: u64,
124        endpoint_name_handle: RawHandle,
125        arg_buffer_handle: RawHandle,
126    ) -> Result<(), &'static [u8]> {
127        let recipient = self.m_types_lock().mb_to_address(to_handle);
128        let egld_value = self.m_types_lock().bu_get(amount_handle);
129        let endpoint_name = self
130            .m_types_lock()
131            .mb_to_function_name(endpoint_name_handle);
132        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
133
134        self.perform_transfer_execute(recipient, egld_value, endpoint_name, arg_buffer);
135
136        Ok(())
137    }
138
139    fn multi_transfer_esdt_nft_execute(
140        &self,
141        to_handle: RawHandle,
142        payments_handle: RawHandle,
143        gas_limit: u64,
144        endpoint_name_handle: RawHandle,
145        arg_buffer_handle: RawHandle,
146    ) {
147        let to = self.m_types_lock().mb_to_address(to_handle);
148        let payments = self
149            .m_types_lock()
150            .mb_get_vec_of_esdt_payments(payments_handle);
151        let endpoint_name = self
152            .m_types_lock()
153            .mb_to_function_name(endpoint_name_handle);
154        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
155
156        if payments.len() == 1 {
157            let payment = payments[0].clone();
158            if payment.nonce == 0 {
159                self.perform_transfer_execute_esdt(
160                    to,
161                    payment.token_identifier,
162                    payment.value,
163                    gas_limit,
164                    endpoint_name,
165                    arg_buffer,
166                )
167            } else {
168                self.perform_transfer_execute_nft(
169                    to,
170                    payment.token_identifier,
171                    payment.nonce,
172                    payment.value,
173                    gas_limit,
174                    endpoint_name,
175                    arg_buffer,
176                )
177            }
178        } else {
179            self.perform_transfer_execute_multi(to, payments, gas_limit, endpoint_name, arg_buffer)
180        }
181    }
182
183    fn async_call_raw(
184        &self,
185        to_handle: RawHandle,
186        egld_value_handle: RawHandle,
187        endpoint_name_handle: RawHandle,
188        arg_buffer_handle: RawHandle,
189    ) -> ! {
190        let to = self.m_types_lock().mb_to_address(to_handle);
191        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
192        let endpoint_name = self
193            .m_types_lock()
194            .mb_to_function_name(endpoint_name_handle);
195        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
196
197        self.perform_async_call(to, egld_value, endpoint_name, arg_buffer)
198    }
199
200    #[allow(clippy::too_many_arguments)]
201    fn create_async_call_raw(
202        &self,
203        to_handle: RawHandle,
204        egld_value_handle: RawHandle,
205        endpoint_name_handle: RawHandle,
206        arg_buffer_handle: RawHandle,
207        success_callback: &[u8],
208        error_callback: &[u8],
209        _gas: u64,
210        _extra_gas_for_callback: u64,
211        callback_closure_handle: RawHandle,
212    ) {
213        let contract_address = self.current_address().clone();
214        let to = self.m_types_lock().mb_to_address(to_handle);
215        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
216        let endpoint_name = self
217            .m_types_lock()
218            .mb_to_function_name(endpoint_name_handle);
219        if endpoint_name.is_empty() {
220            // immitating the behavior of the VM
221            // TODO: lift limitation from the VM, then also remove this condition here
222            self.vm_error(vm_err_msg::PROMISES_TOKENIZE_FAILED);
223        }
224        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
225        let tx_hash = self.tx_hash();
226        let callback_closure_data = self.m_types_lock().mb_get(callback_closure_handle).to_vec();
227
228        let call = AsyncCallTxData {
229            from: contract_address,
230            to,
231            call_value: egld_value,
232            endpoint_name,
233            arguments: arg_buffer,
234            tx_hash,
235        };
236
237        let promise = Promise {
238            call,
239            success_callback: success_callback.into(),
240            error_callback: error_callback.into(),
241            callback_closure_data,
242        };
243
244        let mut tx_result = self.result_lock();
245        tx_result.all_calls.push(promise.call.clone());
246        tx_result.pending_calls.promises.push(promise);
247    }
248
249    #[allow(clippy::too_many_arguments)]
250    fn deploy_contract(
251        &self,
252        _gas: u64,
253        egld_value_handle: RawHandle,
254        code_handle: RawHandle,
255        code_metadata_handle: RawHandle,
256        arg_buffer_handle: RawHandle,
257        new_address_handle: RawHandle,
258        result_handle: RawHandle,
259    ) {
260        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
261        let code = self.m_types_lock().mb_get(code_handle).to_vec();
262        let code_metadata = self
263            .m_types_lock()
264            .mb_to_code_metadata(code_metadata_handle);
265        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
266
267        let (new_address, result) =
268            self.perform_deploy(egld_value, code, code_metadata, arg_buffer);
269
270        self.m_types_lock()
271            .mb_set(new_address_handle, new_address.to_vec());
272        self.m_types_lock()
273            .mb_set_vec_of_bytes(result_handle, result);
274    }
275
276    #[allow(clippy::too_many_arguments)]
277    fn deploy_from_source_contract(
278        &self,
279        _gas: u64,
280        egld_value_handle: RawHandle,
281        source_contract_address_handle: RawHandle,
282        code_metadata_handle: RawHandle,
283        arg_buffer_handle: RawHandle,
284        new_address_handle: RawHandle,
285        result_handle: RawHandle,
286    ) {
287        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
288        let source_contract_address = self
289            .m_types_lock()
290            .mb_to_address(source_contract_address_handle);
291        let source_contract_code = self.account_code(&source_contract_address);
292        let code_metadata = self
293            .m_types_lock()
294            .mb_to_code_metadata(code_metadata_handle);
295        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
296
297        let (new_address, result) =
298            self.perform_deploy(egld_value, source_contract_code, code_metadata, arg_buffer);
299
300        self.m_types_lock()
301            .mb_set(new_address_handle, new_address.to_vec());
302        self.m_types_lock()
303            .mb_set_vec_of_bytes(result_handle, result);
304    }
305
306    fn upgrade_from_source_contract(
307        &self,
308        sc_address_handle: RawHandle,
309        _gas: u64,
310        egld_value_handle: RawHandle,
311        source_contract_address_handle: RawHandle,
312        code_metadata_handle: RawHandle,
313        arg_buffer_handle: RawHandle,
314    ) {
315        let to = self.m_types_lock().mb_to_address(sc_address_handle);
316        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
317        let source_contract_address = self
318            .m_types_lock()
319            .mb_to_address(source_contract_address_handle);
320        let source_contract_code = self.account_code(&source_contract_address);
321        let code_metadata = self
322            .m_types_lock()
323            .mb_to_code_metadata(code_metadata_handle);
324        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
325
326        self.perform_upgrade_contract(
327            to,
328            egld_value,
329            source_contract_code,
330            code_metadata,
331            arg_buffer,
332        )
333    }
334
335    fn upgrade_contract(
336        &self,
337        sc_address_handle: RawHandle,
338        _gas: u64,
339        egld_value_handle: RawHandle,
340        code_handle: RawHandle,
341        code_metadata_handle: RawHandle,
342        arg_buffer_handle: RawHandle,
343    ) {
344        let to = self.m_types_lock().mb_to_address(sc_address_handle);
345        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
346        let code = self.m_types_lock().mb_get(code_handle).to_vec();
347        let code_metadata = self
348            .m_types_lock()
349            .mb_to_code_metadata(code_metadata_handle);
350        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
351
352        self.perform_upgrade_contract(to, egld_value, code, code_metadata, arg_buffer)
353    }
354
355    fn execute_on_dest_context_raw(
356        &self,
357        _gas: u64,
358        to_handle: RawHandle,
359        egld_value_handle: RawHandle,
360        endpoint_name_handle: RawHandle,
361        arg_buffer_handle: RawHandle,
362        result_handle: RawHandle,
363    ) {
364        let to = self.m_types_lock().mb_to_address(to_handle);
365        let egld_value = self.m_types_lock().bu_get(egld_value_handle);
366        let endpoint_name = self
367            .m_types_lock()
368            .mb_to_function_name(endpoint_name_handle);
369        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
370
371        let result =
372            self.perform_execute_on_dest_context(to, egld_value, endpoint_name, arg_buffer);
373
374        self.m_types_lock()
375            .mb_set_vec_of_bytes(result_handle, result);
376    }
377
378    fn execute_on_dest_context_readonly_raw(
379        &self,
380        _gas: u64,
381        to_handle: RawHandle,
382        endpoint_name_handle: RawHandle,
383        arg_buffer_handle: RawHandle,
384        result_handle: RawHandle,
385    ) {
386        let to = self.m_types_lock().mb_to_address(to_handle);
387        let endpoint_name = self
388            .m_types_lock()
389            .mb_to_function_name(endpoint_name_handle);
390        let arg_buffer = self.m_types_lock().mb_get_vec_of_bytes(arg_buffer_handle);
391
392        let result = self.perform_execute_on_dest_context_readonly(to, endpoint_name, arg_buffer);
393
394        self.m_types_lock()
395            .mb_set_vec_of_bytes(result_handle, result);
396    }
397
398    fn clean_return_data(&self) {
399        let mut tx_result = self.result_lock();
400        tx_result.result_values.clear();
401    }
402
403    fn delete_from_return_data(&self, index: usize) {
404        let mut tx_result = self.result_lock();
405        if index > tx_result.result_values.len() {
406            return;
407        }
408
409        let _ = tx_result.result_values.remove(index);
410    }
411}