Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unified syntax prototype #1255

Merged
merged 14 commits into from
Feb 5, 2024
19 changes: 12 additions & 7 deletions contracts/examples/adder/tests/adder_blackbox_with_values_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use adder::*;
use multiversx_sc::storage::mappers::SingleValue;
use multiversx_sc::{
storage::mappers::SingleValue,
types::{AddressExpr, ScExpr},
};
use multiversx_sc_scenario::{api::StaticApi, num_bigint::BigUint, scenario_model::*, *};

const ADDER_PATH_EXPR: &str = "mxsc:output/adder.mxsc.json";
Expand Down Expand Up @@ -41,12 +44,14 @@ fn adder_blackbox_with_values() {
.call(adder_contract.sum())
.expect_value(SingleValue::from(BigUint::from(5u32))),
)
.sc_call(
ScCallStep::new()
.from(owner_address)
.to(&adder_contract)
.call(adder_contract.add(3u32)),
)
.tx(|tx| {
tx.from(AddressExpr("owner"))
.to(ScExpr("adder"))
.call(adder_contract.add(3u32))
.callback(WithTxResult(|response| {
assert!(response.tx_error.is_success());
}))
})
.check_state_step(
CheckStateStep::new()
.put_account(owner_address, CheckAccount::new())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,12 @@ pub trait RewardsDistribution:
}
}

self.send()
.direct_non_zero_egld(&caller, &total_egld_reward);
self.send().direct_multi(&caller, &rewards);
self.send().direct_multi(&caller, &nfts);
self.tx().to(&caller).egld(total_egld_reward).transfer();
self.tx().to(&caller).multi_esdt(rewards).transfer();
self.tx()
.to(&caller)
.multi_esdt(nfts.clone_value())
.transfer();
}

fn claim_reward_token(
Expand Down
13 changes: 7 additions & 6 deletions contracts/feature-tests/composability/vault/src/vault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,12 @@ pub trait Vault {
let caller = self.blockchain().get_caller();

if let Some(esdt_token_id) = token.into_esdt_option() {
self.send()
.direct_esdt(&caller, &esdt_token_id, nonce, &amount);
self.tx()
.to(caller)
.esdt((esdt_token_id, nonce, amount))
.transfer();
} else {
self.send().direct_egld(&caller, &amount);
self.tx().to(caller).egld(amount).transfer();
}
}

Expand All @@ -185,7 +187,7 @@ pub trait Vault {
all_payments.push(EsdtTokenPayment::new(token_id, nonce, amount));
}

self.send().direct_multi(&caller, &all_payments);
self.tx().to(caller).multi_esdt(all_payments).transfer();
}

#[payable("*")]
Expand Down Expand Up @@ -223,8 +225,7 @@ pub trait Vault {
));
}

self.send()
.direct_multi(&self.blockchain().get_caller(), &new_tokens);
self.tx().to_caller().multi_esdt(new_tokens).transfer();
}

#[event("accept_funds")]
Expand Down
11 changes: 10 additions & 1 deletion framework/base/src/contract_base/contract_base_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use super::{
BlockchainWrapper, CallValueWrapper, CryptoWrapper, ErrorHelper, ManagedSerializer,
SendRawWrapper, SendWrapper, StorageRawWrapper,
};
use crate::api::VMApi;
use crate::{
api::VMApi,
types::{Tx, TxBaseWithEnv, TxScEnv},
};

/// Interface to be used by the actual smart contract code.
///
Expand All @@ -26,6 +29,12 @@ pub trait ContractBase: Sized {
SendWrapper::new()
}

/// Starts the declaration of a new transaction.
#[inline]
fn tx(&self) -> TxBaseWithEnv<TxScEnv<Self::Api>> {
Tx::new_tx_from_sc()
}

/// Low-level functionality related to sending transactions from the current contract.
///
/// For almost all cases contracts should instead use `self.send()` and `ContractCall`.
Expand Down
12 changes: 2 additions & 10 deletions framework/base/src/contract_base/wrappers/send_raw_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,12 @@ where
&self,
to: &ManagedAddress<A>,
token: &TokenIdentifier<A>,
egld_value: &BigUint<A>,
value: &BigUint<A>,
gas_limit: u64,
endpoint_name: &ManagedBuffer<A>,
arg_buffer: &ManagedArgBuffer<A>,
) -> Result<(), &'static [u8]> {
self.transfer_esdt_nft_execute(
to,
token,
0,
egld_value,
gas_limit,
endpoint_name,
arg_buffer,
)
self.transfer_esdt_nft_execute(to, token, 0, value, gas_limit, endpoint_name, arg_buffer)
}

#[allow(clippy::too_many_arguments)]
Expand Down
22 changes: 10 additions & 12 deletions framework/base/src/contract_base/wrappers/send_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
types::{
BigUint, ContractCall, ContractCallNoPayment, EgldOrEsdtTokenIdentifier, EsdtTokenPayment,
ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec, TokenIdentifier,
Tx,
},
};

Expand Down Expand Up @@ -314,10 +315,10 @@ where
nonce: u64,
amount: BigUint<A>,
) -> ! {
ContractCallNoPayment::<A, ()>::new(to, ManagedBuffer::new())
.with_esdt_transfer((token, nonce, amount))
.async_call()
.call_and_exit_ignore_callback()
Tx::new_tx_from_sc()
.to(to)
.esdt((token, nonce, amount))
.async_call_and_exit()
}

/// Performs a simple ESDT/NFT transfer, but via async call.
Expand All @@ -339,10 +340,7 @@ where
if amount == 0 {
return;
}
ContractCallNoPayment::<A, ()>::new(to, ManagedBuffer::new())
.with_esdt_transfer((token, nonce, amount))
.async_call()
.call_and_exit_ignore_callback()
self.transfer_esdt_via_async_call(to, token, nonce, amount)
}

/// Sends multiple ESDT tokens to a target address, via an async call.
Expand All @@ -351,10 +349,10 @@ where
to: ManagedAddress<A>,
payments: ManagedVec<A, EsdtTokenPayment<A>>,
) -> ! {
ContractCallNoPayment::<A, ()>::new(to, ManagedBuffer::new())
.with_multi_token_transfer(payments)
.async_call()
.call_and_exit_ignore_callback()
Tx::new_tx_from_sc()
.to(to)
.multi_esdt(payments)
.async_call_and_exit()
}

/// Creates a call to the `ClaimDeveloperRewards` builtin function.
Expand Down
51 changes: 51 additions & 0 deletions framework/base/src/types/interaction/annotated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::{
api::ManagedTypeApi,
types::{ManagedAddress, ManagedBuffer},
};

use super::TxEnv;

pub trait AnnotatedValue<Env, T>
where
Env: TxEnv,
{
fn annotation(&self, _env: &Env) -> ManagedBuffer<Env::Api>;

fn into_value(self) -> T;

fn with_value_ref<F: FnOnce(&T)>(&self, f: F);
}

impl<Env> AnnotatedValue<Env, ManagedAddress<Env::Api>> for ManagedAddress<Env::Api>
where
Env: TxEnv,
{
fn annotation(&self, _env: &Env) -> ManagedBuffer<Env::Api> {
self.hex_expr()
}

fn into_value(self) -> ManagedAddress<Env::Api> {
self
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Env::Api>)>(&self, f: F) {
f(self)
}
}

impl<Env> AnnotatedValue<Env, ManagedAddress<Env::Api>> for &ManagedAddress<Env::Api>
where
Env: TxEnv,
{
fn annotation(&self, _env: &Env) -> crate::types::ManagedBuffer<Env::Api> {
self.hex_expr()
}

fn into_value(self) -> ManagedAddress<Env::Api> {
self.clone()
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Env::Api>)>(&self, f: F) {
f(self)
}
}
62 changes: 24 additions & 38 deletions framework/base/src/types/interaction/async_call.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,46 @@
use crate::{
api::{CallTypeApi, StorageWriteApi},
contract_base::SendRawWrapper,
types::{BigUint, CallbackClosure, ManagedAddress},
types::{BigUint, CallbackClosure, EgldPayment, ManagedAddress},
};

use super::FunctionCall;
use super::{FunctionCall, Tx, TxAsyncCallCallback, TxScEnv};

#[must_use]
pub struct AsyncCall<SA>
where
SA: CallTypeApi + 'static,
{
pub(crate) to: ManagedAddress<SA>,
pub(crate) egld_payment: BigUint<SA>,
pub(crate) function_call: FunctionCall<SA>,
pub(crate) callback_call: Option<CallbackClosure<SA>>,
}
pub type AsyncCall<Api> = Tx<
TxScEnv<Api>,
(),
ManagedAddress<Api>,
EgldPayment<Api>,
(),
FunctionCall<Api>,
Option<CallbackClosure<Api>>,
>;

#[allow(clippy::return_self_not_must_use)]
impl<SA> AsyncCall<SA>
impl<Api> AsyncCall<Api>
where
SA: CallTypeApi,
Api: CallTypeApi,
{
pub fn with_callback(self, callback_call: CallbackClosure<SA>) -> Self {
AsyncCall {
callback_call: Some(callback_call),
..self
}
pub fn with_callback(mut self, callback_call: CallbackClosure<Api>) -> Self {
self.callback = Some(callback_call);
self
}
}

impl<SA> AsyncCall<SA>
impl<Api> AsyncCall<Api>
where
SA: CallTypeApi,
Api: CallTypeApi + StorageWriteApi,
{
pub fn call_and_exit_ignore_callback(&self) -> ! {
SendRawWrapper::<SA>::new().async_call_raw(
&self.to,
&self.egld_payment,
&self.function_call.function_name,
&self.function_call.arg_buffer,
)
pub fn call_and_exit_ignore_callback(self) -> ! {
self.async_call_and_exit()
}
}

impl<SA> AsyncCall<SA>
impl<Api> AsyncCall<Api>
where
SA: CallTypeApi + StorageWriteApi,
Api: CallTypeApi + StorageWriteApi,
{
pub fn call_and_exit(&self) -> ! {
// first, save the callback closure
if let Some(callback_call) = &self.callback_call {
callback_call.save_to_storage::<SA>();
}

// last, send the async call, which will kill the execution
self.call_and_exit_ignore_callback()
pub fn call_and_exit(self) -> ! {
self.async_call_and_exit()
}
}
13 changes: 12 additions & 1 deletion framework/base/src/types/interaction/callback_closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,22 @@ pub const CALLBACK_CLOSURE_STORAGE_BASE_KEY: &[u8] = b"CB_CLOSURE";
///
/// In both cases the framework hides all the magic, the developer shouldn't worry about it.
#[derive(TopEncode)]
pub struct CallbackClosure<M: ManagedTypeApi + ErrorApi> {
pub struct CallbackClosure<M>
where
M: ManagedTypeApi + ErrorApi,
{
pub(super) callback_name: &'static str,
pub(super) closure_args: ManagedArgBuffer<M>,
}

pub struct CallbackClosureWithGas<M>
where
M: ManagedTypeApi + ErrorApi,
{
pub(super) closure: CallbackClosure<M>,
pub(super) gas_for_callback: u64,
}

/// Syntactical sugar to help macros to generate code easier.
/// Unlike calling `CallbackClosure::<SA, R>::new`, here types can be inferred from the context.
pub fn new_callback_call<A>(callback_name: &'static str) -> CallbackClosure<A>
Expand Down
13 changes: 6 additions & 7 deletions framework/base/src/types/interaction/contract_call_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
},
};

use super::{AsyncCall, ContractCallNoPayment, ContractCallWithEgld};
use super::{AsyncCall, ContractCallNoPayment, ContractCallWithEgld, Tx};
use crate::api::managed_types::handles::HandleConstraints;

/// Using max u64 to represent maximum possible gas,
Expand Down Expand Up @@ -66,12 +66,11 @@ where
}

pub(super) fn async_call(self) -> AsyncCall<SA> {
AsyncCall {
to: self.basic.to,
egld_payment: self.egld_payment,
function_call: self.basic.function_call,
callback_call: None,
}
Tx::new_tx_from_sc()
.to(self.basic.to)
.egld(self.egld_payment)
.call(self.basic.function_call)
.callback(None)
}

pub(super) fn async_call_promise(self) -> super::AsyncCallPromises<SA> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use super::{
contract_call_exec::UNSPECIFIED_GAS_LIMIT, contract_call_with_egld::ContractCallWithEgld,
contract_call_with_multi_esdt::ContractCallWithMultiEsdt, ContractCall,
ContractCallWithAnyPayment, ContractCallWithEgldOrSingleEsdt, FunctionCall, ManagedArgBuffer,
Tx, TxScEnv,
};

/// Holds metadata for calling another contract, without payments.
Expand Down Expand Up @@ -166,4 +167,8 @@ where
pub fn into_function_call(self) -> FunctionCall<SA> {
self.function_call
}

pub fn tx(self) -> Tx<TxScEnv<SA>, (), (), (), (), FunctionCall<SA>, ()> {
Tx::new_tx_from_sc().call(self.function_call)
}
}
Loading
Loading