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 host::{
7 context::{AsyncCallTxData, Promise, TxFunctionName, TxTokenTransfer},
8 vm_hooks::{vh_early_exit::early_exit_vm_error, VMHooksContext},
9 },
10 types::{top_encode_big_uint, top_encode_u64, RawHandle, VMAddress, VMCodeMetadata},
11 vm_err_msg,
12};
13use multiversx_chain_core::types::ReturnCode;
14use multiversx_chain_vm_executor::VMHooksEarlyExit;
15use num_traits::Zero;
16
17use super::VMHooksHandler;
18
19fn append_endpoint_name_and_args(
20 args: &mut Vec<Vec<u8>>,
21 endpoint_name: TxFunctionName,
22 arg_buffer: Vec<Vec<u8>>,
23) {
24 if !endpoint_name.is_empty() {
25 args.push(endpoint_name.into_bytes());
26 args.extend(arg_buffer);
27 }
28}
29
30impl<C: VMHooksContext> VMHooksHandler<C> {
31 fn perform_transfer_execute_esdt(
32 &mut self,
33 to: VMAddress,
34 token: Vec<u8>,
35 amount: num_bigint::BigUint,
36 _gas_limit: u64,
37 func_name: TxFunctionName,
38 arguments: Vec<Vec<u8>>,
39 ) -> Result<(), VMHooksEarlyExit> {
40 let mut args = vec![token, amount.to_bytes_be()];
41 append_endpoint_name_and_args(&mut args, func_name, arguments);
42
43 self.context.perform_transfer_execute(
44 to,
45 num_bigint::BigUint::zero(),
46 ESDT_TRANSFER_FUNC_NAME.into(),
47 args,
48 )
49 }
50
51 #[allow(clippy::too_many_arguments)]
52 fn perform_transfer_execute_nft(
53 &mut self,
54 to: VMAddress,
55 token: Vec<u8>,
56 nonce: u64,
57 amount: num_bigint::BigUint,
58 _gas_limit: u64,
59 func_name: TxFunctionName,
60 arguments: Vec<Vec<u8>>,
61 ) -> Result<(), VMHooksEarlyExit> {
62 let contract_address = self.context.current_address().clone();
63
64 let mut args = vec![
65 token,
66 top_encode_u64(nonce),
67 top_encode_big_uint(&amount),
68 to.to_vec(),
69 ];
70
71 append_endpoint_name_and_args(&mut args, func_name, arguments);
72
73 self.context.perform_transfer_execute(
74 contract_address,
75 num_bigint::BigUint::zero(),
76 ESDT_NFT_TRANSFER_FUNC_NAME.into(),
77 args,
78 )
79 }
80
81 fn perform_transfer_execute_multi(
82 &mut self,
83 to: VMAddress,
84 payments: Vec<TxTokenTransfer>,
85 _gas_limit: u64,
86 endpoint_name: TxFunctionName,
87 arguments: Vec<Vec<u8>>,
88 ) -> Result<(), VMHooksEarlyExit> {
89 let contract_address = self.context.current_address().clone();
90
91 let mut args = vec![to.to_vec(), top_encode_u64(payments.len() as u64)];
92
93 for payment in payments.into_iter() {
94 let token_bytes = payment.token_identifier;
95 args.push(token_bytes);
96 let nonce_bytes = top_encode_u64(payment.nonce);
97 args.push(nonce_bytes);
98 let amount_bytes = top_encode_big_uint(&payment.value);
99 args.push(amount_bytes);
100 }
101
102 append_endpoint_name_and_args(&mut args, endpoint_name, arguments);
103
104 self.context.perform_transfer_execute(
105 contract_address,
106 num_bigint::BigUint::zero(),
107 ESDT_MULTI_TRANSFER_FUNC_NAME.into(),
108 args,
109 )
110 }
111
112 fn perform_upgrade_contract(
113 &mut self,
114 to: VMAddress,
115 egld_value: num_bigint::BigUint,
116 contract_code: Vec<u8>,
117 code_metadata: VMCodeMetadata,
118 args: Vec<Vec<u8>>,
119 ) -> Result<(), VMHooksEarlyExit> {
120 let mut arguments = vec![contract_code, code_metadata.to_vec()];
121 arguments.extend(args);
122 self.context.perform_async_call(
123 to,
124 egld_value,
125 UPGRADE_CONTRACT_FUNC_NAME.into(),
126 arguments,
127 )
128 }
129
130 pub fn transfer_value_execute(
131 &mut self,
132 to_handle: RawHandle,
133 amount_handle: RawHandle,
134 _gas_limit: u64,
135 endpoint_name_handle: RawHandle,
136 arg_buffer_handle: RawHandle,
137 ) -> Result<(), VMHooksEarlyExit> {
138 let recipient = self.context.m_types_lock().mb_to_address(to_handle);
139 let egld_value = self.context.m_types_lock().bu_get(amount_handle);
140 let endpoint_name = self
141 .context
142 .m_types_lock()
143 .mb_to_function_name(endpoint_name_handle);
144 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
145
146 self.context
147 .perform_transfer_execute(recipient, egld_value, endpoint_name, arg_buffer)
148 }
149
150 pub fn multi_transfer_esdt_nft_execute(
151 &mut self,
152 to_handle: RawHandle,
153 payments_handle: RawHandle,
154 gas_limit: u64,
155 endpoint_name_handle: RawHandle,
156 arg_buffer_handle: RawHandle,
157 ) -> Result<(), VMHooksEarlyExit> {
158 let to = self.context.m_types_lock().mb_to_address(to_handle);
159 let (payments, num_bytes_copied) = self
160 .context
161 .m_types_lock()
162 .mb_get_vec_of_esdt_payments(payments_handle);
163 self.use_gas_for_data_copy(num_bytes_copied)?;
164 let endpoint_name = self
165 .context
166 .m_types_lock()
167 .mb_to_function_name(endpoint_name_handle);
168 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
169
170 if payments.len() == 1 {
171 let payment = payments[0].clone();
172 if payment.nonce == 0 {
173 self.perform_transfer_execute_esdt(
174 to,
175 payment.token_identifier,
176 payment.value,
177 gas_limit,
178 endpoint_name,
179 arg_buffer,
180 )
181 } else {
182 self.perform_transfer_execute_nft(
183 to,
184 payment.token_identifier,
185 payment.nonce,
186 payment.value,
187 gas_limit,
188 endpoint_name,
189 arg_buffer,
190 )
191 }
192 } else {
193 self.perform_transfer_execute_multi(to, payments, gas_limit, endpoint_name, arg_buffer)
194 }
195 }
196
197 pub fn managed_multi_transfer_esdt_nft_execute_with_return(
198 &mut self,
199 to_handle: i32,
200 payments_handle: i32,
201 gas_limit: u64,
202 function_handle: i32,
203 arguments_handle: i32,
204 ) -> Result<i32, VMHooksEarlyExit> {
205 self.multi_transfer_esdt_nft_execute(
206 to_handle,
207 payments_handle,
208 gas_limit,
209 function_handle,
210 arguments_handle,
211 )?;
212 Ok(0)
214 }
215
216 pub fn async_call_raw(
217 &mut self,
218 to_handle: RawHandle,
219 egld_value_handle: RawHandle,
220 endpoint_name_handle: RawHandle,
221 arg_buffer_handle: RawHandle,
222 ) -> Result<(), VMHooksEarlyExit> {
223 let to = self.context.m_types_lock().mb_to_address(to_handle);
224 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
225 let endpoint_name = self
226 .context
227 .m_types_lock()
228 .mb_to_function_name(endpoint_name_handle);
229 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
230
231 self.context
232 .perform_async_call(to, egld_value, endpoint_name, arg_buffer)
233 }
234
235 #[allow(clippy::too_many_arguments)]
236 pub fn create_async_call_raw(
237 &mut self,
238 to_handle: RawHandle,
239 egld_value_handle: RawHandle,
240 endpoint_name_handle: RawHandle,
241 arg_buffer_handle: RawHandle,
242 success_callback: &[u8],
243 error_callback: &[u8],
244 _gas: u64,
245 _extra_gas_for_callback: u64,
246 callback_closure_handle: RawHandle,
247 ) -> Result<(), VMHooksEarlyExit> {
248 let contract_address = self.context.current_address().clone();
249 let to = self.context.m_types_lock().mb_to_address(to_handle);
250 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
251 let endpoint_name = self
252 .context
253 .m_types_lock()
254 .mb_to_function_name(endpoint_name_handle);
255 if endpoint_name.is_empty() {
256 return Err(early_exit_vm_error(vm_err_msg::PROMISES_TOKENIZE_FAILED));
259 }
260 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
261 let tx_hash = self.context.tx_hash();
262 let callback_closure_data = self
263 .context
264 .m_types_lock()
265 .mb_get(callback_closure_handle)
266 .to_vec();
267
268 let call = AsyncCallTxData {
269 from: contract_address,
270 to,
271 call_value: egld_value,
272 endpoint_name,
273 arguments: arg_buffer,
274 tx_hash,
275 };
276
277 let promise = Promise {
278 call,
279 success_callback: success_callback.into(),
280 error_callback: error_callback.into(),
281 callback_closure_data,
282 };
283
284 let mut tx_result = self.context.result_lock();
285 tx_result.all_calls.push(promise.call.clone());
286 tx_result.pending_calls.promises.push(promise);
287
288 Ok(())
289 }
290
291 #[allow(clippy::too_many_arguments)]
292 pub fn deploy_contract(
293 &mut self,
294 _gas: u64,
295 egld_value_handle: RawHandle,
296 code_handle: RawHandle,
297 code_metadata_handle: RawHandle,
298 arg_buffer_handle: RawHandle,
299 new_address_handle: RawHandle,
300 result_handle: RawHandle,
301 ) -> Result<(), VMHooksEarlyExit> {
302 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
303 let code = self.context.m_types_lock().mb_get(code_handle).to_vec();
304 let code_metadata = self
305 .context
306 .m_types_lock()
307 .mb_to_code_metadata(code_metadata_handle);
308 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
309
310 let (new_address, result) =
311 self.context
312 .perform_deploy(egld_value, code, code_metadata, arg_buffer)?;
313
314 self.context
315 .m_types_lock()
316 .mb_set(new_address_handle, new_address.to_vec());
317 self.set_return_data(result_handle, result)?;
318
319 Ok(())
320 }
321
322 #[allow(clippy::too_many_arguments)]
323 pub fn deploy_from_source_contract(
324 &mut self,
325 _gas: u64,
326 egld_value_handle: RawHandle,
327 source_contract_address_handle: RawHandle,
328 code_metadata_handle: RawHandle,
329 arg_buffer_handle: RawHandle,
330 new_address_handle: RawHandle,
331 result_handle: RawHandle,
332 ) -> Result<(), VMHooksEarlyExit> {
333 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
334 let source_contract_address = self
335 .context
336 .m_types_lock()
337 .mb_to_address(source_contract_address_handle);
338 let source_contract_code = self.context.account_code(&source_contract_address);
339 let code_metadata = self
340 .context
341 .m_types_lock()
342 .mb_to_code_metadata(code_metadata_handle);
343 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
344
345 let (new_address, result) = self.context.perform_deploy(
346 egld_value,
347 source_contract_code,
348 code_metadata,
349 arg_buffer,
350 )?;
351
352 self.context
353 .m_types_lock()
354 .mb_set(new_address_handle, new_address.to_vec());
355
356 self.set_return_data(result_handle, result)?;
357
358 Ok(())
359 }
360
361 pub fn upgrade_from_source_contract(
362 &mut self,
363 sc_address_handle: RawHandle,
364 _gas: u64,
365 egld_value_handle: RawHandle,
366 source_contract_address_handle: RawHandle,
367 code_metadata_handle: RawHandle,
368 arg_buffer_handle: RawHandle,
369 ) -> Result<(), VMHooksEarlyExit> {
370 self.use_gas(self.gas_schedule().base_ops_api_cost.create_contract)?;
371
372 let to = self.context.m_types_lock().mb_to_address(sc_address_handle);
373 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
374 let source_contract_address = self
375 .context
376 .m_types_lock()
377 .mb_to_address(source_contract_address_handle);
378 let source_contract_code = self.context.account_code(&source_contract_address);
379 let code_metadata = self
380 .context
381 .m_types_lock()
382 .mb_to_code_metadata(code_metadata_handle);
383 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
384
385 self.perform_upgrade_contract(
386 to,
387 egld_value,
388 source_contract_code,
389 code_metadata,
390 arg_buffer,
391 )
392 }
393
394 pub fn upgrade_contract(
395 &mut self,
396 sc_address_handle: RawHandle,
397 _gas: u64,
398 egld_value_handle: RawHandle,
399 code_handle: RawHandle,
400 code_metadata_handle: RawHandle,
401 arg_buffer_handle: RawHandle,
402 ) -> Result<(), VMHooksEarlyExit> {
403 self.use_gas(self.gas_schedule().base_ops_api_cost.create_contract)?;
404
405 let to = self.context.m_types_lock().mb_to_address(sc_address_handle);
406 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
407 let code = self.context.m_types_lock().mb_get(code_handle).to_vec();
408 let code_metadata = self
409 .context
410 .m_types_lock()
411 .mb_to_code_metadata(code_metadata_handle);
412 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
413
414 self.perform_upgrade_contract(to, egld_value, code, code_metadata, arg_buffer)
415 }
416
417 fn execute_on_dest_context_fallible_raw(
425 &mut self,
426 _gas: u64,
427 to_handle: RawHandle,
428 egld_value_handle: RawHandle,
429 endpoint_name_handle: RawHandle,
430 arg_buffer_handle: RawHandle,
431 result_handle: RawHandle,
432 ) -> Result<(ReturnCode, String), VMHooksEarlyExit> {
433 let to = self.context.m_types_lock().mb_to_address(to_handle);
434 let egld_value = self.context.m_types_lock().bu_get(egld_value_handle);
435 let endpoint_name = self
436 .context
437 .m_types_lock()
438 .mb_to_function_name(endpoint_name_handle);
439 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
440
441 let tx_result = self.context.perform_execute_on_dest_context(
442 to,
443 egld_value,
444 endpoint_name,
445 arg_buffer,
446 )?;
447
448 if tx_result.result_status.is_success() {
449 self.set_return_data(result_handle, tx_result.result_values)?;
450 }
451
452 Ok((tx_result.result_status, tx_result.result_message))
453 }
454
455 pub fn execute_on_dest_context_raw(
456 &mut self,
457 gas: u64,
458 to_handle: RawHandle,
459 egld_value_handle: RawHandle,
460 endpoint_name_handle: RawHandle,
461 arg_buffer_handle: RawHandle,
462 result_handle: RawHandle,
463 ) -> Result<(), VMHooksEarlyExit> {
464 let (result_status, result_message) = self.execute_on_dest_context_fallible_raw(
465 gas,
466 to_handle,
467 egld_value_handle,
468 endpoint_name_handle,
469 arg_buffer_handle,
470 result_handle,
471 )?;
472
473 if result_status.is_success() {
474 Ok(())
475 } else {
476 Err(VMHooksEarlyExit::new(result_status.as_u64()).with_message(result_message.clone()))
478 }
479 }
480
481 pub fn execute_on_dest_context_fallible(
482 &mut self,
483 gas: u64,
484 to_handle: RawHandle,
485 egld_value_handle: RawHandle,
486 endpoint_name_handle: RawHandle,
487 arg_buffer_handle: RawHandle,
488 result_handle: RawHandle,
489 ) -> Result<i32, VMHooksEarlyExit> {
490 let (result_status, _) = self.execute_on_dest_context_fallible_raw(
491 gas,
492 to_handle,
493 egld_value_handle,
494 endpoint_name_handle,
495 arg_buffer_handle,
496 result_handle,
497 )?;
498
499 if result_status.is_success() {
500 Ok(0)
501 } else {
502 Ok(1)
503 }
504 }
505
506 pub fn execute_on_dest_context_readonly_raw(
507 &mut self,
508 _gas: u64,
509 to_handle: RawHandle,
510 endpoint_name_handle: RawHandle,
511 arg_buffer_handle: RawHandle,
512 result_handle: RawHandle,
513 ) -> Result<(), VMHooksEarlyExit> {
514 let to = self.context.m_types_lock().mb_to_address(to_handle);
515 let endpoint_name = self
516 .context
517 .m_types_lock()
518 .mb_to_function_name(endpoint_name_handle);
519 let arg_buffer = self.load_arg_data(arg_buffer_handle)?;
520
521 let result =
522 self.context
523 .perform_execute_on_dest_context_readonly(to, endpoint_name, arg_buffer)?;
524
525 self.set_return_data(result_handle, result)?;
526
527 Ok(())
528 }
529
530 fn load_arg_data(
531 &mut self,
532 arg_buffer_handle: RawHandle,
533 ) -> Result<Vec<Vec<u8>>, VMHooksEarlyExit> {
534 let (arg_buffer, num_bytes_copied) = self
535 .context
536 .m_types_lock()
537 .mb_get_vec_of_bytes(arg_buffer_handle);
538
539 self.use_gas_for_data_copy(num_bytes_copied)?;
540 Ok(arg_buffer)
541 }
542
543 fn set_return_data(
544 &mut self,
545 result_handle: RawHandle,
546 result: Vec<Vec<u8>>,
547 ) -> Result<(), VMHooksEarlyExit> {
548 let num_bytes_copied = self
549 .context
550 .m_types_lock()
551 .mb_set_vec_of_bytes(result_handle, result);
552
553 self.use_gas_for_data_copy(num_bytes_copied)
554 }
555
556 pub fn clean_return_data(&mut self) -> Result<(), VMHooksEarlyExit> {
557 let mut tx_result = self.context.result_lock();
558 tx_result.result_values.clear();
559 Ok(())
560 }
561
562 pub fn delete_from_return_data(&mut self, index: usize) -> Result<(), VMHooksEarlyExit> {
563 let mut tx_result = self.context.result_lock();
564 if index > tx_result.result_values.len() {
565 return Ok(());
566 }
567
568 let _ = tx_result.result_values.remove(index);
569 Ok(())
570 }
571}