multiversx_sc_modules/
transfer_role_proxy.rs

1use multiversx_sc::codec::TopEncodeMulti;
2
3multiversx_sc::imports!();
4
5const CALLBACK_RESERVED_GAS_PER_TOKEN: u64 = 1_000_000;
6static ERR_CALLBACK_MSG: &[u8] = b"Error received in callback:";
7
8pub type PaymentsVec<M> = ManagedVec<M, EsdtTokenPayment<M>>;
9
10#[multiversx_sc::module]
11pub trait TransferRoleProxyModule {
12    fn transfer_to_user(
13        &self,
14        original_caller: ManagedAddress,
15        dest: ManagedAddress,
16        payments: &PaymentsVec<Self::Api>,
17        data: ManagedBuffer,
18    ) -> ! {
19        let transaction = self.tx().to(&dest).raw_call(data).payment(payments);
20
21        self.execute_async_call(original_caller, payments, transaction, None)
22    }
23
24    fn transfer_to_contract_typed_call<T>(
25        &self,
26        original_caller: ManagedAddress,
27        transaction: Tx<
28            TxScEnv<Self::Api>,
29            (),
30            &ManagedAddress,
31            &ManagedVec<Self::Api, EsdtTokenPayment<Self::Api>>,
32            (),
33            FunctionCall<Self::Api>,
34            (),
35        >,
36        opt_custom_callback: Option<CallbackClosure<Self::Api>>,
37    ) -> !
38    where
39        T: TopEncodeMulti,
40    {
41        self.execute_async_call(
42            original_caller,
43            transaction.payment,
44            transaction,
45            opt_custom_callback,
46        );
47    }
48
49    fn transfer_to_contract_raw(
50        &self,
51        original_caller: ManagedAddress,
52        dest: ManagedAddress,
53        payments: &PaymentsVec<Self::Api>,
54        endpoint_name: ManagedBuffer,
55        args: ManagedArgBuffer<Self::Api>,
56        opt_custom_callback: Option<CallbackClosure<Self::Api>>,
57    ) -> ! {
58        let transaction = self
59            .tx()
60            .to(&dest)
61            .raw_call(endpoint_name)
62            .payment(payments)
63            .arguments_raw(args);
64
65        self.execute_async_call(original_caller, payments, transaction, opt_custom_callback)
66    }
67
68    fn execute_async_call(
69        &self,
70        original_caller: ManagedAddress,
71        initial_payments: &PaymentsVec<Self::Api>,
72        transaction: Tx<
73            TxScEnv<Self::Api>,
74            (),
75            &ManagedAddress,
76            &ManagedVec<Self::Api, EsdtTokenPayment<Self::Api>>,
77            (),
78            FunctionCall<Self::Api>,
79            (),
80        >,
81        opt_custom_callback: Option<CallbackClosure<Self::Api>>,
82    ) -> ! {
83        require!(
84            self.destination_whitelist().contains(transaction.to),
85            "Destination address not whitelisted"
86        );
87
88        let remaining_gas = self.blockchain().get_gas_left();
89        let cb_gas_needed = CALLBACK_RESERVED_GAS_PER_TOKEN * transaction.payment.len() as u64;
90        require!(
91            remaining_gas > cb_gas_needed,
92            "Not enough gas to launch async call"
93        );
94
95        let cb = match opt_custom_callback {
96            Some(custom_cb) => custom_cb,
97            None => TransferRoleProxyModule::callbacks(self)
98                .transfer_callback(original_caller, initial_payments.clone()),
99        };
100
101        transaction.callback(cb).async_call_and_exit()
102    }
103
104    #[callback]
105    fn transfer_callback(
106        &self,
107        original_caller: ManagedAddress,
108        initial_payments: ManagedVec<EsdtTokenPayment<Self::Api>>,
109        #[call_result] result: ManagedAsyncCallResult<MultiValueEncoded<ManagedBuffer>>,
110    ) -> MultiValueEncoded<ManagedBuffer> {
111        match result {
112            ManagedAsyncCallResult::Ok(return_values) => return_values,
113            ManagedAsyncCallResult::Err(err) => {
114                if !initial_payments.is_empty() {
115                    self.tx()
116                        .to(&original_caller)
117                        .payment(initial_payments)
118                        .transfer();
119                }
120
121                let mut err_result = MultiValueEncoded::new();
122                err_result.push(ManagedBuffer::new_from_bytes(ERR_CALLBACK_MSG));
123                err_result.push(err.err_msg.clone());
124
125                sc_print!("{}", err.err_msg);
126
127                err_result
128            },
129        }
130    }
131
132    #[storage_mapper("transfer_role_proxy:destination_whitelist")]
133    fn destination_whitelist(&self) -> UnorderedSetMapper<ManagedAddress>;
134}