multiversx_sc_modules/
transfer_role_proxy.rs1use 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}