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 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}