multiversx_sc/contract_base/wrappers/
call_value_wrapper.rs1use core::marker::PhantomData;
2
3use crate::{
4 api::{
5 CallValueApi, CallValueApiImpl, ErrorApi, ErrorApiImpl, ManagedBufferApiImpl,
6 ManagedTypeApi, RawHandle, StaticVarApiFlags, StaticVarApiImpl, const_handles,
7 use_raw_handle,
8 },
9 contract_base::BlockchainWrapper,
10 err_msg,
11 types::{
12 BigUint, EgldDecimals, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPayment,
13 EgldOrMultiEsdtPayment, EsdtTokenIdentifier, EsdtTokenPayment, ManagedDecimal, ManagedRef,
14 ManagedType, ManagedVec, ManagedVecItem, ManagedVecItemPayload, ManagedVecPayloadIterator,
15 Payment, PaymentVec, Ref,
16 },
17};
18
19#[derive(Default)]
20pub struct CallValueWrapper<A>
21where
22 A: CallValueApi + ErrorApi + ManagedTypeApi,
23{
24 _phantom: PhantomData<A>,
25}
26
27impl<A> CallValueWrapper<A>
28where
29 A: CallValueApi + ErrorApi + ManagedTypeApi,
30{
31 pub fn new() -> Self {
32 CallValueWrapper {
33 _phantom: PhantomData,
34 }
35 }
36
37 fn all_esdt_transfers_unchecked(&self) -> A::ManagedBufferHandle {
39 let all_transfers_unchecked_handle: A::ManagedBufferHandle =
40 use_raw_handle(const_handles::CALL_VALUE_MULTI_ESDT);
41 if !A::static_var_api_impl()
42 .flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ESDT_UNCHECKED_INITIALIZED)
43 {
44 A::call_value_api_impl()
45 .load_all_esdt_transfers(all_transfers_unchecked_handle.clone());
46 }
47 all_transfers_unchecked_handle
48 }
49
50 pub fn egld_direct_non_strict(&self) -> ManagedRef<'static, A, BigUint<A>> {
54 let call_value_handle: A::BigIntHandle = use_raw_handle(const_handles::CALL_VALUE_EGLD);
55 if !A::static_var_api_impl()
56 .flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_EGLD_DIRECT_INITIALIZED)
57 {
58 A::call_value_api_impl().load_egld_value(call_value_handle.clone());
59 }
60 unsafe { ManagedRef::wrap_handle(call_value_handle) }
61 }
62
63 pub fn egld(&self) -> ManagedRef<'static, A, BigUint<A>> {
69 let all_transfers = self.all();
70 match all_transfers.len() {
71 0 => {
72 use crate::api::BigIntApiImpl;
73
74 let call_value_handle: A::BigIntHandle =
75 use_raw_handle(const_handles::CALL_VALUE_EGLD);
76 A::managed_type_impl().bi_set_int64(call_value_handle.clone(), 0);
77 unsafe { ManagedRef::wrap_handle(call_value_handle) }
78 }
79 1 => {
80 let first = all_transfers.get(0);
81 if !first.token_identifier.is_native() {
82 A::error_api_impl().signal_error(err_msg::NON_PAYABLE_FUNC_ESDT.as_bytes());
83 }
84 unsafe { ManagedRef::wrap_handle(first.amount.get_handle()) }
85 }
86 _ => A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes()),
87 }
88 }
89
90 #[deprecated(
102 since = "0.55.0",
103 note = "Does not cover multi-transfer scenarios properly, but left for backwards compatibility. Please use .egld() instead!"
104 )]
105 pub fn egld_value(&self) -> ManagedRef<'static, A, BigUint<A>> {
106 self.egld_direct_non_strict()
107 }
108
109 pub fn egld_decimal(&self) -> ManagedDecimal<A, EgldDecimals> {
111 ManagedDecimal::<A, EgldDecimals>::const_decimals_from_raw(self.egld_value().clone())
112 }
113
114 pub fn all_esdt_transfers(&self) -> ManagedRef<'static, A, ManagedVec<A, EsdtTokenPayment<A>>> {
121 let multi_esdt_handle: A::ManagedBufferHandle = self.all_esdt_transfers_unchecked();
122 let checked = A::static_var_api_impl()
123 .flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ESDT_INITIALIZED);
124 if !checked && egld_000000_transfer_exists::<A>(multi_esdt_handle.clone()) {
125 A::error_api_impl().signal_error(err_msg::ESDT_UNEXPECTED_EGLD.as_bytes())
126 }
127
128 unsafe { ManagedRef::wrap_handle(multi_esdt_handle) }
129 }
130
131 fn all_transfers_handle(&self) -> A::ManagedBufferHandle {
132 let all_transfers_handle: A::ManagedBufferHandle =
133 use_raw_handle(const_handles::CALL_VALUE_ALL);
134 if !A::static_var_api_impl()
135 .flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED)
136 {
137 A::call_value_api_impl().load_all_transfers(all_transfers_handle.clone());
138 }
139 all_transfers_handle
140 }
141
142 pub fn all_transfers(
149 &self,
150 ) -> ManagedRef<'static, A, ManagedVec<A, EgldOrEsdtTokenPayment<A>>> {
151 let all_transfers_handle = self.all_transfers_handle();
152 unsafe { ManagedRef::wrap_handle(all_transfers_handle) }
153 }
154
155 pub fn all(&self) -> ManagedRef<'static, A, PaymentVec<A>> {
162 let all_transfers_handle = self.all_transfers_handle();
163 unsafe { ManagedRef::wrap_handle(all_transfers_handle) }
164 }
165
166 pub fn single(&self) -> Ref<'static, Payment<A>> {
170 let esdt_transfers = self.all();
171 if esdt_transfers.len() != 1 {
172 A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes())
173 }
174 let value = esdt_transfers.get(0);
175 unsafe {
176 core::mem::transmute::<Ref<'_, Payment<A>>, Ref<'static, Payment<A>>>(value)
178 }
179 }
180
181 pub fn single_optional(&self) -> Option<Ref<'static, Payment<A>>> {
185 let esdt_transfers: ManagedRef<'static, A, ManagedVec<A, Payment<A>>> = self.all();
186 match esdt_transfers.len() {
187 0 => None,
188 1 => {
189 let value = esdt_transfers.get(0);
190 let lifetime_fix = unsafe {
192 core::mem::transmute::<Ref<'_, Payment<A>>, Ref<'static, Payment<A>>>(value)
193 };
194 Some(lifetime_fix)
195 }
196 _ => A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes()),
197 }
198 }
199
200 pub fn array<const N: usize>(&self) -> [Ref<'static, Payment<A>>; N] {
206 let list = self.all();
207 let array = list.to_array_of_refs::<N>().unwrap_or_else(|| {
208 A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes())
209 });
210 unsafe { core::mem::transmute(array) }
211 }
212
213 pub fn multi_esdt<const N: usize>(&self) -> [Ref<'static, EsdtTokenPayment<A>>; N] {
221 let esdt_transfers = self.all_esdt_transfers();
222 let array = esdt_transfers.to_array_of_refs::<N>().unwrap_or_else(|| {
223 A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes())
224 });
225 unsafe { core::mem::transmute(array) }
226 }
227
228 pub fn multi_egld_or_esdt<const N: usize>(
234 &self,
235 ) -> [Ref<'static, EgldOrEsdtTokenPayment<A>>; N] {
236 let esdt_transfers = self.all_transfers();
237 let array = esdt_transfers.to_array_of_refs::<N>().unwrap_or_else(|| {
238 A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes())
239 });
240 unsafe { core::mem::transmute(array) }
241 }
242
243 pub fn single_esdt(&self) -> Ref<'static, EsdtTokenPayment<A>> {
249 let esdt_transfers = self.all_esdt_transfers();
250 if esdt_transfers.len() != 1 {
251 A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes())
252 }
253 let value = esdt_transfers.get(0);
254 unsafe { core::mem::transmute(value) }
255 }
256
257 pub fn single_fungible_esdt(
263 &self,
264 ) -> (
265 ManagedRef<'static, A, EsdtTokenIdentifier<A>>,
266 ManagedRef<'static, A, BigUint<A>>,
267 ) {
268 let payment = self.single_esdt();
269 if payment.token_nonce != 0 {
270 A::error_api_impl().signal_error(err_msg::FUNGIBLE_TOKEN_EXPECTED.as_bytes());
271 }
272
273 unsafe {
274 (
275 ManagedRef::wrap_handle(payment.token_identifier.get_handle()),
276 ManagedRef::wrap_handle(payment.amount.get_handle()),
277 )
278 }
279 }
280
281 pub fn egld_or_single_esdt(&self) -> EgldOrEsdtTokenPayment<A> {
287 let esdt_transfers_handle = self.all_esdt_transfers_unchecked();
288 let esdt_transfers: ManagedRef<'static, A, ManagedVec<A, EgldOrEsdtTokenPayment<A>>> =
289 unsafe { ManagedRef::wrap_handle(esdt_transfers_handle) };
290 match esdt_transfers.len() {
291 0 => EgldOrEsdtTokenPayment {
292 token_identifier: EgldOrEsdtTokenIdentifier::egld(),
293 token_nonce: 0,
294 amount: self.egld_direct_non_strict().clone(),
295 },
296 1 => esdt_transfers.get(0).clone(),
297 _ => A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_TRANSFERS.as_bytes()),
298 }
299 }
300
301 pub fn egld_or_single_fungible_esdt(&self) -> (EgldOrEsdtTokenIdentifier<A>, BigUint<A>) {
310 let payment = self.egld_or_single_esdt();
311 if payment.token_nonce != 0 {
312 A::error_api_impl().signal_error(err_msg::FUNGIBLE_TOKEN_EXPECTED.as_bytes());
313 }
314
315 (payment.token_identifier, payment.amount)
316 }
317
318 #[deprecated(
322 note = "It comes from a time when only 1 EGLD payment, or ESDT multi-transfer was possible. This is no longer the case. Use `any` instead.",
323 since = "0.64.0"
324 )]
325 pub fn any_payment(&self) -> EgldOrMultiEsdtPayment<A> {
326 let esdt_transfers = self.all_esdt_transfers();
327 if esdt_transfers.is_empty() {
328 EgldOrMultiEsdtPayment::Egld(self.egld_direct_non_strict().clone())
329 } else {
330 EgldOrMultiEsdtPayment::MultiEsdt(esdt_transfers.clone())
331 }
332 }
333}
334
335fn egld_000000_transfer_exists<A>(transfers_vec_handle: A::ManagedBufferHandle) -> bool
336where
337 A: CallValueApi + ErrorApi + ManagedTypeApi,
338{
339 let native_token_handle = BlockchainWrapper::<A>::new().get_native_token_handle();
340 unsafe {
341 let mut iter: ManagedVecPayloadIterator<
342 A,
343 <EsdtTokenPayment<A> as ManagedVecItem>::PAYLOAD,
344 > = ManagedVecPayloadIterator::new(transfers_vec_handle);
345
346 iter.any(|payload| {
347 let token_identifier_handle = RawHandle::read_from_payload(payload.slice_unchecked(0));
348 A::managed_type_impl().mb_eq(
349 native_token_handle.clone(),
350 use_raw_handle(token_identifier_handle),
351 )
352 })
353 }
354}