multiversx_sc/contract_base/wrappers/
send_raw_wrapper.rs

1use core::marker::PhantomData;
2
3use crate::{
4    api::{
5        BigIntApiImpl, BlockchainApiImpl, CallTypeApi, HandleConstraints, ManagedBufferApiImpl,
6        RawHandle, SendApiImpl, StaticVarApiImpl, const_handles, use_raw_handle,
7    },
8    types::{
9        BigUint, CodeMetadata, EgldOrEsdtTokenPayment, EsdtTokenIdentifier, EsdtTokenPayment,
10        ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec,
11    },
12};
13
14/// Wraps the result of a sync call.
15///
16/// Is used primarily in result handlers.
17pub struct SyncCallRawResult<Api>(pub ManagedVec<Api, ManagedBuffer<Api>>)
18where
19    Api: CallTypeApi;
20
21/// Wraps the result of a fallible sync call (one that returns the error instead of stopping execution).
22///
23/// Is used primarily in result handlers.
24pub enum SyncCallRawResultOrError<Api>
25where
26    Api: CallTypeApi,
27{
28    Success(SyncCallRawResult<Api>),
29    Error(u32),
30}
31
32#[derive(Default)]
33pub struct SendRawWrapper<A>
34where
35    A: CallTypeApi,
36{
37    _phantom: PhantomData<A>,
38}
39
40/// Error type returned when a transfer-execute operation signals that it failed.
41pub struct TransferExecuteFailed;
42
43impl<A> SendRawWrapper<A>
44where
45    A: CallTypeApi,
46{
47    pub fn new() -> Self {
48        SendRawWrapper {
49            _phantom: PhantomData,
50        }
51    }
52
53    fn load_code_metadata_to_mb(
54        &self,
55        code_metadata: CodeMetadata,
56        code_metadata_handle: RawHandle,
57    ) {
58        let code_metadata_bytes = code_metadata.to_byte_array();
59        A::managed_type_impl().mb_overwrite(
60            use_raw_handle(code_metadata_handle),
61            &code_metadata_bytes[..],
62        );
63    }
64
65    pub fn direct_egld<D>(&self, to: &ManagedAddress<A>, egld_value: &BigUint<A>, data: D)
66    where
67        D: Into<ManagedBuffer<A>>,
68    {
69        let empty_mb_handle: A::ManagedBufferHandle =
70            use_raw_handle(const_handles::MBUF_TEMPORARY_1);
71        A::managed_type_impl().mb_overwrite(empty_mb_handle.clone(), &[]);
72
73        A::send_api_impl().transfer_value_execute(
74            to.get_handle().get_raw_handle(),
75            egld_value.get_handle().get_raw_handle(),
76            0,
77            data.into().get_handle().get_raw_handle(),
78            empty_mb_handle.get_raw_handle(),
79        );
80    }
81
82    pub fn direct_egld_execute(
83        &self,
84        to: &ManagedAddress<A>,
85        egld_value: &BigUint<A>,
86        gas_limit: u64,
87        endpoint_name: &ManagedBuffer<A>,
88        arg_buffer: &ManagedArgBuffer<A>,
89    ) {
90        A::send_api_impl().transfer_value_execute(
91            to.get_handle().get_raw_handle(),
92            egld_value.get_handle().get_raw_handle(),
93            gas_limit,
94            endpoint_name.get_handle().get_raw_handle(),
95            arg_buffer.get_handle().get_raw_handle(),
96        );
97    }
98
99    pub fn transfer_esdt_execute(
100        &self,
101        to: &ManagedAddress<A>,
102        token: &EsdtTokenIdentifier<A>,
103        value: &BigUint<A>,
104        gas_limit: u64,
105        endpoint_name: &ManagedBuffer<A>,
106        arg_buffer: &ManagedArgBuffer<A>,
107    ) {
108        self.transfer_esdt_nft_execute(to, token, 0, value, gas_limit, endpoint_name, arg_buffer);
109    }
110
111    #[allow(clippy::too_many_arguments)]
112    pub fn transfer_esdt_nft_execute(
113        &self,
114        to: &ManagedAddress<A>,
115        token: &EsdtTokenIdentifier<A>,
116        nonce: u64,
117        egld_value: &BigUint<A>,
118        gas_limit: u64,
119        endpoint_name: &ManagedBuffer<A>,
120        arg_buffer: &ManagedArgBuffer<A>,
121    ) {
122        let mut payments: ManagedVec<A, EsdtTokenPayment<A>> = ManagedVec::new();
123        payments.push(EsdtTokenPayment::new(
124            token.clone(),
125            nonce,
126            egld_value.clone(),
127        ));
128        self.multi_esdt_transfer_execute(to, &payments, gas_limit, endpoint_name, arg_buffer);
129    }
130
131    pub fn multi_esdt_transfer_execute(
132        &self,
133        to: &ManagedAddress<A>,
134        payments: &ManagedVec<A, EsdtTokenPayment<A>>,
135        gas_limit: u64,
136        endpoint_name: &ManagedBuffer<A>,
137        arg_buffer: &ManagedArgBuffer<A>,
138    ) {
139        A::send_api_impl().multi_transfer_esdt_nft_execute(
140            to.get_handle().get_raw_handle(),
141            payments.get_handle().get_raw_handle(),
142            gas_limit,
143            endpoint_name.get_handle().get_raw_handle(),
144            arg_buffer.get_handle().get_raw_handle(),
145        );
146    }
147
148    /// `multi_transfer_esdt_nft_execute` doesn't work for a single EGLD payment,
149    /// so we need a different strategy in this one particular case.
150    ///
151    /// Returns `true` if single EGLD payment was produced.
152    fn fallback_to_single_egld_if_necessary(
153        &self,
154        to: &ManagedAddress<A>,
155        payments: &ManagedVec<A, EgldOrEsdtTokenPayment<A>>,
156        gas_limit: u64,
157        endpoint_name: &ManagedBuffer<A>,
158        arg_buffer: &ManagedArgBuffer<A>,
159    ) -> bool {
160        if payments.is_empty() {
161            self.direct_egld_execute(to, &BigUint::zero(), gas_limit, endpoint_name, arg_buffer);
162
163            return true;
164        }
165
166        if let Some(single_item) = payments.is_single_item() {
167            if single_item.token_identifier.is_egld() {
168                self.direct_egld_execute(
169                    to,
170                    &single_item.amount,
171                    gas_limit,
172                    endpoint_name,
173                    arg_buffer,
174                );
175                return true;
176            }
177        }
178
179        false
180    }
181
182    #[deprecated(
183        since = "0.59.0",
184        note = "Use multi_egld_or_esdt_transfer_execute_fallible instead"
185    )]
186    pub fn multi_egld_or_esdt_transfer_execute(
187        &self,
188        to: &ManagedAddress<A>,
189        payments: &ManagedVec<A, EgldOrEsdtTokenPayment<A>>,
190        gas_limit: u64,
191        endpoint_name: &ManagedBuffer<A>,
192        arg_buffer: &ManagedArgBuffer<A>,
193    ) {
194        if self.fallback_to_single_egld_if_necessary(
195            to,
196            payments,
197            gas_limit,
198            endpoint_name,
199            arg_buffer,
200        ) {
201            return;
202        }
203        A::send_api_impl().multi_transfer_esdt_nft_execute(
204            to.get_handle().get_raw_handle(),
205            payments.get_handle().get_raw_handle(),
206            gas_limit,
207            endpoint_name.get_handle().get_raw_handle(),
208            arg_buffer.get_handle().get_raw_handle(),
209        );
210    }
211
212    pub fn multi_egld_or_esdt_transfer_execute_fallible(
213        &self,
214        to: &ManagedAddress<A>,
215        payments: &ManagedVec<A, EgldOrEsdtTokenPayment<A>>,
216        gas_limit: u64,
217        endpoint_name: &ManagedBuffer<A>,
218        arg_buffer: &ManagedArgBuffer<A>,
219    ) -> Result<(), TransferExecuteFailed> {
220        if payments.is_empty() {
221            use crate::{api::quick_signal_error, err_msg};
222
223            quick_signal_error::<A>(err_msg::TRANSFER_EXECUTE_REQUIRES_PAYMENT);
224        }
225
226        let ret = A::send_api_impl().multi_transfer_esdt_nft_execute_with_return(
227            to.get_handle().get_raw_handle(),
228            payments.get_handle().get_raw_handle(),
229            gas_limit,
230            endpoint_name.get_handle().get_raw_handle(),
231            arg_buffer.get_handle().get_raw_handle(),
232        );
233
234        if ret == 0 {
235            Ok(())
236        } else {
237            Err(TransferExecuteFailed)
238        }
239    }
240
241    pub fn async_call_raw(
242        &self,
243        to: &ManagedAddress<A>,
244        egld_value: &BigUint<A>,
245        endpoint_name: &ManagedBuffer<A>,
246        arg_buffer: &ManagedArgBuffer<A>,
247    ) -> ! {
248        A::send_api_impl().async_call_raw(
249            to.get_handle().get_raw_handle(),
250            egld_value.get_handle().get_raw_handle(),
251            endpoint_name.get_handle().get_raw_handle(),
252            arg_buffer.get_handle().get_raw_handle(),
253        )
254    }
255
256    #[allow(clippy::too_many_arguments)]
257    pub fn create_async_call_raw(
258        &self,
259        to: &ManagedAddress<A>,
260        egld_value: &BigUint<A>,
261        endpoint_name: &ManagedBuffer<A>,
262        arg_buffer: &ManagedArgBuffer<A>,
263        success_callback: &'static str,
264        error_callback: &'static str,
265        gas: u64,
266        extra_gas_for_callback: u64,
267        serialized_callback_closure_args: &ManagedBuffer<A>,
268    ) {
269        A::send_api_impl().create_async_call_raw(
270            to.get_handle().get_raw_handle(),
271            egld_value.get_handle().get_raw_handle(),
272            endpoint_name.get_handle().get_raw_handle(),
273            arg_buffer.get_handle().get_raw_handle(),
274            success_callback,
275            error_callback,
276            gas,
277            extra_gas_for_callback,
278            serialized_callback_closure_args
279                .get_handle()
280                .get_raw_handle(),
281        )
282    }
283
284    /// Deploys a new contract in the same shard.
285    /// Unlike `async_call_raw`, the deployment is synchronous and tx execution continues afterwards.
286    /// Also unlike `async_call_raw`, it uses an argument buffer to pass arguments
287    /// If the deployment fails, Option::None is returned
288    pub fn deploy_contract(
289        &self,
290        gas: u64,
291        egld_value: &BigUint<A>,
292        code: &ManagedBuffer<A>,
293        code_metadata: CodeMetadata,
294        arg_buffer: &ManagedArgBuffer<A>,
295    ) -> (ManagedAddress<A>, ManagedVec<A, ManagedBuffer<A>>) {
296        let code_metadata_handle = const_handles::MBUF_TEMPORARY_1;
297        self.load_code_metadata_to_mb(code_metadata, code_metadata_handle);
298        let new_address_handle = A::static_var_api_impl().next_handle();
299        let result_handle = A::static_var_api_impl().next_handle();
300        A::send_api_impl().deploy_contract(
301            gas,
302            egld_value.get_handle().get_raw_handle(),
303            code.get_handle().get_raw_handle(),
304            code_metadata_handle,
305            arg_buffer.get_handle().get_raw_handle(),
306            new_address_handle,
307            result_handle,
308        );
309        unsafe {
310            (
311                ManagedAddress::from_raw_handle(new_address_handle),
312                ManagedVec::from_raw_handle(result_handle),
313            )
314        }
315    }
316
317    /// Deploys a new contract in the same shard by re-using the code of an already deployed source contract.
318    /// The deployment is done synchronously and the new contract's address is returned.
319    /// If the deployment fails, Option::None is returned
320    pub fn deploy_from_source_contract(
321        &self,
322        gas: u64,
323        egld_value: &BigUint<A>,
324        source_contract_address: &ManagedAddress<A>,
325        code_metadata: CodeMetadata,
326        arg_buffer: &ManagedArgBuffer<A>,
327    ) -> (ManagedAddress<A>, ManagedVec<A, ManagedBuffer<A>>) {
328        let code_metadata_handle = const_handles::MBUF_TEMPORARY_1;
329        self.load_code_metadata_to_mb(code_metadata, code_metadata_handle);
330        let new_address_handle = A::static_var_api_impl().next_handle();
331        let result_handle = A::static_var_api_impl().next_handle();
332        A::send_api_impl().deploy_from_source_contract(
333            gas,
334            egld_value.get_handle().get_raw_handle(),
335            source_contract_address.get_handle().get_raw_handle(),
336            code_metadata_handle,
337            arg_buffer.get_handle().get_raw_handle(),
338            new_address_handle,
339            result_handle,
340        );
341        unsafe {
342            (
343                ManagedAddress::from_raw_handle(new_address_handle),
344                ManagedVec::from_raw_handle(result_handle),
345            )
346        }
347    }
348
349    pub fn upgrade_from_source_contract(
350        &self,
351        sc_address: &ManagedAddress<A>,
352        gas: u64,
353        egld_value: &BigUint<A>,
354        source_contract_address: &ManagedAddress<A>,
355        code_metadata: CodeMetadata,
356        arg_buffer: &ManagedArgBuffer<A>,
357    ) {
358        let code_metadata_handle = const_handles::MBUF_TEMPORARY_1;
359        self.load_code_metadata_to_mb(code_metadata, code_metadata_handle);
360        A::send_api_impl().upgrade_from_source_contract(
361            sc_address.get_handle().get_raw_handle(),
362            gas,
363            egld_value.get_handle().get_raw_handle(),
364            source_contract_address.get_handle().get_raw_handle(),
365            code_metadata_handle,
366            arg_buffer.get_handle().get_raw_handle(),
367        )
368    }
369
370    /// Upgrades a child contract of the currently executing contract.
371    /// The upgrade is synchronous, and the current transaction will fail if the upgrade fails.
372    /// The child contract's new init function will be called with the provided arguments
373    pub fn upgrade_contract(
374        &self,
375        sc_address: &ManagedAddress<A>,
376        gas: u64,
377        egld_value: &BigUint<A>,
378        code: &ManagedBuffer<A>,
379        code_metadata: CodeMetadata,
380        arg_buffer: &ManagedArgBuffer<A>,
381    ) {
382        let code_metadata_handle = const_handles::MBUF_TEMPORARY_1;
383        self.load_code_metadata_to_mb(code_metadata, code_metadata_handle);
384        A::send_api_impl().upgrade_contract(
385            sc_address.get_handle().get_raw_handle(),
386            gas,
387            egld_value.get_handle().get_raw_handle(),
388            code.get_handle().get_raw_handle(),
389            code_metadata_handle,
390            arg_buffer.get_handle().get_raw_handle(),
391        )
392    }
393
394    /// Same shard, in-line execution of another contract.
395    pub fn execute_on_dest_context_raw(
396        &self,
397        gas: u64,
398        address: &ManagedAddress<A>,
399        value: &BigUint<A>,
400        endpoint_name: &ManagedBuffer<A>,
401        arg_buffer: &ManagedArgBuffer<A>,
402    ) -> SyncCallRawResult<A> {
403        let result_handle = A::static_var_api_impl().next_handle();
404        A::send_api_impl().execute_on_dest_context_raw(
405            gas,
406            address.get_handle().get_raw_handle(),
407            value.get_handle().get_raw_handle(),
408            endpoint_name.get_handle().get_raw_handle(),
409            arg_buffer.get_handle().get_raw_handle(),
410            result_handle,
411        );
412        let result_vec = unsafe { ManagedVec::from_raw_handle(result_handle) };
413        SyncCallRawResult(result_vec)
414    }
415
416    pub fn execute_on_same_context_raw(
417        &self,
418        gas: u64,
419        address: &ManagedAddress<A>,
420        value: &BigUint<A>,
421        endpoint_name: &ManagedBuffer<A>,
422        arg_buffer: &ManagedArgBuffer<A>,
423    ) -> ManagedVec<A, ManagedBuffer<A>> {
424        let result_handle = A::static_var_api_impl().next_handle();
425        A::send_api_impl().execute_on_same_context_raw(
426            gas,
427            address.get_handle().get_raw_handle(),
428            value.get_handle().get_raw_handle(),
429            endpoint_name.get_handle().get_raw_handle(),
430            arg_buffer.get_handle().get_raw_handle(),
431            result_handle,
432        );
433        unsafe { ManagedVec::from_raw_handle(result_handle) }
434    }
435
436    /// Same shard, in-line execution of another contract.
437    pub fn execute_on_dest_context_readonly_raw(
438        &self,
439        gas: u64,
440        address: &ManagedAddress<A>,
441        endpoint_name: &ManagedBuffer<A>,
442        arg_buffer: &ManagedArgBuffer<A>,
443    ) -> ManagedVec<A, ManagedBuffer<A>> {
444        let result_handle = A::static_var_api_impl().next_handle();
445        A::send_api_impl().execute_on_dest_context_readonly_raw(
446            gas,
447            address.get_handle().get_raw_handle(),
448            endpoint_name.get_handle().get_raw_handle(),
449            arg_buffer.get_handle().get_raw_handle(),
450            result_handle,
451        );
452        unsafe { ManagedVec::from_raw_handle(result_handle) }
453    }
454
455    /// Same shard, in-line execution of another contract.
456    pub fn execute_on_dest_context_error_return_raw(
457        &self,
458        gas: u64,
459        address: &ManagedAddress<A>,
460        value: &BigUint<A>,
461        endpoint_name: &ManagedBuffer<A>,
462        arg_buffer: &ManagedArgBuffer<A>,
463    ) -> SyncCallRawResultOrError<A> {
464        let result_handle = A::static_var_api_impl().next_handle();
465        let result_code = A::send_api_impl().execute_on_dest_context_error_return_raw(
466            gas,
467            address.get_handle().get_raw_handle(),
468            value.get_handle().get_raw_handle(),
469            endpoint_name.get_handle().get_raw_handle(),
470            arg_buffer.get_handle().get_raw_handle(),
471            result_handle,
472        );
473        if result_code == 0 {
474            let result_vec = unsafe { ManagedVec::from_raw_handle(result_handle) };
475            SyncCallRawResultOrError::Success(SyncCallRawResult(result_vec))
476        } else {
477            SyncCallRawResultOrError::Error(result_code as u32)
478        }
479    }
480}
481
482impl<A> SendRawWrapper<A>
483where
484    A: CallTypeApi,
485{
486    /// Allows synchronously calling a local function by name. Execution is resumed afterwards.
487    pub fn call_local_esdt_built_in_function(
488        &self,
489        gas: u64,
490        function_name: &ManagedBuffer<A>,
491        arg_buffer: &ManagedArgBuffer<A>,
492    ) -> ManagedVec<A, ManagedBuffer<A>> {
493        // account-level built-in function, so the destination address is the contract itself
494        let own_address_handle: A::ManagedBufferHandle =
495            use_raw_handle(const_handles::MBUF_TEMPORARY_1);
496        A::blockchain_api_impl().load_sc_address_managed(own_address_handle.clone());
497        let egld_value_handle = A::managed_type_impl().bi_new_zero();
498
499        let result_handle = A::static_var_api_impl().next_handle();
500        A::send_api_impl().execute_on_dest_context_raw(
501            gas,
502            own_address_handle.get_raw_handle(),
503            egld_value_handle.get_raw_handle(),
504            function_name.get_handle().get_raw_handle(),
505            arg_buffer.get_handle().get_raw_handle(),
506            result_handle,
507        );
508
509        self.clean_return_data();
510        unsafe { ManagedVec::from_raw_handle(result_handle) }
511    }
512
513    pub fn clean_return_data(&self) {
514        A::send_api_impl().clean_return_data()
515    }
516}