diff --git a/framework/base/src/contract_base/wrappers/call_value_wrapper.rs b/framework/base/src/contract_base/wrappers/call_value_wrapper.rs index a1f44e026e..777d8bdcf8 100644 --- a/framework/base/src/contract_base/wrappers/call_value_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/call_value_wrapper.rs @@ -10,12 +10,10 @@ use crate::{ BigUint, ConstDecimals, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EsdtTokenPayment, ManagedDecimal, ManagedRef, ManagedType, ManagedVec, ManagedVecItem, ManagedVecItemPayload, ManagedVecPayloadIterator, - ManagedVecRef, TokenIdentifier, + ManagedVecRef, TokenIdentifier, EGLD_000000_TOKEN_IDENTIFIER, }, }; -const EGLD_000000_TOKEN_IDENTIFIER: &str = "EGLD-000000"; - #[derive(Default)] pub struct CallValueWrapper where diff --git a/framework/base/src/types/managed/wrapped.rs b/framework/base/src/types/managed/wrapped.rs index 52d827c79f..679890964b 100644 --- a/framework/base/src/types/managed/wrapped.rs +++ b/framework/base/src/types/managed/wrapped.rs @@ -31,7 +31,7 @@ mod traits; pub use big_uint::BigUint; pub use builder::*; -pub use egld_or_esdt_token_identifier::EgldOrEsdtTokenIdentifier; +pub use egld_or_esdt_token_identifier::{EgldOrEsdtTokenIdentifier, EGLD_000000_TOKEN_IDENTIFIER}; pub use egld_or_esdt_token_payment::{EgldOrEsdtTokenPayment, EgldOrEsdtTokenPaymentRefs}; pub use egld_or_multi_esdt_payment::{EgldOrMultiEsdtPayment, EgldOrMultiEsdtPaymentRefs}; pub(crate) use encoded_managed_vec_item::EncodedManagedVecItem; diff --git a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_identifier.rs b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_identifier.rs index 7b46cb2e50..8dc341f657 100644 --- a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_identifier.rs +++ b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_identifier.rs @@ -2,16 +2,21 @@ use alloc::string::ToString; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, - api::{HandleConstraints, ManagedTypeApi}, + api::{ + const_handles, use_raw_handle, ErrorApiImpl, HandleConstraints, ManagedBufferApiImpl, + ManagedTypeApi, + }, codec::*, derive::ManagedVecItem, formatter::{FormatByteReceiver, SCDisplay, SCLowerHex}, proxy_imports::TestTokenIdentifier, - types::{ManagedBuffer, ManagedOption, ManagedRef, ManagedType, TokenIdentifier}, + types::{ManagedBuffer, ManagedRef, ManagedType, TokenIdentifier}, }; use crate as multiversx_sc; // required by the ManagedVecItem derive +pub const EGLD_000000_TOKEN_IDENTIFIER: &str = "EGLD-000000"; + /// Specialized type for handling either EGLD or ESDT token identifiers. /// /// Equivalent to a structure of the form @@ -30,19 +35,45 @@ use crate as multiversx_sc; // required by the ManagedVecItem derive #[repr(transparent)] #[derive(ManagedVecItem, Clone)] pub struct EgldOrEsdtTokenIdentifier { - pub(crate) data: ManagedOption>, + pub(crate) buffer: ManagedBuffer, +} + +impl ManagedType for EgldOrEsdtTokenIdentifier { + type OwnHandle = M::ManagedBufferHandle; + + #[inline] + unsafe fn from_handle(handle: M::ManagedBufferHandle) -> Self { + EgldOrEsdtTokenIdentifier { + buffer: ManagedBuffer::from_handle(handle), + } + } + + fn get_handle(&self) -> M::ManagedBufferHandle { + self.buffer.get_handle() + } + + unsafe fn forget_into_handle(self) -> Self::OwnHandle { + self.buffer.forget_into_handle() + } + + fn transmute_from_handle_ref(handle_ref: &M::ManagedBufferHandle) -> &Self { + unsafe { core::mem::transmute(handle_ref) } + } + + fn transmute_from_handle_ref_mut(handle_ref: &mut M::ManagedBufferHandle) -> &mut Self { + unsafe { core::mem::transmute(handle_ref) } + } } impl EgldOrEsdtTokenIdentifier { /// This special representation is interpreted as the EGLD token. - #[allow(clippy::needless_borrow)] // clippy is wrog here, there is no other way - pub const EGLD_REPRESENTATION: &'static [u8; 4] = &b"EGLD"; + pub const EGLD_REPRESENTATION: &'static [u8; 4] = b"EGLD"; /// New instance of the special EGLD token representation. #[inline] pub fn egld() -> Self { - Self { - data: ManagedOption::none(), + EgldOrEsdtTokenIdentifier { + buffer: ManagedBuffer::from(EGLD_000000_TOKEN_IDENTIFIER), } } @@ -52,30 +83,36 @@ impl EgldOrEsdtTokenIdentifier { where TokenIdentifier: From, { - Self { - data: ManagedOption::some(TokenIdentifier::from(token_identifier)), - } + let ti_obj = TokenIdentifier::from(token_identifier); + ti_obj.data } pub fn parse(data: ManagedBuffer) -> Self { if data == Self::EGLD_REPRESENTATION { Self::egld() } else { - Self::esdt(TokenIdentifier::from(data)) + Self { buffer: data } } } #[inline] pub fn is_egld(&self) -> bool { - self.data.is_none() + M::managed_type_impl().mb_overwrite( + use_raw_handle(const_handles::MBUF_EGLD_000000), + EGLD_000000_TOKEN_IDENTIFIER.as_bytes(), + ); + M::managed_type_impl().mb_eq( + use_raw_handle(const_handles::MBUF_EGLD_000000), + self.buffer.handle.clone(), + ) } #[inline] pub fn is_esdt(&self) -> bool { - self.data.is_some() + !self.is_egld() } - #[inline] + /// Returns "EGLD" or the token identifier. pub fn into_name(self) -> ManagedBuffer { self.map_or_else( (), @@ -95,12 +132,31 @@ impl EgldOrEsdtTokenIdentifier { ) } + #[inline] + pub fn into_managed_buffer(self) -> ManagedBuffer { + self.buffer + } + + #[inline] + pub fn as_managed_buffer(&self) -> &ManagedBuffer { + &self.buffer + } + + #[inline] + pub fn to_boxed_bytes(&self) -> crate::types::heap::BoxedBytes { + self.buffer.to_boxed_bytes() + } + pub fn map_or_else(self, context: Context, for_egld: D, for_esdt: F) -> R where D: FnOnce(Context) -> R, F: FnOnce(Context, TokenIdentifier) -> R, { - self.data.map_or_else(context, for_egld, for_esdt) + if self.is_egld() { + for_egld(context) + } else { + unsafe { for_esdt(context, TokenIdentifier::esdt_unchecked(self)) } + } } pub fn map_ref_or_else(&self, context: Context, for_egld: D, for_esdt: F) -> R @@ -108,30 +164,65 @@ impl EgldOrEsdtTokenIdentifier { D: FnOnce(Context) -> R, F: FnOnce(Context, &TokenIdentifier) -> R, { - self.data.map_ref_or_else(context, for_egld, for_esdt) + if self.is_egld() { + for_egld(context) + } else { + unsafe { + let token_identifier = + ManagedRef::<'_, M, TokenIdentifier>::wrap_handle(self.get_handle()); + for_esdt(context, &token_identifier) + } + } } pub fn unwrap_esdt(self) -> TokenIdentifier { - self.data.unwrap_or_sc_panic("ESDT expected") + self.map_or_else( + (), + |()| M::error_api_impl().signal_error(b"ESDT expected"), + |(), token_identifier| token_identifier, + ) } /// Representation of the object as an `Option`. /// /// Because it does not consume `self` only a reference to the ESDT token identifier can be returned. pub fn as_esdt_option(&self) -> Option>> { - self.data.as_option() + if self.is_egld() { + None + } else { + unsafe { + Some(ManagedRef::<'_, M, TokenIdentifier>::wrap_handle( + self.get_handle(), + )) + } + } } /// Converts `self` into an `Option`. Consumes `self` in the process. pub fn into_esdt_option(self) -> Option> { - self.data.into_option() + self.map_or_else((), |()| None, |(), token_identifier| Some(token_identifier)) + } +} + +impl From> for EgldOrEsdtTokenIdentifier { + #[inline] + fn from(buffer: ManagedBuffer) -> Self { + EgldOrEsdtTokenIdentifier { buffer } + } +} + +impl From<&[u8]> for EgldOrEsdtTokenIdentifier { + fn from(bytes: &[u8]) -> Self { + EgldOrEsdtTokenIdentifier { + buffer: ManagedBuffer::new_from_bytes(bytes), + } } } impl PartialEq for EgldOrEsdtTokenIdentifier { #[inline] fn eq(&self, other: &Self) -> bool { - self.data == other.data + self.buffer == other.buffer } } @@ -155,10 +246,10 @@ impl NestedEncode for EgldOrEsdtTokenIdentifier { O: NestedEncodeOutput, H: EncodeErrorHandler, { - if let Some(token_identifier) = self.data.as_option() { - token_identifier.dep_encode_or_handle_err(dest, h) - } else { + if self.is_egld() { (&Self::EGLD_REPRESENTATION[..]).dep_encode_or_handle_err(dest, h) + } else { + self.buffer.dep_encode_or_handle_err(dest, h) } } } @@ -170,10 +261,10 @@ impl TopEncode for EgldOrEsdtTokenIdentifier { O: TopEncodeOutput, H: EncodeErrorHandler, { - if let Some(token_identifier) = self.data.as_option() { - token_identifier.top_encode_or_handle_err(output, h) - } else { + if self.is_egld() { (&Self::EGLD_REPRESENTATION[..]).top_encode_or_handle_err(output, h) + } else { + self.buffer.top_encode_or_handle_err(output, h) } } } @@ -231,12 +322,12 @@ impl TypeAbi for EgldOrEsdtTokenIdentifier { impl SCDisplay for EgldOrEsdtTokenIdentifier { fn fmt(&self, f: &mut F) { - if let Some(token_identifier) = self.data.as_option() { - let cast_handle = token_identifier.get_handle().cast_or_signal_error::(); + if self.is_egld() { + f.append_bytes(Self::EGLD_REPRESENTATION); + } else { + let cast_handle = self.buffer.get_handle().cast_or_signal_error::(); let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) }; f.append_managed_buffer(&wrap_cast); - } else { - f.append_bytes(Self::EGLD_REPRESENTATION); } } } @@ -245,12 +336,12 @@ const EGLD_REPRESENTATION_HEX: &[u8] = b"45474C44"; impl SCLowerHex for EgldOrEsdtTokenIdentifier { fn fmt(&self, f: &mut F) { - if let Some(token_identifier) = self.data.as_option() { - let cast_handle = token_identifier.get_handle().cast_or_signal_error::(); + if self.is_egld() { + f.append_bytes(EGLD_REPRESENTATION_HEX); + } else { + let cast_handle = self.buffer.get_handle().cast_or_signal_error::(); let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) }; f.append_managed_buffer_lower_hex(&wrap_cast); - } else { - f.append_bytes(EGLD_REPRESENTATION_HEX); } } } diff --git a/framework/base/src/types/managed/wrapped/token_identifier.rs b/framework/base/src/types/managed/wrapped/token_identifier.rs index 39fd17f5e1..6228fba515 100644 --- a/framework/base/src/types/managed/wrapped/token_identifier.rs +++ b/framework/base/src/types/managed/wrapped/token_identifier.rs @@ -19,7 +19,7 @@ use super::{EgldOrEsdtTokenIdentifier, ManagedRef}; #[repr(transparent)] #[derive(Clone)] pub struct TokenIdentifier { - buffer: ManagedBuffer, + pub(crate) data: EgldOrEsdtTokenIdentifier, } impl ManagedType for TokenIdentifier { @@ -28,16 +28,16 @@ impl ManagedType for TokenIdentifier { #[inline] unsafe fn from_handle(handle: M::ManagedBufferHandle) -> Self { TokenIdentifier { - buffer: ManagedBuffer::from_handle(handle), + data: EgldOrEsdtTokenIdentifier::from_handle(handle), } } fn get_handle(&self) -> M::ManagedBufferHandle { - self.buffer.get_handle() + self.data.get_handle() } unsafe fn forget_into_handle(self) -> Self::OwnHandle { - self.buffer.forget_into_handle() + self.data.forget_into_handle() } fn transmute_from_handle_ref(handle_ref: &M::ManagedBufferHandle) -> &Self { @@ -50,36 +50,44 @@ impl ManagedType for TokenIdentifier { } impl TokenIdentifier { + /// Creates a new TokenIdentifier without verifying that it is not EGLD-000000. + /// + /// ## Safety + /// + /// Calling it for the EGLD token might lead to unexpected bugs. + pub unsafe fn esdt_unchecked(data: EgldOrEsdtTokenIdentifier) -> Self { + Self { data } + } + #[inline] pub fn from_esdt_bytes>>(bytes: B) -> Self { - TokenIdentifier { - buffer: bytes.into(), - } + TokenIdentifier::from(bytes.into()) } #[inline] pub fn into_managed_buffer(self) -> ManagedBuffer { - self.buffer + self.data.into_managed_buffer() } #[inline] pub fn as_managed_buffer(&self) -> &ManagedBuffer { - &self.buffer + self.data.as_managed_buffer() } #[inline] pub fn to_boxed_bytes(&self) -> crate::types::heap::BoxedBytes { - self.buffer.to_boxed_bytes() + self.data.to_boxed_bytes() } pub fn is_valid_esdt_identifier(&self) -> bool { - M::managed_type_impl().validate_token_identifier(self.buffer.handle.clone()) + M::managed_type_impl().validate_token_identifier(self.data.buffer.handle.clone()) } pub fn ticker(&self) -> ManagedBuffer { - let token_id_len = self.buffer.len(); + let buffer = self.as_managed_buffer(); + let token_id_len = buffer.len(); let ticker_len = M::managed_type_impl().get_token_ticker_len(token_id_len); - self.buffer.copy_slice(0, ticker_len).unwrap_or_else(|| { + buffer.copy_slice(0, ticker_len).unwrap_or_else(|| { M::error_api_impl().signal_error(err_msg::BAD_TOKEN_TICKER_FORMAT.as_bytes()) }) } @@ -88,15 +96,13 @@ impl TokenIdentifier { impl From> for TokenIdentifier { #[inline] fn from(buffer: ManagedBuffer) -> Self { - TokenIdentifier { buffer } + EgldOrEsdtTokenIdentifier::from(buffer).unwrap_esdt() } } impl From<&[u8]> for TokenIdentifier { fn from(bytes: &[u8]) -> Self { - TokenIdentifier { - buffer: ManagedBuffer::new_from_bytes(bytes), - } + EgldOrEsdtTokenIdentifier::from(bytes).unwrap_esdt() } } @@ -115,7 +121,7 @@ impl From<&crate::types::heap::String> for TokenIdentifier impl PartialEq for TokenIdentifier { #[inline] fn eq(&self, other: &Self) -> bool { - self.buffer == other.buffer + self.data == other.data } } @@ -139,7 +145,7 @@ impl NestedEncode for TokenIdentifier { O: NestedEncodeOutput, H: EncodeErrorHandler, { - self.buffer.dep_encode_or_handle_err(dest, h) + self.data.dep_encode_or_handle_err(dest, h) } } @@ -150,7 +156,7 @@ impl TopEncode for TokenIdentifier { O: TopEncodeOutput, H: EncodeErrorHandler, { - self.buffer.top_encode_or_handle_err(output, h) + self.data.top_encode_or_handle_err(output, h) } } @@ -160,9 +166,11 @@ impl NestedDecode for TokenIdentifier { I: NestedDecodeInput, H: DecodeErrorHandler, { - Ok(TokenIdentifier::from( - ManagedBuffer::dep_decode_or_handle_err(input, h)?, - )) + unsafe { + Ok(TokenIdentifier::esdt_unchecked( + EgldOrEsdtTokenIdentifier::dep_decode_or_handle_err(input, h)?, + )) + } } } @@ -172,9 +180,11 @@ impl TopDecode for TokenIdentifier { I: TopDecodeInput, H: DecodeErrorHandler, { - Ok(TokenIdentifier::from( - ManagedBuffer::top_decode_or_handle_err(input, h)?, - )) + unsafe { + Ok(TokenIdentifier::esdt_unchecked( + EgldOrEsdtTokenIdentifier::top_decode_or_handle_err(input, h)?, + )) + } } } @@ -198,7 +208,7 @@ impl TypeAbi for TokenIdentifier { impl SCDisplay for TokenIdentifier { fn fmt(&self, f: &mut F) { - let cast_handle = self.buffer.get_handle().cast_or_signal_error::(); + let cast_handle = self.get_handle().cast_or_signal_error::(); let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) }; f.append_managed_buffer(&wrap_cast); } @@ -206,7 +216,7 @@ impl SCDisplay for TokenIdentifier { impl SCLowerHex for TokenIdentifier { fn fmt(&self, f: &mut F) { - let cast_handle = self.buffer.get_handle().cast_or_signal_error::(); + let cast_handle = self.get_handle().cast_or_signal_error::(); let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) }; f.append_managed_buffer_lower_hex(&wrap_cast); } @@ -214,7 +224,7 @@ impl SCLowerHex for TokenIdentifier { impl core::fmt::Display for TokenIdentifier { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let bytes = self.buffer.to_boxed_bytes(); + let bytes = self.to_boxed_bytes(); let s = alloc::string::String::from_utf8_lossy(bytes.as_slice()); s.fmt(f) }