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