Skip to content

Commit

Permalink
call data const handles stored on stack (experiment)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-marinica committed Dec 13, 2024
1 parent 4bc903f commit 2c7cab0
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 44 deletions.
4 changes: 1 addition & 3 deletions framework/base/src/contract_base/contract_base_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ pub trait ContractBase: Sized {
/// Gateway into the call value retrieval functionality.
/// The payment annotations should normally be the ones to handle this,
/// but the developer is also given direct access to the API.
fn call_value(&self) -> CallValueWrapper<Self::Api> {
CallValueWrapper::new()
}
fn call_value(&self) -> CallValueWrapper<Self::Api>;

/// Gateway to the functionality related to sending transactions from the current contract.
#[inline]
Expand Down
29 changes: 26 additions & 3 deletions framework/base/src/contract_base/universal_contract_obj.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
use core::marker::PhantomData;
use core::{cell::UnsafeCell, marker::PhantomData};

use crate::api::VMApi;
use crate::api::{const_handles, RawHandle, VMApi};

use super::ContractBase;
use super::{CallValueWrapper, ContractBase};

pub struct ContractObjData {
pub call_value_egld_handle: RawHandle,
pub call_value_multi_esdt_handle: RawHandle,
}

impl Default for ContractObjData {
fn default() -> Self {
ContractObjData {
call_value_egld_handle: const_handles::UNINITIALIZED_HANDLE,
call_value_multi_esdt_handle: const_handles::UNINITIALIZED_HANDLE,
}
}
}

/// A unique empty structure that automatically implements all smart contract traits.
///
Expand All @@ -18,15 +32,20 @@ where
A: VMApi,
{
_phantom: PhantomData<A>,
pub data: UnsafeCell<ContractObjData>,
}

unsafe impl<A> Sync for UniversalContractObj<A> where A: VMApi {}
unsafe impl<A> Send for UniversalContractObj<A> where A: VMApi {}

impl<A> UniversalContractObj<A>
where
A: VMApi,
{
pub fn new() -> Self {
Self {
_phantom: PhantomData,
data: UnsafeCell::new(ContractObjData::default()),
}
}
}
Expand All @@ -45,4 +64,8 @@ where
A: VMApi,
{
type Api = A;

fn call_value(&self) -> CallValueWrapper<'_, Self::Api> {
CallValueWrapper::new(&self.data)
}
}
40 changes: 19 additions & 21 deletions framework/base/src/contract_base/wrappers/call_value_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use core::marker::PhantomData;
use core::{cell::UnsafeCell, marker::PhantomData};

use crate::{
api::{
const_handles, use_raw_handle, CallValueApi, CallValueApiImpl, ErrorApi, ErrorApiImpl,
HandleConstraints, ManagedTypeApi, StaticVarApiImpl,
ManagedTypeApi,
},
contract_base::ContractObjData,
err_msg,
types::{
BigUint, ConstDecimals, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPayment,
Expand All @@ -13,35 +14,34 @@ use crate::{
},
};

#[derive(Default)]
pub struct CallValueWrapper<A>
pub struct CallValueWrapper<'a, A>
where
A: CallValueApi + ErrorApi + ManagedTypeApi,
{
_phantom: PhantomData<A>,
pub data_cell: &'a UnsafeCell<ContractObjData>,
}

impl<A> CallValueWrapper<A>
impl<'a, A> CallValueWrapper<'a, A>
where
A: CallValueApi + ErrorApi + ManagedTypeApi,
{
pub fn new() -> Self {
pub fn new(data_cell: &'a UnsafeCell<ContractObjData>) -> Self {
CallValueWrapper {
_phantom: PhantomData,
data_cell,
}
}

/// 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).
pub fn egld_value(&self) -> ManagedRef<'static, A, BigUint<A>> {
let mut call_value_handle: A::BigIntHandle =
use_raw_handle(A::static_var_api_impl().get_call_value_egld_handle());
if call_value_handle == const_handles::UNINITIALIZED_HANDLE {
call_value_handle = use_raw_handle(const_handles::CALL_VALUE_EGLD);
A::static_var_api_impl().set_call_value_egld_handle(call_value_handle.get_raw_handle());
A::call_value_api_impl().load_egld_value(call_value_handle.clone());
let data = unsafe { &mut *self.data_cell.get() };
if data.call_value_egld_handle == const_handles::UNINITIALIZED_HANDLE {
data.call_value_egld_handle = const_handles::CALL_VALUE_EGLD;
A::call_value_api_impl().load_egld_value(use_raw_handle(data.call_value_egld_handle));
}
unsafe { ManagedRef::wrap_handle(call_value_handle) }
unsafe { ManagedRef::wrap_handle(use_raw_handle(data.call_value_egld_handle)) }
}

/// Returns the EGLD call value from the VM as ManagedDecimal
Expand All @@ -55,15 +55,13 @@ where
/// Will return 0 results if nothing was transfered, or just EGLD.
/// Fully managed underlying types, very efficient.
pub fn all_esdt_transfers(&self) -> ManagedRef<'static, A, ManagedVec<A, EsdtTokenPayment<A>>> {
let mut call_value_handle: A::ManagedBufferHandle =
use_raw_handle(A::static_var_api_impl().get_call_value_multi_esdt_handle());
if call_value_handle == const_handles::UNINITIALIZED_HANDLE {
call_value_handle = use_raw_handle(const_handles::CALL_VALUE_MULTI_ESDT);
A::static_var_api_impl()
.set_call_value_multi_esdt_handle(call_value_handle.get_raw_handle());
A::call_value_api_impl().load_all_esdt_transfers(call_value_handle.clone());
let data = unsafe { &mut *self.data_cell.get() };
if data.call_value_multi_esdt_handle == const_handles::UNINITIALIZED_HANDLE {
data.call_value_multi_esdt_handle = const_handles::CALL_VALUE_MULTI_ESDT;
A::call_value_api_impl()
.load_all_esdt_transfers(use_raw_handle(data.call_value_multi_esdt_handle));
}
unsafe { ManagedRef::wrap_handle(call_value_handle) }
unsafe { ManagedRef::wrap_handle(use_raw_handle(data.call_value_multi_esdt_handle)) }
}

/// Verify and casts the received multi ESDT transfer in to an array.
Expand Down
33 changes: 18 additions & 15 deletions framework/base/src/io/call_value_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::{
const_handles, use_raw_handle, CallValueApi, CallValueApiImpl, ErrorApi, ErrorApiImpl,
ManagedBufferApiImpl, ManagedTypeApi,
},
contract_base::CallValueWrapper,
err_msg,
imports::ContractBase,
types::{
BigUint, EgldOrEsdtTokenIdentifier, EsdtTokenPayment, ManagedRef, ManagedType, ManagedVec,
},
Expand Down Expand Up @@ -38,11 +38,12 @@ where
/// Called initially in the generated code whenever `#[payable("<token identifier>")]` annotation is provided.
///
/// Was never really used, expected to be deprecated/removed.
pub fn payable_single_specific_token<A>(expected_tokend_identifier: &str)
pub fn payable_single_specific_token<A, O>(obj: &O, expected_tokend_identifier: &str)
where
A: CallValueApi + ManagedTypeApi + ErrorApi,
O: ContractBase<Api = A>,
{
let transfers = CallValueWrapper::<A>::new().all_esdt_transfers();
let transfers = obj.call_value().all_esdt_transfers();
if transfers.len() != 1 {
A::error_api_impl().signal_error(err_msg::SINGLE_ESDT_EXPECTED.as_bytes());
}
Expand All @@ -62,37 +63,39 @@ where
}

/// Initializes an argument annotated with `#[payment_amount]` or `#[payment]`.
pub fn arg_payment_amount<A>() -> BigUint<A>
pub fn arg_payment_amount<A, O>(obj: &O) -> BigUint<A>
where
A: CallValueApi + ManagedTypeApi,
O: ContractBase<Api = A>,
{
CallValueWrapper::<A>::new().egld_or_single_esdt().amount
obj.call_value().egld_or_single_esdt().amount
}

/// Initializes an argument annotated with `#[payment_token]`.
pub fn arg_payment_token<A>() -> EgldOrEsdtTokenIdentifier<A>
pub fn arg_payment_token<A, O>(obj: &O) -> EgldOrEsdtTokenIdentifier<A>
where
A: CallValueApi + ManagedTypeApi,
O: ContractBase<Api = A>,
{
CallValueWrapper::<A>::new()
.egld_or_single_esdt()
.token_identifier
obj.call_value().egld_or_single_esdt().token_identifier
}

/// Initializes an argument annotated with `#[payment_nonce]`.
pub fn arg_payment_nonce<A>() -> u64
pub fn arg_payment_nonce<A, O>(obj: &O) -> u64
where
A: CallValueApi + ManagedTypeApi,
O: ContractBase<Api = A>,
{
CallValueWrapper::<A>::new()
.egld_or_single_esdt()
.token_nonce
obj.call_value().egld_or_single_esdt().token_nonce
}

/// Initializes an argument annotated with `#[payment_multi]`.
pub fn arg_payment_multi<A>() -> ManagedRef<'static, A, ManagedVec<A, EsdtTokenPayment<A>>>
pub fn arg_payment_multi<A, O>(
obj: &O,
) -> ManagedRef<'static, A, ManagedVec<A, EsdtTokenPayment<A>>>
where
A: CallValueApi + ManagedTypeApi,
O: ContractBase<Api = A>,
{
CallValueWrapper::<A>::new().all_esdt_transfers()
obj.call_value().all_esdt_transfers()
}
4 changes: 2 additions & 2 deletions framework/derive/src/generate/payable_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn call_value_init_snippet(mpm: MethodPayableMetadata) -> proc_macro2::TokenStre
},
MethodPayableMetadata::SingleEsdtToken(token_identifier) => {
quote! {
multiversx_sc::io::call_value_init::payable_single_specific_token::<Self::Api>(#token_identifier);
multiversx_sc::io::call_value_init::payable_single_specific_token::<Self::Api, _>(&*self, #token_identifier);
}
},
MethodPayableMetadata::AnyToken => {
Expand All @@ -51,7 +51,7 @@ fn opt_payment_arg_snippet(
.map(|arg| {
let pat = &arg.pat;
quote! {
let #pat = multiversx_sc::io::call_value_init::#init_fn_name::<Self::Api>();
let #pat = multiversx_sc::io::call_value_init::#init_fn_name::<Self::Api, Self>(&*self);
}
})
.unwrap_or_default()
Expand Down
4 changes: 4 additions & 0 deletions framework/derive/src/generate/snippets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub fn impl_contract_base() -> proc_macro2::TokenStream {
A: multiversx_sc::api::VMApi,
{
type Api = A;

fn call_value(&self) -> multiversx_sc::contract_base::CallValueWrapper<'_, Self::Api> {
multiversx_sc::contract_base::CallValueWrapper::new(&self.0.data)
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions framework/scenario/tests/contract_without_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,10 @@ mod sample_adder {
A: multiversx_sc::api::VMApi,
{
type Api = A;

fn call_value(&self) -> multiversx_sc::contract_base::CallValueWrapper<'_, Self::Api> {
multiversx_sc::contract_base::CallValueWrapper::new(&self.0.data)
}
}

impl<A> super::module_1::AutoImpl for ContractObj<A> where A: multiversx_sc::api::VMApi {}
Expand Down

0 comments on commit 2c7cab0

Please sign in to comment.