Skip to content

Commit

Permalink
call value - EGLD+ESDT support & backwards compatibility fix
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-marinica committed Dec 14, 2024
1 parent a74d5ae commit 39e64c2
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 81 deletions.
57 changes: 46 additions & 11 deletions framework/base/src/api/managed_types/const_handles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,27 @@ pub const UNINITIALIZED_HANDLE: RawHandle = i32::MAX;

/// WARNING! With the current VM this still needs to be initialized before use.
pub const BIG_INT_CONST_ZERO: RawHandle = -10;

pub const CALL_VALUE_EGLD: RawHandle = -11;
pub const CALL_VALUE_SINGLE_ESDT: RawHandle = -13;

pub const BIG_INT_TEMPORARY_1: RawHandle = -14;
pub const BIG_INT_TEMPORARY_2: RawHandle = -15;
pub const BIG_FLOAT_TEMPORARY: RawHandle = -16;
pub const BIG_INT_TEMPORARY_1: RawHandle = -11;
pub const BIG_INT_TEMPORARY_2: RawHandle = -12;
pub const BIG_FLOAT_TEMPORARY: RawHandle = -15;

/// WARNING! With the current VM this still needs to be initialized before use.
pub const MBUF_CONST_EMPTY: RawHandle = -20;
pub const CALL_VALUE_MULTI_ESDT: RawHandle = -21;
pub const CALL_VALUE_SINGLE_ESDT_TOKEN_NAME: RawHandle = -22;
pub const CALLBACK_CLOSURE_ARGS_BUFFER: RawHandle = -23;
pub const MBUF_TEMPORARY_1: RawHandle = -25;
pub const MBUF_TEMPORARY_2: RawHandle = -26;
pub const MBUF_EGLD_000000: RawHandle = -27;

pub const ADDRESS_CALLER: RawHandle = -30;
pub const ADDRESS_SELF: RawHandle = -31;

pub const CALL_VALUE_EGLD: RawHandle = -35;
pub const CALL_VALUE_EGLD_MULTI: RawHandle = -36;
pub const CALL_VALUE_EGLD_FROM_ESDT: RawHandle = -37;
pub const CALL_VALUE_MULTI_ESDT: RawHandle = -38;
pub const CALL_VALUE_ALL: RawHandle = -39;
pub const MBUF_EGLD_000000: RawHandle = -40;

pub const CALLBACK_CLOSURE_ARGS_BUFFER: RawHandle = -50;

pub const NEW_HANDLE_START_FROM: RawHandle = -200; // > -100 reserved for APIs

// Vec of 64 entries of 1 bit
Expand All @@ -40,3 +41,37 @@ pub const fn get_scaling_factor_handle(decimals: usize) -> i32 {
let decimals_i32 = decimals as i32;
SCALING_FACTOR_START - decimals_i32
}

/// Payload of the singleton ManagedVec that contains the current single EGLD transfer, modelled as an ESDT payment.
pub const EGLD_PAYMENT_PAYLOAD: [u8; 16] = [
0xff,
0xff,
0xff,
(0x0100 + MBUF_EGLD_000000) as u8,
0,
0,
0,
0,
0,
0,
0,
0,
0xff,
0xff,
0xff,
(0x0100 + CALL_VALUE_EGLD) as u8,
];

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn egld_payment_payload_test() {
let mut bytes = [0u8; 4];
bytes.copy_from_slice(&EGLD_PAYMENT_PAYLOAD[0..4]);
assert_eq!(i32::from_be_bytes(bytes), MBUF_EGLD_000000);
bytes.copy_from_slice(&EGLD_PAYMENT_PAYLOAD[12..16]);
assert_eq!(i32::from_be_bytes(bytes), CALL_VALUE_EGLD);
}
}
29 changes: 18 additions & 11 deletions framework/base/src/api/managed_types/static_var_api_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use bitflags::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StaticVarApiFlags: u8 {
const NONE = 0b00000000;
const CALL_VALUE_EGLD_INITIALIZED = 0b00000001;
const CALL_VALUE_EGLD_MULTI_INITIALIZED = 0b00000010;
const CALL_VALUE_MULTI_ESDT_INITIALIZED = 0b00000100;
const CALL_VALUE_ALL_INITIALIZED = 0b00001000;
const NONE = 0b00000000;
const CALL_VALUE_EGLD_SINGLE_INITIALIZED = 0b00000001;
const CALL_VALUE_ESDT_UNCHECKED_INITIALIZED = 0b00000010;
const CALL_VALUE_EGLD_MULTI_INITIALIZED = 0b00000100;
const CALL_VALUE_EGLD_FROM_ESDT_INITIALIZED = 0b00001000;
const CALL_VALUE_ALL_INITIALIZED = 0b00010000;
}
}

Expand All @@ -32,21 +33,27 @@ pub mod tests {
assert!(current.check_and_set(StaticVarApiFlags::NONE));
assert_eq!(current, StaticVarApiFlags::NONE);

assert!(!current.check_and_set(StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED));
assert_eq!(current, StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED);
assert!(current.check_and_set(StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED));
assert_eq!(current, StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED);
assert!(!current.check_and_set(StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED));
assert_eq!(
current,
StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED
);
assert!(current.check_and_set(StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED));
assert_eq!(
current,
StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED
);

assert!(!current.check_and_set(StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED));
assert_eq!(
current,
StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED
StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED
| StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED
);
assert!(current.check_and_set(StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED));
assert_eq!(
current,
StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED
StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED
| StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED
);
}
Expand Down
169 changes: 113 additions & 56 deletions framework/base/src/contract_base/wrappers/call_value_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ use core::marker::PhantomData;
use crate::{
api::{
const_handles, use_raw_handle, CallValueApi, CallValueApiImpl, ErrorApi, ErrorApiImpl,
HandleConstraints, ManagedBufferApiImpl, ManagedTypeApi, RawHandle, StaticVarApiFlags,
StaticVarApiImpl,
ManagedBufferApiImpl, ManagedTypeApi, RawHandle, StaticVarApiFlags, StaticVarApiImpl,
},
err_msg,
types::{
BigUint, ConstDecimals, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPayment,
EgldOrMultiEsdtPayment, EsdtTokenPayment, ManagedDecimal, ManagedRef, ManagedType,
ManagedVec, ManagedVecItem, ManagedVecItemPayload, ManagedVecPayloadIterator,
ManagedVecRef, TokenIdentifier, EGLD_000000_TOKEN_IDENTIFIER,
big_num_cmp::bi_gt_zero, BigInt, BigUint, ConstDecimals, EgldOrEsdtTokenIdentifier,
EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EsdtTokenPayment, ManagedDecimal,
ManagedRef, ManagedType, ManagedVec, ManagedVecItem, ManagedVecItemPayload,
ManagedVecPayloadIterator, ManagedVecRef, TokenIdentifier, EGLD_000000_TOKEN_IDENTIFIER,
},
};

Expand All @@ -33,12 +32,47 @@ where
}
}

/// Cached transfers from the VM.
fn all_esdt_transfers_unchecked(&self) -> A::ManagedBufferHandle {
let all_transfers_unchecked_handle: A::ManagedBufferHandle =
use_raw_handle(const_handles::CALL_VALUE_MULTI_ESDT);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ESDT_UNCHECKED_INITIALIZED)
{
A::call_value_api_impl()
.load_all_esdt_transfers(all_transfers_unchecked_handle.clone());
}
all_transfers_unchecked_handle
}

/// Cached egld transfer searched for in the ESDT transfers from the VM.
fn egld_from_multi_esdt(&self) -> A::BigIntHandle {
let egld_from_multi_esdt_handle: A::BigIntHandle =
use_raw_handle(const_handles::CALL_VALUE_EGLD_FROM_ESDT);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_EGLD_FROM_ESDT_INITIALIZED)
{
let all_transfers_unchecked_handle = self.all_esdt_transfers_unchecked();
let egld_handle_result = find_egld_000000_transfer::<A>(all_transfers_unchecked_handle);
if let Some(found_egld_handle) = egld_handle_result {
BigInt::<A>::clone_to_handle(
use_raw_handle(found_egld_handle),
egld_from_multi_esdt_handle.clone(),
);
} else {
BigInt::<A>::set_value(egld_from_multi_esdt_handle.clone(), 0);
}
}
egld_from_multi_esdt_handle
}

/// Retrieves the EGLD call value from the VM.
/// Will return 0 in case of an ESDT transfer (cannot have both EGLD and ESDT transfer simultaneously).
///
/// Will return 0 in case of an ESDT transfer, even though EGLD and ESDT transfers are now posible.
pub fn egld_value(&self) -> ManagedRef<'static, A, BigUint<A>> {
let call_value_handle: A::BigIntHandle = use_raw_handle(const_handles::CALL_VALUE_EGLD);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_EGLD_INITIALIZED)
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_EGLD_SINGLE_INITIALIZED)
{
A::call_value_api_impl().load_egld_value(call_value_handle.clone());
}
Expand All @@ -52,23 +86,82 @@ where
)
}

/// The quantity of EGLD transfered, either via simple EGLD transfer, or via ESDT multi-transfer.
pub fn egld_value_multi(&self) -> ManagedRef<'static, A, BigUint<A>> {
let egld_value_multi_handle: A::BigIntHandle =
use_raw_handle(const_handles::CALL_VALUE_EGLD_MULTI);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_EGLD_MULTI_INITIALIZED)
{
let egld_single = self.egld_value();
if bi_gt_zero::<A>(egld_single.get_handle()) {
BigInt::<A>::clone_to_handle(
egld_single.get_handle(),
egld_value_multi_handle.clone(),
);
} else {
let egld_from_multi_esdt_handle = self.egld_from_multi_esdt();
BigInt::<A>::clone_to_handle(
egld_from_multi_esdt_handle,
egld_value_multi_handle.clone(),
);
}
}
unsafe { ManagedRef::wrap_handle(egld_value_multi_handle) }
}

/// Returns all ESDT transfers that accompany this SC call.
/// Will return 0 results if nothing was transfered, or just EGLD.
/// Fully managed underlying types, very efficient.
///
/// Will crash for EGLD + ESDT multi transfers.
pub fn all_esdt_transfers(&self) -> ManagedRef<'static, A, ManagedVec<A, EsdtTokenPayment<A>>> {
let call_value_handle = load_all_transfers::<A>();

let egld_payment = find_egld_000000_transfer::<A>(call_value_handle.clone());
if egld_payment.is_some() {
A::error_api_impl().signal_error(err_msg::ESDT_UNEXPECTED_EGLD.as_bytes())
let multi_esdt_handle: A::ManagedBufferHandle =
use_raw_handle(const_handles::CALL_VALUE_MULTI_ESDT);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ESDT_UNCHECKED_INITIALIZED)
{
let egld_value_multi_handle = self.egld_from_multi_esdt();
if bi_gt_zero::<A>(egld_value_multi_handle) {
A::error_api_impl().signal_error(err_msg::ESDT_UNEXPECTED_EGLD.as_bytes())
}
}
unsafe { ManagedRef::wrap_handle(call_value_handle) }

unsafe { ManagedRef::wrap_handle(multi_esdt_handle) }
}

/// Will return all transfers in the form of a list of EgldOrEsdtTokenPayment.
///
/// Both EGLD and ESDT can be returned.
///
/// In case of a single EGLD transfer, only one item will be returned,
/// the EGLD payment represented as an ESDT transfer (EGLD-000000).
pub fn all_transfers(
&self,
) -> ManagedRef<'static, A, ManagedVec<A, EgldOrEsdtTokenPayment<A>>> {
let (_, all_transfers_handle) = load_all_call_data::<A>();
let all_transfers_handle: A::ManagedBufferHandle =
use_raw_handle(const_handles::CALL_VALUE_ALL);
if !A::static_var_api_impl()
.flag_is_set_or_update(StaticVarApiFlags::CALL_VALUE_ALL_INITIALIZED)
{
let egld_single = self.egld_value();
if bi_gt_zero::<A>(egld_single.get_handle()) {
A::managed_type_impl().mb_overwrite(
use_raw_handle(const_handles::MBUF_EGLD_000000),
EGLD_000000_TOKEN_IDENTIFIER.as_bytes(),
);
let _ = A::managed_type_impl().mb_set_slice(
all_transfers_handle.clone(),
0,
&const_handles::EGLD_PAYMENT_PAYLOAD[..],
);
} else {
// clone all_esdt_transfers_unchecked -> all_transfers
let all_transfers_unchecked_handle = self.all_esdt_transfers_unchecked();
let _ = A::managed_type_impl().mb_set_slice(all_transfers_handle.clone(), 0, &[]);
A::managed_type_impl()
.mb_append(all_transfers_handle.clone(), all_transfers_unchecked_handle);
}
}
unsafe { ManagedRef::wrap_handle(all_transfers_handle) }
}

Expand Down Expand Up @@ -129,14 +222,16 @@ where
///
/// In case no transfer of value happen, it will return a payment of 0 EGLD.
pub fn egld_or_single_esdt(&self) -> EgldOrEsdtTokenPayment<A> {
let esdt_transfers = self.all_esdt_transfers();
let esdt_transfers_handle = self.all_esdt_transfers_unchecked();
let esdt_transfers: ManagedRef<'static, A, ManagedVec<A, EgldOrEsdtTokenPayment<A>>> =
unsafe { ManagedRef::wrap_handle(esdt_transfers_handle) };
match esdt_transfers.len() {
0 => EgldOrEsdtTokenPayment {
token_identifier: EgldOrEsdtTokenIdentifier::egld(),
token_nonce: 0,
amount: self.egld_value().clone_value(),
},
1 => esdt_transfers.get(0).clone().into(),
1 => esdt_transfers.get(0).clone(),
_ => A::error_api_impl().signal_error(err_msg::INCORRECT_NUM_ESDT_TRANSFERS.as_bytes()),
}
}
Expand Down Expand Up @@ -171,22 +266,6 @@ where
}
}

fn load_all_transfers<A>() -> A::ManagedBufferHandle
where
A: CallValueApi + ErrorApi + ManagedTypeApi,
{
// let mut all_transfers_handle: A::ManagedBufferHandle =
// use_raw_handle(A::static_var_api_impl().get_call_value_multi_esdt_handle());
// if all_transfers_handle == const_handles::UNINITIALIZED_HANDLE {
// all_transfers_handle = use_raw_handle(const_handles::CALL_VALUE_MULTI_ESDT);
// A::static_var_api_impl()
// .set_call_value_multi_esdt_handle(all_transfers_handle.get_raw_handle());
// A::call_value_api_impl().load_all_esdt_transfers(all_transfers_handle.clone());
// }
// all_transfers_handle
todo!()
}

fn find_egld_000000_transfer<A>(transfers_vec_handle: A::ManagedBufferHandle) -> Option<RawHandle>
where
A: CallValueApi + ErrorApi + ManagedTypeApi,
Expand Down Expand Up @@ -217,25 +296,3 @@ where
egld_payload.map(|payload| RawHandle::read_from_payload(payload.slice_unchecked(12)))
}
}

fn load_all_call_data<A>() -> (A::BigIntHandle, A::ManagedBufferHandle)
where
A: CallValueApi + ErrorApi + ManagedTypeApi,
{
// let all_transfers_handle = load_all_transfers::<A>();
// let mut egld_amount_handle: A::BigIntHandle =
// use_raw_handle(A::static_var_api_impl().get_call_value_egld_handle());
// if egld_amount_handle == const_handles::UNINITIALIZED_HANDLE {
// let egld_payment = find_egld_000000_transfer::<A>(all_transfers_handle.clone());
// if let Some(egld_amount_raw_handle) = egld_payment {
// egld_amount_handle = use_raw_handle(egld_amount_raw_handle);
// } else {
// egld_amount_handle = use_raw_handle(const_handles::CALL_VALUE_EGLD);
// A::call_value_api_impl().load_egld_value(egld_amount_handle.clone());
// }
// A::static_var_api_impl().set_call_value_egld_handle(egld_amount_handle.get_raw_handle());
// }

// (egld_amount_handle, all_transfers_handle)
todo!()
}
11 changes: 9 additions & 2 deletions framework/base/src/types/managed/basic/big_num_cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::api::{const_handles, BigIntApiImpl, ManagedTypeApi};

use super::BigInt;

pub(crate) fn bi_cmp_0<M>(bi_handle: M::BigIntHandle) -> Ordering
pub(crate) fn bi_cmp_zero<M>(bi_handle: M::BigIntHandle) -> Ordering
where
M: ManagedTypeApi,
{
Expand All @@ -15,13 +15,20 @@ where
}
}

pub(crate) fn bi_gt_zero<M>(bi_handle: M::BigIntHandle) -> bool
where
M: ManagedTypeApi,
{
bi_cmp_zero::<M>(bi_handle) == Ordering::Greater
}

pub(crate) fn bi_cmp_i64<M>(bi_handle: M::BigIntHandle, other: i64) -> Ordering
where
M: ManagedTypeApi,
{
let api = M::managed_type_impl();
if other == 0 {
bi_cmp_0::<M>(bi_handle)
bi_cmp_zero::<M>(bi_handle)
} else {
let big_int_temp_1 = BigInt::<M>::make_temp(const_handles::BIG_INT_TEMPORARY_1, other);
api.bi_cmp(bi_handle, big_int_temp_1)
Expand Down
2 changes: 1 addition & 1 deletion framework/base/src/types/managed/basic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod big_int;
mod big_int_cmp;
mod big_int_operators;
mod big_int_sign;
mod big_num_cmp;
pub(crate) mod big_num_cmp;
pub(crate) mod cast_to_i64;
mod elliptic_curve;
mod managed_buffer;
Expand Down

0 comments on commit 39e64c2

Please sign in to comment.