From e34027ea58e075f1a4b3a667d9f063ed20bd23bf Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 26 Oct 2023 19:01:25 +0300 Subject: [PATCH 01/11] unified tx syntax - prototype --- .../contract_base/wrappers/send_wrapper.rs | 6 + .../src/types/interaction/function_call.rs | 21 +- framework/base/src/types/interaction/mod.rs | 12 + framework/base/src/types/interaction/tx.rs | 327 ++++++++++++++++++ .../base/src/types/interaction/tx_data.rs | 38 ++ .../base/src/types/interaction/tx_from.rs | 26 ++ .../base/src/types/interaction/tx_gas.rs | 7 + .../types/interaction/tx_normalize_call.rs | 91 +++++ .../base/src/types/interaction/tx_payment.rs | 209 +++++++++++ framework/base/src/types/interaction/tx_to.rs | 18 + .../wrapped/egld_or_esdt_token_identifier.rs | 2 +- .../wrapped/egld_or_esdt_token_payment.rs | 23 ++ .../src/types/managed/wrapped/egld_payment.rs | 30 ++ .../types/managed/wrapped/managed_option.rs | 15 +- .../base/src/types/managed/wrapped/mod.rs | 2 + 15 files changed, 809 insertions(+), 18 deletions(-) create mode 100644 framework/base/src/types/interaction/tx.rs create mode 100644 framework/base/src/types/interaction/tx_data.rs create mode 100644 framework/base/src/types/interaction/tx_from.rs create mode 100644 framework/base/src/types/interaction/tx_gas.rs create mode 100644 framework/base/src/types/interaction/tx_normalize_call.rs create mode 100644 framework/base/src/types/interaction/tx_payment.rs create mode 100644 framework/base/src/types/interaction/tx_to.rs create mode 100644 framework/base/src/types/managed/wrapped/egld_payment.rs diff --git a/framework/base/src/contract_base/wrappers/send_wrapper.rs b/framework/base/src/contract_base/wrappers/send_wrapper.rs index 78f4f045d6..aa7ae5c653 100644 --- a/framework/base/src/contract_base/wrappers/send_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/send_wrapper.rs @@ -15,6 +15,7 @@ use crate::{ types::{ BigUint, ContractCall, ContractCallNoPayment, EgldOrEsdtTokenIdentifier, EsdtTokenPayment, ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec, TokenIdentifier, + TxBase, }, }; @@ -70,6 +71,11 @@ where ContractCallNoPayment::new(to, endpoint_name) } + #[inline] + pub fn tx(&self) -> TxBase { + TxBase::new() + } + /// Sends EGLD to a given address, directly. /// Used especially for sending EGLD to regular accounts. #[inline] diff --git a/framework/base/src/types/interaction/function_call.rs b/framework/base/src/types/interaction/function_call.rs index a2140f14fa..ae12687b26 100644 --- a/framework/base/src/types/interaction/function_call.rs +++ b/framework/base/src/types/interaction/function_call.rs @@ -9,11 +9,7 @@ use crate::{ ManagedTypeApi, ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME, ESDT_TRANSFER_FUNC_NAME, }, - formatter::SCLowerHex, - types::{ - EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedBufferCachedBuilder, ManagedVec, - MultiValueEncoded, - }, + types::{EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedVec, MultiValueEncoded}, }; use super::ManagedArgBuffer; @@ -62,15 +58,14 @@ where self.arg_buffer.push_multi_arg(arg); self } +} - pub fn to_call_data_string(&self) -> ManagedBuffer { - let mut result = ManagedBufferCachedBuilder::default(); - result.append_managed_buffer(&self.function_name); - for arg in self.arg_buffer.raw_arg_iter() { - result.append_bytes(b"@"); - SCLowerHex::fmt(&*arg, &mut result); - } - result.into_managed_buffer() +impl From<()> for FunctionCall +where + Api: ManagedTypeApi, +{ + fn from(_: ()) -> Self { + FunctionCall::empty() } } diff --git a/framework/base/src/types/interaction/mod.rs b/framework/base/src/types/interaction/mod.rs index 3de007ee79..8efeba7b03 100644 --- a/framework/base/src/types/interaction/mod.rs +++ b/framework/base/src/types/interaction/mod.rs @@ -14,6 +14,12 @@ mod contract_call_with_multi_esdt; mod contract_deploy; mod function_call; mod managed_arg_buffer; +mod tx; +mod tx_data; +mod tx_from; +mod tx_gas; +mod tx_payment; +mod tx_to; pub use async_call::AsyncCall; pub use async_call_promises::AsyncCallPromises; @@ -31,3 +37,9 @@ pub use contract_call_with_multi_esdt::ContractCallWithMultiEsdt; pub use contract_deploy::{new_contract_deploy, ContractDeploy}; pub use function_call::FunctionCall; pub use managed_arg_buffer::ManagedArgBuffer; +pub use tx::*; +pub use tx_data::*; +pub use tx_from::*; +pub use tx_gas::*; +pub use tx_payment::*; +pub use tx_to::*; diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs new file mode 100644 index 0000000000..7b1da10187 --- /dev/null +++ b/framework/base/src/types/interaction/tx.rs @@ -0,0 +1,327 @@ +use core::marker::PhantomData; + +use multiversx_sc_codec::TopEncodeMulti; + +use crate::{ + api::CallTypeApi, + types::{ + BigUint, EgldPayment, EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedVec, + MultiEsdtPayment, + }, +}; + +use super::{AsyncCall, ExplicitGas, FunctionCall, TxData, TxFrom, TxGas, TxPayment, TxTo}; + +pub struct Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, +{ + pub(super) _phantom: PhantomData, + pub from: From, + pub to: To, + pub payment: Payment, + pub gas: Gas, + pub data: Data, +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, +{ + #[inline] + pub fn nothing(self) -> Tx { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: self.data, + } + } +} + +pub type TxBase = Tx; + +impl TxBase +where + Api: CallTypeApi + 'static, +{ + #[inline] + pub fn new() -> Self { + Tx { + _phantom: PhantomData, + from: (), + to: (), + payment: (), + gas: (), + data: (), + } + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, +{ + #[inline] + pub fn from(self, from: From) -> Tx + where + From: TxFrom, + { + Tx { + _phantom: PhantomData, + from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: self.data, + } + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, +{ + #[inline] + pub fn to(self, to: To) -> Tx + where + To: TxTo, + { + Tx { + _phantom: PhantomData, + from: self.from, + to, + payment: self.payment, + gas: self.gas, + data: self.data, + } + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, +{ + #[inline] + pub fn with_egld_transfer( + self, + egld_amount: BigUint, + ) -> Tx, Gas, Data> { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: EgldPayment { value: egld_amount }, + gas: self.gas, + data: self.data, + } + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, +{ + /// Adds a single ESDT token transfer to a contract call. + /// + /// Can be called multiple times on the same call. + #[inline] + pub fn with_esdt_transfer>>( + self, + payment: P, + ) -> Tx, Gas, Data> { + let mut payments = ManagedVec::new(); + payments.push(payment.into()); + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: payments, + gas: self.gas, + data: self.data, + } + } +} + +impl Tx, Gas, Data> +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, +{ + /// Adds a single ESDT token transfer to a contract call. + /// + /// Can be called multiple times on the same call. + #[inline] + pub fn with_esdt_transfer>>( + mut self, + payment: P, + ) -> Tx, Gas, Data> { + self.payment.push(payment.into()); + self + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Data: TxData, +{ + /// Sets an explicit gas limit to the call. + #[inline] + pub fn with_gas_limit(self, gas_limit: u64) -> Tx { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: self.payment, + gas: ExplicitGas(gas_limit), + data: self.data, + } + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, +{ + #[inline] + pub fn call>>( + self, + call: F, + ) -> Tx> { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: call.into(), + } + } + + #[inline] + pub fn function_name>>( + self, + function_name: N, + ) -> Tx> { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: FunctionCall::new(function_name), + } + } +} + +impl Tx> +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, +{ + #[inline] + pub fn argument(mut self, arg: &T) -> Self { + self.data = self.data.argument(arg); + self + } +} + +impl Tx, Payment, Gas, FunctionCall> +where + Api: CallTypeApi + 'static, + From: TxFrom, + Payment: TxPayment, + Gas: TxGas, +{ + pub fn normalize_tx( + self, + ) -> Tx, EgldPayment, Gas, FunctionCall> { + let result = self.payment.convert_tx_data(&self.from, self.to, self.data); + Tx { + _phantom: PhantomData, + from: self.from, + to: result.to, + payment: result.egld_payment, + gas: self.gas, + data: result.fc, + } + } +} + +impl Tx, Payment, (), FunctionCall> +where + Api: CallTypeApi + 'static, + Payment: TxPayment, +{ + pub fn async_call(self) -> AsyncCall { + let normalized = self.normalize_tx(); + AsyncCall { + to: normalized.to, + egld_payment: normalized.payment.value, + function_call: normalized.data, + callback_call: None, + } + } +} + +impl Tx, Payment, ExplicitGas, FunctionCall> +where + Api: CallTypeApi + 'static, + Payment: TxPayment, +{ + #[cfg(feature = "promises")] + pub fn async_call_promise(self) -> super::AsyncCallPromises { + let explicit_gas_limit = self.gas.0; + let normalized = self.normalize_tx(); + super::AsyncCallPromises { + to: normalized.to, + egld_payment: normalized.payment.value, + function_call: normalized.data, + explicit_gas_limit, + extra_gas_for_callback: 0, + callback_call: None, + } + } +} diff --git a/framework/base/src/types/interaction/tx_data.rs b/framework/base/src/types/interaction/tx_data.rs new file mode 100644 index 0000000000..440cdbdcb3 --- /dev/null +++ b/framework/base/src/types/interaction/tx_data.rs @@ -0,0 +1,38 @@ +use crate::{ + api::ManagedTypeApi, + formatter::SCLowerHex, + types::{ManagedBuffer, ManagedBufferCachedBuilder}, +}; + +use super::FunctionCall; + +pub trait TxData +where + Api: ManagedTypeApi, +{ + fn to_call_data_string(&self) -> ManagedBuffer; +} + +impl TxData for () +where + Api: ManagedTypeApi, +{ + fn to_call_data_string(&self) -> ManagedBuffer { + ManagedBuffer::new() + } +} + +impl TxData for FunctionCall +where + Api: ManagedTypeApi, +{ + fn to_call_data_string(&self) -> ManagedBuffer { + let mut result = ManagedBufferCachedBuilder::default(); + result.append_managed_buffer(&self.function_name); + for arg in self.arg_buffer.raw_arg_iter() { + result.append_bytes(b"@"); + SCLowerHex::fmt(&*arg, &mut result); + } + result.into_managed_buffer() + } +} diff --git a/framework/base/src/types/interaction/tx_from.rs b/framework/base/src/types/interaction/tx_from.rs new file mode 100644 index 0000000000..984f537ad1 --- /dev/null +++ b/framework/base/src/types/interaction/tx_from.rs @@ -0,0 +1,26 @@ +use crate::{api::CallTypeApi, contract_base::BlockchainWrapper, types::ManagedAddress}; + +pub trait TxFrom +where + Api: CallTypeApi, +{ + fn to_address(&self) -> ManagedAddress; +} + +impl TxFrom for () +where + Api: CallTypeApi, +{ + fn to_address(&self) -> ManagedAddress { + BlockchainWrapper::::new().get_sc_address() + } +} + +impl TxFrom for ManagedAddress +where + Api: CallTypeApi, +{ + fn to_address(&self) -> ManagedAddress { + self.clone() + } +} diff --git a/framework/base/src/types/interaction/tx_gas.rs b/framework/base/src/types/interaction/tx_gas.rs new file mode 100644 index 0000000000..475a15d1bf --- /dev/null +++ b/framework/base/src/types/interaction/tx_gas.rs @@ -0,0 +1,7 @@ +pub trait TxGas {} + +impl TxGas for () {} + +pub struct ExplicitGas(pub u64); + +impl TxGas for ExplicitGas {} diff --git a/framework/base/src/types/interaction/tx_normalize_call.rs b/framework/base/src/types/interaction/tx_normalize_call.rs new file mode 100644 index 0000000000..8a9cfb4fde --- /dev/null +++ b/framework/base/src/types/interaction/tx_normalize_call.rs @@ -0,0 +1,91 @@ +use core::marker::PhantomData; + +use crate::{ + api::CallTypeApi, + types::{BigUint, EgldPayment, ManagedAddress}, +}; + +use super::{AsyncCall, FunctionCall, Tx, TxData, TxFrom, TxGas}; + +/// Encodes +pub type NormalizedTx = + Tx, EgldPayment, Gas, FunctionCall>; + +pub trait TxNormalizeCall +where + Api: CallTypeApi + 'static, + From: TxFrom, + Gas: TxGas, +{ + fn normalize_call(self) -> NormalizedTx; +} + +impl TxNormalizeCall + for Tx, (), Gas, Call> +where + Api: CallTypeApi + 'static, + From: TxFrom, + Gas: TxGas, + Call: TxData + Into>, +{ + fn normalize_call(self) -> NormalizedTx { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: EgldPayment { + value: BigUint::zero(), + }, + gas: self.gas, + data: self.data.into(), + } + } +} + +impl TxNormalizeCall + for Tx, EgldPayment, Gas, Call> +where + Api: CallTypeApi + 'static, + From: TxFrom, + Gas: TxGas, + Call: TxData + Into>, +{ + fn normalize_call(self) -> NormalizedTx { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: self.data.into(), + } + } +} + +impl NormalizedTx +where + Api: CallTypeApi + 'static, + From: TxFrom, + Gas: TxGas, +{ + fn async_call(self) -> AsyncCall { + AsyncCall { + to: self.to, + egld_payment: self.payment.value, + function_call: self.data, + callback_call: None, + } + } + + // #[cfg(feature = "promises")] + // fn async_call_promise(self) -> super::AsyncCallPromises { + // super::AsyncCallPromises { + // to: self.basic.to, + // egld_payment: self.egld_payment, + // function_call: self.basic.function_call, + // explicit_gas_limit: self.basic.explicit_gas_limit, + // extra_gas_for_callback: 0, + // callback_call: None, + // } + // } +} diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs new file mode 100644 index 0000000000..e36d43cfc5 --- /dev/null +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -0,0 +1,209 @@ +use crate::{ + api::{CallTypeApi, ManagedTypeApi}, + types::{ + EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EgldPayment, EsdtTokenPayment, + ManagedAddress, MultiEsdtPayment, + }, +}; + +use super::{FunctionCall, TxFrom}; + +/// Temporary structure for returning a normalized transfer. +pub struct PaymentConversionResult +where + Api: ManagedTypeApi, +{ + pub to: ManagedAddress, + pub egld_payment: EgldPayment, + pub fc: FunctionCall, +} + +pub trait TxPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom; +} + +impl TxPayment for () +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + _from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + PaymentConversionResult { + to, + egld_payment: EgldPayment::no_payment(), + fc, + } + } +} + +impl TxPayment for EgldPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + _from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + PaymentConversionResult { + to, + egld_payment: self, + fc, + } + } +} + +impl TxPayment for EsdtTokenPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + if self.token_nonce == 0 { + convert_tx_data_fungible(self, to, fc) + } else { + convert_tx_data_nft(self, from.to_address(), to, fc) + } + } +} + +impl TxPayment for MultiEsdtPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + match self.len() { + 0 => ().convert_tx_data(from, to, fc), + 1 => self.get(0).convert_tx_data(from, to, fc), + _ => convert_tx_data_multi(self, from.to_address(), to, fc), + } + } +} + +impl TxPayment for EgldOrEsdtTokenPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + self.map_egld_or_esdt( + (to, fc), + |(to, fc), amount| EgldPayment::from(amount).convert_tx_data(from, to, fc), + |(to, fc), esdt_payment| esdt_payment.convert_tx_data(from, to, fc), + ) + } +} + +impl TxPayment for EgldOrMultiEsdtPayment +where + Api: CallTypeApi, +{ + fn convert_tx_data( + self, + from: &From, + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult + where + From: TxFrom, + { + match self { + EgldOrMultiEsdtPayment::Egld(egld_amount) => { + EgldPayment::from(egld_amount).convert_tx_data(from, to, fc) + }, + EgldOrMultiEsdtPayment::MultiEsdt(multi_esdt_payment) => { + multi_esdt_payment.convert_tx_data(from, to, fc) + }, + } + } +} + +fn convert_tx_data_fungible( + payment: EsdtTokenPayment, + to: ManagedAddress, + fc: FunctionCall, +) -> PaymentConversionResult +where + Api: ManagedTypeApi, +{ + PaymentConversionResult { + to, + egld_payment: EgldPayment::no_payment(), + fc: fc.convert_to_single_transfer_fungible_call(payment), + } +} + +fn convert_tx_data_nft( + payment: EsdtTokenPayment, + from: ManagedAddress, + to: ManagedAddress, + fc: FunctionCall, +) -> PaymentConversionResult +where + Api: ManagedTypeApi, +{ + PaymentConversionResult { + to: from, + egld_payment: EgldPayment::no_payment(), + fc: fc.convert_to_single_transfer_nft_call(&to, payment), + } +} + +fn convert_tx_data_multi( + payment: MultiEsdtPayment, + from: ManagedAddress, + to: ManagedAddress, + fc: FunctionCall, +) -> PaymentConversionResult +where + Api: ManagedTypeApi, +{ + PaymentConversionResult { + to: from, + egld_payment: EgldPayment::no_payment(), + fc: fc.convert_to_multi_transfer_esdt_call(&to, payment), + } +} diff --git a/framework/base/src/types/interaction/tx_to.rs b/framework/base/src/types/interaction/tx_to.rs new file mode 100644 index 0000000000..88d3e7a427 --- /dev/null +++ b/framework/base/src/types/interaction/tx_to.rs @@ -0,0 +1,18 @@ +use crate::{api::ManagedTypeApi, types::ManagedAddress}; + +pub trait TxTo +where + Api: ManagedTypeApi, +{ +} + +impl TxTo for () where Api: ManagedTypeApi {} + +pub trait TxToSpecified: TxTo +where + Api: ManagedTypeApi, +{ +} + +impl TxTo for ManagedAddress where Api: ManagedTypeApi {} +impl TxToSpecified for ManagedAddress where Api: ManagedTypeApi {} 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 3e7ae8afb5..ed2c7083a7 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 @@ -26,7 +26,7 @@ use crate as multiversx_sc; // required by the ManagedVecItem derive #[repr(transparent)] #[derive(ManagedVecItem, Clone)] pub struct EgldOrEsdtTokenIdentifier { - data: ManagedOption>, + pub(crate) data: ManagedOption>, } impl EgldOrEsdtTokenIdentifier { diff --git a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs index ed76946617..8d5e692610 100644 --- a/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs +++ b/framework/base/src/types/managed/wrapped/egld_or_esdt_token_payment.rs @@ -53,6 +53,29 @@ impl EgldOrEsdtTokenPayment { ) } + /// Equivalent to a `match { Egld | Esdt }`. + /// + /// Context passed on from function to closures, to avoid ownership issues. + /// More precisely, since only one of the two closures `for_egld` and `for_esdt` is called, + /// it is ok for them to have fully owned access to anything from the environment. + /// The compiler doesn't know that only one of them can ever be called, + /// so if we pass context to both closures, it will complain that they are moved twice.. + pub fn map_egld_or_esdt(self, context: Context, for_egld: D, for_esdt: F) -> U + where + D: FnOnce(Context, BigUint) -> U, + F: FnOnce(Context, EsdtTokenPayment) -> U, + { + if self.token_identifier.data.is_some() { + let token_identifier = unsafe { self.token_identifier.data.unwrap_no_check() }; + for_esdt( + context, + EsdtTokenPayment::new(token_identifier, self.token_nonce, self.amount), + ) + } else { + for_egld(context, self.amount) + } + } + pub fn into_tuple(self) -> (EgldOrEsdtTokenIdentifier, u64, BigUint) { (self.token_identifier, self.token_nonce, self.amount) } diff --git a/framework/base/src/types/managed/wrapped/egld_payment.rs b/framework/base/src/types/managed/wrapped/egld_payment.rs new file mode 100644 index 0000000000..f4c190e899 --- /dev/null +++ b/framework/base/src/types/managed/wrapped/egld_payment.rs @@ -0,0 +1,30 @@ +use crate::{api::ManagedTypeApi, types::BigUint}; + +/// Simple newtype wrapper around a BigUint value. +/// +/// Its purpose is to indicate +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EgldPayment +where + Api: ManagedTypeApi + 'static, +{ + pub value: BigUint, +} + +impl From> for EgldPayment +where + Api: ManagedTypeApi + 'static, +{ + fn from(value: BigUint) -> Self { + EgldPayment { value } + } +} + +impl EgldPayment +where + Api: ManagedTypeApi + 'static, +{ + pub fn no_payment() -> Self { + EgldPayment::from(BigUint::zero()) + } +} diff --git a/framework/base/src/types/managed/wrapped/managed_option.rs b/framework/base/src/types/managed/wrapped/managed_option.rs index 64766f43db..99160ff5cd 100644 --- a/framework/base/src/types/managed/wrapped/managed_option.rs +++ b/framework/base/src/types/managed/wrapped/managed_option.rs @@ -73,9 +73,16 @@ where !self.is_none() } + /// Assumes that value is Some and unwraps without checking. + /// + /// Must always be called under an `if` checking `.is_some()`, otherwise will lead to undefined behaviour. + pub unsafe fn unwrap_no_check(self) -> T { + T::from_handle(self.handle) + } + pub fn into_option(self) -> Option { if self.is_some() { - Some(T::from_handle(self.handle)) + Some(unsafe { self.unwrap_no_check() }) } else { None } @@ -91,7 +98,7 @@ where pub fn unwrap_or_else T>(self, f: F) -> T { if self.is_some() { - T::from_handle(self.handle) + unsafe { self.unwrap_no_check() } } else { f() } @@ -107,7 +114,7 @@ where F: FnOnce(T) -> U, { if self.is_some() { - ManagedOption::::some(f(T::from_handle(self.handle))) + ManagedOption::::some(f(unsafe { self.unwrap_no_check() })) } else { ManagedOption::::none() } @@ -119,7 +126,7 @@ where F: FnOnce(T) -> U, { if self.is_some() { - f(T::from_handle(self.handle)) + f(unsafe { self.unwrap_no_check() }) } else { default() } diff --git a/framework/base/src/types/managed/wrapped/mod.rs b/framework/base/src/types/managed/wrapped/mod.rs index 8ac485fa38..dee1161f44 100644 --- a/framework/base/src/types/managed/wrapped/mod.rs +++ b/framework/base/src/types/managed/wrapped/mod.rs @@ -1,6 +1,7 @@ mod egld_or_esdt_token_identifier; mod egld_or_esdt_token_payment; mod egld_or_multi_esdt_payment; +mod egld_payment; mod encoded_managed_vec_item; mod esdt_token_data; mod esdt_token_payment; @@ -21,6 +22,7 @@ mod token_identifier; pub use egld_or_esdt_token_identifier::EgldOrEsdtTokenIdentifier; pub use egld_or_esdt_token_payment::EgldOrEsdtTokenPayment; pub use egld_or_multi_esdt_payment::EgldOrMultiEsdtPayment; +pub use egld_payment::EgldPayment; pub(crate) use encoded_managed_vec_item::EncodedManagedVecItem; pub use esdt_token_data::EsdtTokenData; pub use esdt_token_payment::{EsdtTokenPayment, MultiEsdtPayment}; From 669ff3a742f0a2f1715d22022e00a98b73dc590c Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 31 Oct 2023 12:37:15 +0200 Subject: [PATCH 02/11] unified tx syntax - transfer-execute --- .../src/rewards_distribution.rs | 10 +- .../composability/vault/src/vault.rs | 13 +- .../src/contract_base/contract_base_trait.rs | 8 +- .../wrappers/send_raw_wrapper.rs | 12 +- .../contract_base/wrappers/send_wrapper.rs | 6 - .../interaction/contract_call_no_payment.rs | 5 + framework/base/src/types/interaction/tx.rs | 131 +++++++++++++++--- .../base/src/types/interaction/tx_data.rs | 10 ++ .../base/src/types/interaction/tx_gas.rs | 32 ++++- .../types/interaction/tx_normalize_call.rs | 91 ------------ .../base/src/types/interaction/tx_payment.rs | 113 +++++++++++++++ framework/base/src/types/interaction/tx_to.rs | 30 +++- .../wrapped/egld_or_multi_esdt_payment.rs | 9 ++ 13 files changed, 330 insertions(+), 140 deletions(-) delete mode 100644 framework/base/src/types/interaction/tx_normalize_call.rs diff --git a/contracts/examples/rewards-distribution/src/rewards_distribution.rs b/contracts/examples/rewards-distribution/src/rewards_distribution.rs index 59672da272..da8492e19e 100644 --- a/contracts/examples/rewards-distribution/src/rewards_distribution.rs +++ b/contracts/examples/rewards-distribution/src/rewards_distribution.rs @@ -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( diff --git a/contracts/feature-tests/composability/vault/src/vault.rs b/contracts/feature-tests/composability/vault/src/vault.rs index 4cebf5bc43..758aa4444b 100644 --- a/contracts/feature-tests/composability/vault/src/vault.rs +++ b/contracts/feature-tests/composability/vault/src/vault.rs @@ -151,10 +151,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(); } } @@ -172,7 +174,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("*")] @@ -210,8 +212,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")] diff --git a/framework/base/src/contract_base/contract_base_trait.rs b/framework/base/src/contract_base/contract_base_trait.rs index cec45dfa0f..bf5fcc7654 100644 --- a/framework/base/src/contract_base/contract_base_trait.rs +++ b/framework/base/src/contract_base/contract_base_trait.rs @@ -2,7 +2,7 @@ use super::{ BlockchainWrapper, CallValueWrapper, CryptoWrapper, ErrorHelper, ManagedSerializer, SendRawWrapper, SendWrapper, StorageRawWrapper, }; -use crate::api::VMApi; +use crate::{api::VMApi, types::TxBase}; /// Interface to be used by the actual smart contract code. /// @@ -26,6 +26,12 @@ pub trait ContractBase: Sized { SendWrapper::new() } + /// Starts the declaration of a new transaction. + #[inline] + fn tx(&self) -> TxBase { + TxBase::new() + } + /// Low-level functionality related to sending transactions from the current contract. /// /// For almost all cases contracts should instead use `self.send()` and `ContractCall`. diff --git a/framework/base/src/contract_base/wrappers/send_raw_wrapper.rs b/framework/base/src/contract_base/wrappers/send_raw_wrapper.rs index 5635c0b44a..5d290369df 100644 --- a/framework/base/src/contract_base/wrappers/send_raw_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/send_raw_wrapper.rs @@ -79,20 +79,12 @@ where &self, to: &ManagedAddress, token: &TokenIdentifier, - egld_value: &BigUint, + value: &BigUint, gas_limit: u64, endpoint_name: &ManagedBuffer, arg_buffer: &ManagedArgBuffer, ) -> 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)] diff --git a/framework/base/src/contract_base/wrappers/send_wrapper.rs b/framework/base/src/contract_base/wrappers/send_wrapper.rs index aa7ae5c653..78f4f045d6 100644 --- a/framework/base/src/contract_base/wrappers/send_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/send_wrapper.rs @@ -15,7 +15,6 @@ use crate::{ types::{ BigUint, ContractCall, ContractCallNoPayment, EgldOrEsdtTokenIdentifier, EsdtTokenPayment, ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec, TokenIdentifier, - TxBase, }, }; @@ -71,11 +70,6 @@ where ContractCallNoPayment::new(to, endpoint_name) } - #[inline] - pub fn tx(&self) -> TxBase { - TxBase::new() - } - /// Sends EGLD to a given address, directly. /// Used especially for sending EGLD to regular accounts. #[inline] diff --git a/framework/base/src/types/interaction/contract_call_no_payment.rs b/framework/base/src/types/interaction/contract_call_no_payment.rs index 7198f71cd1..c8866ddf55 100644 --- a/framework/base/src/types/interaction/contract_call_no_payment.rs +++ b/framework/base/src/types/interaction/contract_call_no_payment.rs @@ -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, }; /// Holds metadata for calling another contract, without payments. @@ -166,4 +167,8 @@ where pub fn into_function_call(self) -> FunctionCall { self.function_call } + + pub fn tx(self) -> Tx> { + Tx::new().call(self.function_call) + } } diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs index 7b1da10187..f173ef3dfb 100644 --- a/framework/base/src/types/interaction/tx.rs +++ b/framework/base/src/types/interaction/tx.rs @@ -4,13 +4,16 @@ use multiversx_sc_codec::TopEncodeMulti; use crate::{ api::CallTypeApi, + contract_base::BlockchainWrapper, types::{ BigUint, EgldPayment, EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedVec, MultiEsdtPayment, }, }; -use super::{AsyncCall, ExplicitGas, FunctionCall, TxData, TxFrom, TxGas, TxPayment, TxTo}; +use super::{ + AsyncCall, ExplicitGas, FunctionCall, TxData, TxFrom, TxGas, TxPayment, TxTo, TxToSpecified, +}; pub struct Tx where @@ -38,6 +41,7 @@ where Gas: TxGas, Data: TxData, { + /// TODO: does nothing, delete, added for easier copy-paste. #[inline] pub fn nothing(self) -> Tx { Tx { @@ -53,12 +57,12 @@ where pub type TxBase = Tx; -impl TxBase +impl Default for TxBase where Api: CallTypeApi + 'static, { #[inline] - pub fn new() -> Self { + fn default() -> Self { Tx { _phantom: PhantomData, from: (), @@ -70,6 +74,16 @@ where } } +impl TxBase +where + Api: CallTypeApi + 'static, +{ + #[inline] + pub fn new() -> Self { + Self::default() + } +} + impl Tx where Api: CallTypeApi + 'static, @@ -78,7 +92,6 @@ where Gas: TxGas, Data: TxData, { - #[inline] pub fn from(self, from: From) -> Tx where From: TxFrom, @@ -102,7 +115,6 @@ where Gas: TxGas, Data: TxData, { - #[inline] pub fn to(self, to: To) -> Tx where To: TxTo, @@ -116,6 +128,11 @@ where data: self.data, } } + + pub fn to_caller(self) -> Tx, Payment, Gas, Data> { + let caller = BlockchainWrapper::::new().get_caller(); + self.to(caller) + } } impl Tx @@ -126,11 +143,7 @@ where Gas: TxGas, Data: TxData, { - #[inline] - pub fn with_egld_transfer( - self, - egld_amount: BigUint, - ) -> Tx, Gas, Data> { + pub fn egld(self, egld_amount: BigUint) -> Tx, Gas, Data> { Tx { _phantom: PhantomData, from: self.from, @@ -143,6 +156,47 @@ where } impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, +{ + /// Adds a single ESDT token transfer to a transaction. + /// + /// Since this is the first ESDT payment, a single payment tx is produced. Can be called again for multiple payments. + pub fn esdt>>( + self, + payment: P, + ) -> Tx, Gas, Data> { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: payment.into(), + gas: self.gas, + data: self.data, + } + } + + /// Adds a collection of ESDT payments to a transaction. + pub fn multi_esdt( + self, + payments: MultiEsdtPayment, // TODO: references + ) -> Tx, Gas, Data> { + Tx { + _phantom: PhantomData, + from: self.from, + to: self.to, + payment: payments, + gas: self.gas, + data: self.data, + } + } +} + +impl Tx, Gas, Data> where Api: CallTypeApi + 'static, From: TxFrom, @@ -153,12 +207,12 @@ where /// Adds a single ESDT token transfer to a contract call. /// /// Can be called multiple times on the same call. - #[inline] pub fn with_esdt_transfer>>( self, payment: P, ) -> Tx, Gas, Data> { let mut payments = ManagedVec::new(); + payments.push(self.payment); payments.push(payment.into()); Tx { _phantom: PhantomData, @@ -182,7 +236,6 @@ where /// Adds a single ESDT token transfer to a contract call. /// /// Can be called multiple times on the same call. - #[inline] pub fn with_esdt_transfer>>( mut self, payment: P, @@ -223,9 +276,9 @@ where Gas: TxGas, { #[inline] - pub fn call>>( + pub fn call>>( self, - call: F, + call: FC, ) -> Tx> { Tx { _phantom: PhantomData, @@ -268,17 +321,20 @@ where } } -impl Tx, Payment, Gas, FunctionCall> +impl Tx> where Api: CallTypeApi + 'static, From: TxFrom, + To: TxToSpecified, Payment: TxPayment, Gas: TxGas, { pub fn normalize_tx( self, ) -> Tx, EgldPayment, Gas, FunctionCall> { - let result = self.payment.convert_tx_data(&self.from, self.to, self.data); + let result = self + .payment + .convert_tx_data(&self.from, self.to.into_address(), self.data); Tx { _phantom: PhantomData, from: self.from, @@ -290,9 +346,10 @@ where } } -impl Tx, Payment, (), FunctionCall> +impl Tx> where Api: CallTypeApi + 'static, + To: TxToSpecified, Payment: TxPayment, { pub fn async_call(self) -> AsyncCall { @@ -306,9 +363,10 @@ where } } -impl Tx, Payment, ExplicitGas, FunctionCall> +impl Tx> where Api: CallTypeApi + 'static, + To: TxToSpecified, Payment: TxPayment, { #[cfg(feature = "promises")] @@ -325,3 +383,40 @@ where } } } + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, + FC: TxData + Into>, +{ + pub fn transfer_execute(self) { + if self.payment.is_no_payment() && self.data.is_no_call() { + return; + } + + let gas_limit = self.gas.resolve_gas::(); + self.payment.perform_transfer_execute( + self.to.to_address_ref(), + gas_limit, + self.data.into(), + ); + } +} + +impl Tx +where + Api: CallTypeApi + 'static, + From: TxFrom, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, +{ + /// Syntactic sugar, only allowed for simple transfers. + pub fn transfer(self) { + self.transfer_execute() + } +} diff --git a/framework/base/src/types/interaction/tx_data.rs b/framework/base/src/types/interaction/tx_data.rs index 440cdbdcb3..e49038a1f7 100644 --- a/framework/base/src/types/interaction/tx_data.rs +++ b/framework/base/src/types/interaction/tx_data.rs @@ -10,6 +10,8 @@ pub trait TxData where Api: ManagedTypeApi, { + fn is_no_call(&self) -> bool; + fn to_call_data_string(&self) -> ManagedBuffer; } @@ -17,6 +19,10 @@ impl TxData for () where Api: ManagedTypeApi, { + fn is_no_call(&self) -> bool { + true + } + fn to_call_data_string(&self) -> ManagedBuffer { ManagedBuffer::new() } @@ -26,6 +32,10 @@ impl TxData for FunctionCall where Api: ManagedTypeApi, { + fn is_no_call(&self) -> bool { + self.is_empty() + } + fn to_call_data_string(&self) -> ManagedBuffer { let mut result = ManagedBufferCachedBuilder::default(); result.append_managed_buffer(&self.function_name); diff --git a/framework/base/src/types/interaction/tx_gas.rs b/framework/base/src/types/interaction/tx_gas.rs index 475a15d1bf..dcb37140b7 100644 --- a/framework/base/src/types/interaction/tx_gas.rs +++ b/framework/base/src/types/interaction/tx_gas.rs @@ -1,7 +1,33 @@ -pub trait TxGas {} +use crate::api::{BlockchainApiImpl, CallTypeApi}; -impl TxGas for () {} +use super::contract_call_exec::TRANSFER_EXECUTE_DEFAULT_LEFTOVER; + +pub trait TxGas { + fn resolve_gas(&self) -> u64 + where + Api: CallTypeApi + 'static; +} + +impl TxGas for () { + fn resolve_gas(&self) -> u64 + where + Api: CallTypeApi + 'static, + { + let mut gas_left = Api::blockchain_api_impl().get_gas_left(); + if gas_left > TRANSFER_EXECUTE_DEFAULT_LEFTOVER { + gas_left -= TRANSFER_EXECUTE_DEFAULT_LEFTOVER; + } + gas_left + } +} pub struct ExplicitGas(pub u64); -impl TxGas for ExplicitGas {} +impl TxGas for ExplicitGas { + fn resolve_gas(&self) -> u64 + where + Api: CallTypeApi + 'static, + { + self.0 + } +} diff --git a/framework/base/src/types/interaction/tx_normalize_call.rs b/framework/base/src/types/interaction/tx_normalize_call.rs deleted file mode 100644 index 8a9cfb4fde..0000000000 --- a/framework/base/src/types/interaction/tx_normalize_call.rs +++ /dev/null @@ -1,91 +0,0 @@ -use core::marker::PhantomData; - -use crate::{ - api::CallTypeApi, - types::{BigUint, EgldPayment, ManagedAddress}, -}; - -use super::{AsyncCall, FunctionCall, Tx, TxData, TxFrom, TxGas}; - -/// Encodes -pub type NormalizedTx = - Tx, EgldPayment, Gas, FunctionCall>; - -pub trait TxNormalizeCall -where - Api: CallTypeApi + 'static, - From: TxFrom, - Gas: TxGas, -{ - fn normalize_call(self) -> NormalizedTx; -} - -impl TxNormalizeCall - for Tx, (), Gas, Call> -where - Api: CallTypeApi + 'static, - From: TxFrom, - Gas: TxGas, - Call: TxData + Into>, -{ - fn normalize_call(self) -> NormalizedTx { - Tx { - _phantom: PhantomData, - from: self.from, - to: self.to, - payment: EgldPayment { - value: BigUint::zero(), - }, - gas: self.gas, - data: self.data.into(), - } - } -} - -impl TxNormalizeCall - for Tx, EgldPayment, Gas, Call> -where - Api: CallTypeApi + 'static, - From: TxFrom, - Gas: TxGas, - Call: TxData + Into>, -{ - fn normalize_call(self) -> NormalizedTx { - Tx { - _phantom: PhantomData, - from: self.from, - to: self.to, - payment: self.payment, - gas: self.gas, - data: self.data.into(), - } - } -} - -impl NormalizedTx -where - Api: CallTypeApi + 'static, - From: TxFrom, - Gas: TxGas, -{ - fn async_call(self) -> AsyncCall { - AsyncCall { - to: self.to, - egld_payment: self.payment.value, - function_call: self.data, - callback_call: None, - } - } - - // #[cfg(feature = "promises")] - // fn async_call_promise(self) -> super::AsyncCallPromises { - // super::AsyncCallPromises { - // to: self.basic.to, - // egld_payment: self.egld_payment, - // function_call: self.basic.function_call, - // explicit_gas_limit: self.basic.explicit_gas_limit, - // extra_gas_for_callback: 0, - // callback_call: None, - // } - // } -} diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs index e36d43cfc5..6daa4ebd27 100644 --- a/framework/base/src/types/interaction/tx_payment.rs +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -1,5 +1,6 @@ use crate::{ api::{CallTypeApi, ManagedTypeApi}, + contract_base::SendRawWrapper, types::{ EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EgldPayment, EsdtTokenPayment, ManagedAddress, MultiEsdtPayment, @@ -22,6 +23,8 @@ pub trait TxPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool; + fn convert_tx_data( self, from: &From, @@ -30,12 +33,23 @@ where ) -> PaymentConversionResult where From: TxFrom; + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ); } impl TxPayment for () where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + true + } + fn convert_tx_data( self, _from: &From, @@ -51,12 +65,25 @@ where fc, } } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + EgldPayment::no_payment().perform_transfer_execute(to, gas_limit, fc); + } } impl TxPayment for EgldPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + self.value == 0u32 + } + fn convert_tx_data( self, _from: &From, @@ -72,12 +99,31 @@ where fc, } } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + let _ = SendRawWrapper::::new().direct_egld_execute( + to, + &self.value, + gas_limit, + &fc.function_name, + &fc.arg_buffer, + ); + } } impl TxPayment for EsdtTokenPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + self.amount == 0u32 + } + fn convert_tx_data( self, from: &From, @@ -93,12 +139,25 @@ where convert_tx_data_nft(self, from.to_address(), to, fc) } } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + MultiEsdtPayment::from_single_item(self).perform_transfer_execute(to, gas_limit, fc) + } } impl TxPayment for MultiEsdtPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + self.is_empty() + } + fn convert_tx_data( self, from: &From, @@ -114,12 +173,31 @@ where _ => convert_tx_data_multi(self, from.to_address(), to, fc), } } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + let _ = SendRawWrapper::::new().multi_esdt_transfer_execute( + to, + &self, + gas_limit, + &fc.function_name, + &fc.arg_buffer, + ); + } } impl TxPayment for EgldOrEsdtTokenPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + self.amount == 0u32 + } + fn convert_tx_data( self, from: &From, @@ -135,12 +213,31 @@ where |(to, fc), esdt_payment| esdt_payment.convert_tx_data(from, to, fc), ) } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + self.map_egld_or_esdt( + (to, fc), + |(to, fc), amount| { + EgldPayment::from(amount).perform_transfer_execute(to, gas_limit, fc) + }, + |(to, fc), esdt_payment| esdt_payment.perform_transfer_execute(to, gas_limit, fc), + ) + } } impl TxPayment for EgldOrMultiEsdtPayment where Api: CallTypeApi, { + fn is_no_payment(&self) -> bool { + self.is_empty() + } + fn convert_tx_data( self, from: &From, @@ -159,6 +256,22 @@ where }, } } + + fn perform_transfer_execute( + self, + to: &ManagedAddress, + gas_limit: u64, + fc: FunctionCall, + ) { + match self { + EgldOrMultiEsdtPayment::Egld(egld_amount) => { + EgldPayment::from(egld_amount).perform_transfer_execute(to, gas_limit, fc) + }, + EgldOrMultiEsdtPayment::MultiEsdt(multi_esdt_payment) => { + multi_esdt_payment.perform_transfer_execute(to, gas_limit, fc) + }, + } + } } fn convert_tx_data_fungible( diff --git a/framework/base/src/types/interaction/tx_to.rs b/framework/base/src/types/interaction/tx_to.rs index 88d3e7a427..62aad803a7 100644 --- a/framework/base/src/types/interaction/tx_to.rs +++ b/framework/base/src/types/interaction/tx_to.rs @@ -12,7 +12,35 @@ pub trait TxToSpecified: TxTo where Api: ManagedTypeApi, { + fn to_address_ref(&self) -> &ManagedAddress; + + fn into_address(self) -> ManagedAddress; } impl TxTo for ManagedAddress where Api: ManagedTypeApi {} -impl TxToSpecified for ManagedAddress where Api: ManagedTypeApi {} +impl TxToSpecified for ManagedAddress +where + Api: ManagedTypeApi, +{ + fn to_address_ref(&self) -> &ManagedAddress { + self + } + + fn into_address(self) -> ManagedAddress { + self + } +} + +impl TxTo for &ManagedAddress where Api: ManagedTypeApi {} +impl TxToSpecified for &ManagedAddress +where + Api: ManagedTypeApi, +{ + fn to_address_ref(&self) -> &ManagedAddress { + self + } + + fn into_address(self) -> ManagedAddress { + self.clone() + } +} diff --git a/framework/base/src/types/managed/wrapped/egld_or_multi_esdt_payment.rs b/framework/base/src/types/managed/wrapped/egld_or_multi_esdt_payment.rs index c71aa249d7..730f7b22a9 100644 --- a/framework/base/src/types/managed/wrapped/egld_or_multi_esdt_payment.rs +++ b/framework/base/src/types/managed/wrapped/egld_or_multi_esdt_payment.rs @@ -24,3 +24,12 @@ pub enum EgldOrMultiEsdtPayment { } impl CodecFromSelf for EgldOrMultiEsdtPayment where M: ManagedTypeApi {} + +impl EgldOrMultiEsdtPayment { + pub fn is_empty(&self) -> bool { + match self { + EgldOrMultiEsdtPayment::Egld(egld_value) => egld_value == &0u32, + EgldOrMultiEsdtPayment::MultiEsdt(esdt_payments) => esdt_payments.is_empty(), + } + } +} From ae054e168cbceb041382cc72101917257aa13e31 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Sun, 5 Nov 2023 02:30:54 +0200 Subject: [PATCH 03/11] unified tx syntax - blackbox prototype --- .../tests/adder_blackbox_with_values_test.rs | 16 ++- .../base/src/types/interaction/annotated.rs | 49 +++++++ .../interaction/contract_call_no_payment.rs | 2 +- .../src/types/interaction/expr_address.rs | 85 ++++++++++++ .../base/src/types/interaction/expr_sc.rs | 109 +++++++++++++++ .../src/types/interaction/function_call.rs | 13 +- .../types/interaction/managed_arg_buffer.rs | 4 + framework/base/src/types/interaction/mod.rs | 8 ++ framework/base/src/types/interaction/tx.rs | 131 ++++++++++++------ .../src/types/interaction/tx_environment.rs | 66 +++++++++ .../base/src/types/interaction/tx_from.rs | 25 +++- .../base/src/types/interaction/tx_payment.rs | 4 +- framework/base/src/types/interaction/tx_to.rs | 33 +---- .../src/types/managed/basic/managed_buffer.rs | 20 +++ .../types/managed/wrapped/managed_address.rs | 12 +- framework/scenario/src/facade.rs | 1 + .../scenario/src/facade/contract_info.rs | 51 +++++++ .../src/facade/scenario_world_steps_tx.rs | 79 +++++++++++ 18 files changed, 623 insertions(+), 85 deletions(-) create mode 100644 framework/base/src/types/interaction/annotated.rs create mode 100644 framework/base/src/types/interaction/expr_address.rs create mode 100644 framework/base/src/types/interaction/expr_sc.rs create mode 100644 framework/base/src/types/interaction/tx_environment.rs create mode 100644 framework/scenario/src/facade/scenario_world_steps_tx.rs diff --git a/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs b/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs index 62173c5cce..061432c95f 100644 --- a/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs +++ b/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs @@ -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 = "file:output/adder.wasm"; @@ -41,12 +44,11 @@ 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)) + }) .check_state_step( CheckStateStep::new() .put_account(owner_address, CheckAccount::new()) diff --git a/framework/base/src/types/interaction/annotated.rs b/framework/base/src/types/interaction/annotated.rs new file mode 100644 index 0000000000..2814f95444 --- /dev/null +++ b/framework/base/src/types/interaction/annotated.rs @@ -0,0 +1,49 @@ +use crate::{ + api::ManagedTypeApi, + types::{ManagedAddress, ManagedBuffer}, +}; + +pub trait AnnotatedValue +where + Api: ManagedTypeApi, +{ + fn annotation(&self) -> ManagedBuffer; + + fn into_value(self) -> T; + + fn with_value_ref(&self, f: F); +} + +impl AnnotatedValue> for ManagedAddress +where + Api: ManagedTypeApi, +{ + fn annotation(&self) -> ManagedBuffer { + self.hex_expr() + } + + fn into_value(self) -> ManagedAddress { + self + } + + fn with_value_ref)>(&self, f: F) { + f(self) + } +} + +impl AnnotatedValue> for &ManagedAddress +where + Api: ManagedTypeApi, +{ + fn annotation(&self) -> crate::types::ManagedBuffer { + self.hex_expr() + } + + fn into_value(self) -> ManagedAddress { + self.clone() + } + + fn with_value_ref)>(&self, f: F) { + f(self) + } +} diff --git a/framework/base/src/types/interaction/contract_call_no_payment.rs b/framework/base/src/types/interaction/contract_call_no_payment.rs index c8866ddf55..5471c2a9e8 100644 --- a/framework/base/src/types/interaction/contract_call_no_payment.rs +++ b/framework/base/src/types/interaction/contract_call_no_payment.rs @@ -168,7 +168,7 @@ where self.function_call } - pub fn tx(self) -> Tx> { + pub fn tx(self) -> Tx> { Tx::new().call(self.function_call) } } diff --git a/framework/base/src/types/interaction/expr_address.rs b/framework/base/src/types/interaction/expr_address.rs new file mode 100644 index 0000000000..305d833cc8 --- /dev/null +++ b/framework/base/src/types/interaction/expr_address.rs @@ -0,0 +1,85 @@ +use core::ptr; + +use crate::{ + api::CallTypeApi, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::{AnnotatedValue, TxFrom, TxFromSpecified}; + +const ADDRESS_PREFIX: &str = "address:"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct AddressExpr(pub &'static str); + +impl AnnotatedValue> for AddressExpr +where + Api: CallTypeApi, +{ + fn annotation(&self) -> ManagedBuffer { + let mut result = ManagedBuffer::new_from_bytes(ADDRESS_PREFIX.as_bytes()); + result.append_bytes(self.0.as_bytes()); + result + } + + fn into_value(self) -> ManagedAddress { + let expr: [u8; 32] = self.eval_to_array(); + expr.into() + } + + fn with_value_ref)>(&self, f: F) { + let expr: [u8; 32] = self.eval_to_array(); + let ma = expr.into(); + f(&ma); + } +} +impl TxFrom for AddressExpr +where + Api: CallTypeApi, +{ + fn resolve_address(&self) -> ManagedAddress { + let expr: [u8; 32] = self.eval_to_array(); + expr.into() + } +} +impl TxFromSpecified for AddressExpr where Api: CallTypeApi {} + +impl AddressExpr { + pub const fn eval_to_array(&self) -> [u8; 32] { + let result = [b'_'; 32]; + let expr_bytes = self.0.as_bytes(); + let mut len = expr_bytes.len(); + if len > 32 { + len = 32; + } + unsafe { + ptr::copy_nonoverlapping(expr_bytes.as_ptr(), result.as_ptr() as *mut u8, len); + } + result + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + fn assert_eq_eval(expr: &'static str, expected: &[u8; 32]) { + assert_eq!(&AddressExpr(expr).eval_to_array(), expected); + } + + #[test] + fn test_address_value() { + assert_eq_eval("", b"________________________________"); + assert_eq_eval("a", b"a_______________________________"); + assert_eq_eval("a\x05", b"a\x05______________________________"); + assert_eq_eval("an_address", b"an_address______________________"); + assert_eq_eval( + "12345678901234567890123456789012", + b"12345678901234567890123456789012", + ); + assert_eq_eval( + "123456789012345678901234567890123", + b"12345678901234567890123456789012", + ); + } +} diff --git a/framework/base/src/types/interaction/expr_sc.rs b/framework/base/src/types/interaction/expr_sc.rs new file mode 100644 index 0000000000..f83bfe3149 --- /dev/null +++ b/framework/base/src/types/interaction/expr_sc.rs @@ -0,0 +1,109 @@ +use core::ptr; + +use crate::{ + api::CallTypeApi, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::{AnnotatedValue, TxFrom, TxFromSpecified, TxTo, TxToSpecified}; + +const SC_PREFIX: &str = "sc:"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ScExpr<'a>(pub &'a str); + +impl<'a, Api> AnnotatedValue> for ScExpr<'a> +where + Api: CallTypeApi, +{ + fn annotation(&self) -> ManagedBuffer { + let mut result = ManagedBuffer::new_from_bytes(SC_PREFIX.as_bytes()); + result.append_bytes(self.0.as_bytes()); + result + } + + fn into_value(self) -> ManagedAddress { + let expr: [u8; 32] = self.eval_to_array(); + expr.into() + } + + fn with_value_ref)>(&self, f: F) { + let expr: [u8; 32] = self.eval_to_array(); + let ma = expr.into(); + f(&ma); + } +} +impl<'a, Api> TxFrom for ScExpr<'a> +where + Api: CallTypeApi, +{ + fn resolve_address(&self) -> ManagedAddress { + let expr: [u8; 32] = self.eval_to_array(); + expr.into() + } +} +impl<'a, Api> TxFromSpecified for ScExpr<'a> where Api: CallTypeApi {} +impl<'a, Api> TxTo for ScExpr<'a> where Api: CallTypeApi {} +impl<'a, Api> TxToSpecified for ScExpr<'a> where Api: CallTypeApi {} + +impl<'a> ScExpr<'a> { + pub const fn eval_to_array(&self) -> [u8; 32] { + let result = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00______________________"; + let expr_bytes = self.0.as_bytes(); + let mut len = expr_bytes.len(); + if len > 22 { + len = 22; + } + unsafe { + ptr::copy_nonoverlapping( + expr_bytes.as_ptr(), + result.as_ptr().offset(10) as *mut u8, + len, + ); + } + result + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + fn assert_eq_eval(expr: &'static str, expected: &[u8; 32]) { + assert_eq!(&ScExpr(expr).eval_to_array(), expected); + } + + #[test] + fn test_address_value() { + assert_eq_eval( + "", + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00______________________", + ); + assert_eq_eval( + "a", + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a_____________________", + ); + assert_eq_eval( + "12345678901234567890120s", + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012", + ); + } + + // #[test] + // fn test_sc_address() { + // let context = InterpreterContext::default(); + // assert_eq!( + // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a_____________________".to_vec(), + // interpret_string("sc:a", &context) + // ); + // assert_eq!( + // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(), + // interpret_string("sc:12345678901234567890120s", &context) + // ); + // // trims excess + // assert_eq!( + // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(), + // interpret_string("sc:12345678901234567890120sx", &context) + // ); + // } +} diff --git a/framework/base/src/types/interaction/function_call.rs b/framework/base/src/types/interaction/function_call.rs index ae12687b26..bfe74958dc 100644 --- a/framework/base/src/types/interaction/function_call.rs +++ b/framework/base/src/types/interaction/function_call.rs @@ -6,13 +6,13 @@ use multiversx_sc_codec::{ use crate::{ abi::{TypeAbi, TypeName}, api::{ - ManagedTypeApi, ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME, + CallTypeApi, ManagedTypeApi, ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME, ESDT_TRANSFER_FUNC_NAME, }, types::{EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedVec, MultiValueEncoded}, }; -use super::ManagedArgBuffer; +use super::{ContractCallNoPayment, ManagedArgBuffer}; /// Encodes a function call on the blockchain, composed of a function name and its encoded arguments. /// @@ -69,6 +69,15 @@ where } } +impl From> for FunctionCall +where + Api: CallTypeApi, +{ + fn from(ccnp: ContractCallNoPayment) -> Self { + ccnp.function_call + } +} + impl TopEncodeMulti for FunctionCall where Api: ManagedTypeApi, diff --git a/framework/base/src/types/interaction/managed_arg_buffer.rs b/framework/base/src/types/interaction/managed_arg_buffer.rs index b0cee5db30..7af6346c7b 100644 --- a/framework/base/src/types/interaction/managed_arg_buffer.rs +++ b/framework/base/src/types/interaction/managed_arg_buffer.rs @@ -172,6 +172,10 @@ where pub fn into_vec_of_buffers(self) -> ManagedVec> { self.data } + + pub fn iter_buffers(&self) -> ManagedVecRefIterator> { + ManagedVecRefIterator::new(&self.data) + } } impl ManagedArgBuffer diff --git a/framework/base/src/types/interaction/mod.rs b/framework/base/src/types/interaction/mod.rs index 8efeba7b03..b44ff66d0f 100644 --- a/framework/base/src/types/interaction/mod.rs +++ b/framework/base/src/types/interaction/mod.rs @@ -1,3 +1,4 @@ +mod annotated; mod async_call; mod async_call_promises; mod back_transfers; @@ -12,15 +13,19 @@ mod contract_call_with_egld; mod contract_call_with_egld_or_single_esdt; mod contract_call_with_multi_esdt; mod contract_deploy; +mod expr_address; +mod expr_sc; mod function_call; mod managed_arg_buffer; mod tx; mod tx_data; +mod tx_environment; mod tx_from; mod tx_gas; mod tx_payment; mod tx_to; +pub use annotated::*; pub use async_call::AsyncCall; pub use async_call_promises::AsyncCallPromises; pub use back_transfers::BackTransfers; @@ -35,10 +40,13 @@ pub use contract_call_with_egld::ContractCallWithEgld; pub use contract_call_with_egld_or_single_esdt::ContractCallWithEgldOrSingleEsdt; pub use contract_call_with_multi_esdt::ContractCallWithMultiEsdt; pub use contract_deploy::{new_contract_deploy, ContractDeploy}; +pub use expr_address::AddressExpr; +pub use expr_sc::ScExpr; pub use function_call::FunctionCall; pub use managed_arg_buffer::ManagedArgBuffer; pub use tx::*; pub use tx_data::*; +pub use tx_environment::*; pub use tx_from::*; pub use tx_gas::*; pub use tx_payment::*; diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs index f173ef3dfb..93d0ff6b04 100644 --- a/framework/base/src/types/interaction/tx.rs +++ b/framework/base/src/types/interaction/tx.rs @@ -12,12 +12,14 @@ use crate::{ }; use super::{ - AsyncCall, ExplicitGas, FunctionCall, TxData, TxFrom, TxGas, TxPayment, TxTo, TxToSpecified, + AsyncCall, ExplicitGas, FunctionCall, TxData, TxEnvironemnt, TxFrom, TxGas, TxPayment, TxTo, + TxToSpecified, TxFromSpecified, }; -pub struct Tx +pub struct Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Payment: TxPayment, @@ -25,6 +27,7 @@ where Data: TxData, { pub(super) _phantom: PhantomData, + pub env: Env, pub from: From, pub to: To, pub payment: Payment, @@ -32,9 +35,10 @@ where pub data: Data, } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Payment: TxPayment, @@ -43,9 +47,10 @@ where { /// TODO: does nothing, delete, added for easier copy-paste. #[inline] - pub fn nothing(self) -> Tx { + pub fn nothing(self) -> Tx { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: self.payment, @@ -55,16 +60,18 @@ where } } -pub type TxBase = Tx; +pub type TxBaseWithEnv = Tx; -impl Default for TxBase +impl TxBaseWithEnv where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, { #[inline] - fn default() -> Self { + pub fn new_with_env(env: Env) -> Self { Tx { _phantom: PhantomData, + env, from: (), to: (), payment: (), @@ -74,6 +81,18 @@ where } } +pub type TxBase = Tx; + +impl Default for TxBase +where + Api: CallTypeApi + 'static, +{ + #[inline] + fn default() -> Self { + Self::new_with_env(()) + } +} + impl TxBase where Api: CallTypeApi + 'static, @@ -84,20 +103,24 @@ where } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, To: TxTo, Payment: TxPayment, Gas: TxGas, Data: TxData, { - pub fn from(self, from: From) -> Tx + pub fn from(self, from: From) -> Tx where - From: TxFrom, + From: TxFromSpecified, { + let mut env = self.env; + env.annotate_from(&from); Tx { _phantom: PhantomData, + env, from, to: self.to, payment: self.payment, @@ -107,20 +130,24 @@ where } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, Payment: TxPayment, Gas: TxGas, Data: TxData, { - pub fn to(self, to: To) -> Tx + pub fn to(self, to: To) -> Tx where - To: TxTo, + To: TxToSpecified, { + let mut env = self.env; + env.annotate_to(&to); Tx { _phantom: PhantomData, + env, from: self.from, to, payment: self.payment, @@ -129,23 +156,28 @@ where } } - pub fn to_caller(self) -> Tx, Payment, Gas, Data> { + pub fn to_caller(self) -> Tx, Payment, Gas, Data> { let caller = BlockchainWrapper::::new().get_caller(); self.to(caller) } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Gas: TxGas, Data: TxData, { - pub fn egld(self, egld_amount: BigUint) -> Tx, Gas, Data> { + pub fn egld( + self, + egld_amount: BigUint, + ) -> Tx, Gas, Data> { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: EgldPayment { value: egld_amount }, @@ -155,9 +187,10 @@ where } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Gas: TxGas, @@ -169,9 +202,10 @@ where pub fn esdt>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: payment.into(), @@ -184,9 +218,10 @@ where pub fn multi_esdt( self, payments: MultiEsdtPayment, // TODO: references - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: payments, @@ -196,9 +231,10 @@ where } } -impl Tx, Gas, Data> +impl Tx, Gas, Data> where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Gas: TxGas, @@ -210,12 +246,13 @@ where pub fn with_esdt_transfer>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { let mut payments = ManagedVec::new(); payments.push(self.payment); payments.push(payment.into()); Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: payments, @@ -225,9 +262,10 @@ where } } -impl Tx, Gas, Data> +impl Tx, Gas, Data> where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Gas: TxGas, @@ -239,15 +277,16 @@ where pub fn with_esdt_transfer>>( mut self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { self.payment.push(payment.into()); self } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Payment: TxPayment, @@ -255,9 +294,13 @@ where { /// Sets an explicit gas limit to the call. #[inline] - pub fn with_gas_limit(self, gas_limit: u64) -> Tx { + pub fn with_gas_limit( + self, + gas_limit: u64, + ) -> Tx { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: self.payment, @@ -267,9 +310,10 @@ where } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Payment: TxPayment, @@ -279,9 +323,10 @@ where pub fn call>>( self, call: FC, - ) -> Tx> { + ) -> Tx> { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: self.payment, @@ -294,9 +339,10 @@ where pub fn function_name>>( self, function_name: N, - ) -> Tx> { + ) -> Tx> { Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: self.to, payment: self.payment, @@ -306,9 +352,10 @@ where } } -impl Tx> +impl Tx> where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxTo, Payment: TxPayment, @@ -321,9 +368,10 @@ where } } -impl Tx> +impl Tx> where Api: CallTypeApi + 'static, + Env: TxEnvironemnt, From: TxFrom, To: TxToSpecified, Payment: TxPayment, @@ -331,12 +379,13 @@ where { pub fn normalize_tx( self, - ) -> Tx, EgldPayment, Gas, FunctionCall> { + ) -> Tx, EgldPayment, Gas, FunctionCall> { let result = self .payment - .convert_tx_data(&self.from, self.to.into_address(), self.data); + .convert_tx_data(&self.from, self.to.into_value(), self.data); Tx { _phantom: PhantomData, + env: self.env, from: self.from, to: result.to, payment: result.egld_payment, @@ -346,7 +395,7 @@ where } } -impl Tx> +impl Tx> where Api: CallTypeApi + 'static, To: TxToSpecified, @@ -363,7 +412,7 @@ where } } -impl Tx> +impl Tx> where Api: CallTypeApi + 'static, To: TxToSpecified, @@ -384,7 +433,7 @@ where } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, From: TxFrom, @@ -399,15 +448,15 @@ where } let gas_limit = self.gas.resolve_gas::(); - self.payment.perform_transfer_execute( - self.to.to_address_ref(), - gas_limit, - self.data.into(), - ); + + self.to.with_value_ref(|to| { + self.payment + .perform_transfer_execute(to, gas_limit, self.data.into()); + }); } } -impl Tx +impl Tx where Api: CallTypeApi + 'static, From: TxFrom, diff --git a/framework/base/src/types/interaction/tx_environment.rs b/framework/base/src/types/interaction/tx_environment.rs new file mode 100644 index 0000000000..62c90ab47f --- /dev/null +++ b/framework/base/src/types/interaction/tx_environment.rs @@ -0,0 +1,66 @@ +use core::marker::PhantomData; + +use crate::{ + api::CallTypeApi, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::AnnotatedValue; + +pub trait TxEnvironemnt +where + Api: CallTypeApi, +{ + fn annotate_from(&mut self, from: &From) + where + From: AnnotatedValue>; + + fn annotate_to(&mut self, to: &To) + where + To: AnnotatedValue>; +} + +impl TxEnvironemnt for () +where + Api: CallTypeApi, +{ + fn annotate_from(&mut self, _from: &From) + where + From: AnnotatedValue>, + { + } + + fn annotate_to(&mut self, _to: &To) + where + To: AnnotatedValue>, + { + } +} + +pub struct TxTestingEnvironemnt +where + Api: CallTypeApi, +{ + pub(super) _phantom: PhantomData, + pub from_annotation: ManagedBuffer, + pub to_annotation: ManagedBuffer, +} + +impl TxEnvironemnt for TxTestingEnvironemnt +where + Api: CallTypeApi, +{ + fn annotate_from(&mut self, from: &From) + where + From: AnnotatedValue>, + { + self.from_annotation = from.annotation(); + } + + fn annotate_to(&mut self, to: &To) + where + To: AnnotatedValue>, + { + self.to_annotation = to.annotation(); + } +} diff --git a/framework/base/src/types/interaction/tx_from.rs b/framework/base/src/types/interaction/tx_from.rs index 984f537ad1..5edba3f2a5 100644 --- a/framework/base/src/types/interaction/tx_from.rs +++ b/framework/base/src/types/interaction/tx_from.rs @@ -1,17 +1,25 @@ use crate::{api::CallTypeApi, contract_base::BlockchainWrapper, types::ManagedAddress}; +use super::AnnotatedValue; + pub trait TxFrom where Api: CallTypeApi, { - fn to_address(&self) -> ManagedAddress; + fn resolve_address(&self) -> ManagedAddress; +} + +pub trait TxFromSpecified: TxFrom + AnnotatedValue> +where + Api: CallTypeApi, +{ } impl TxFrom for () where Api: CallTypeApi, { - fn to_address(&self) -> ManagedAddress { + fn resolve_address(&self) -> ManagedAddress { BlockchainWrapper::::new().get_sc_address() } } @@ -20,7 +28,18 @@ impl TxFrom for ManagedAddress where Api: CallTypeApi, { - fn to_address(&self) -> ManagedAddress { + fn resolve_address(&self) -> ManagedAddress { self.clone() } } +impl TxFromSpecified for ManagedAddress where Api: CallTypeApi {} + +impl TxFrom for &ManagedAddress +where + Api: CallTypeApi, +{ + fn resolve_address(&self) -> ManagedAddress { + (*self).clone() + } +} +impl TxFromSpecified for &ManagedAddress where Api: CallTypeApi {} diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs index 6daa4ebd27..95ad62fae1 100644 --- a/framework/base/src/types/interaction/tx_payment.rs +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -136,7 +136,7 @@ where if self.token_nonce == 0 { convert_tx_data_fungible(self, to, fc) } else { - convert_tx_data_nft(self, from.to_address(), to, fc) + convert_tx_data_nft(self, from.resolve_address(), to, fc) } } @@ -170,7 +170,7 @@ where match self.len() { 0 => ().convert_tx_data(from, to, fc), 1 => self.get(0).convert_tx_data(from, to, fc), - _ => convert_tx_data_multi(self, from.to_address(), to, fc), + _ => convert_tx_data_multi(self, from.resolve_address(), to, fc), } } diff --git a/framework/base/src/types/interaction/tx_to.rs b/framework/base/src/types/interaction/tx_to.rs index 62aad803a7..bf7e44e2be 100644 --- a/framework/base/src/types/interaction/tx_to.rs +++ b/framework/base/src/types/interaction/tx_to.rs @@ -1,5 +1,7 @@ use crate::{api::ManagedTypeApi, types::ManagedAddress}; +use super::AnnotatedValue; + pub trait TxTo where Api: ManagedTypeApi, @@ -8,39 +10,14 @@ where impl TxTo for () where Api: ManagedTypeApi {} -pub trait TxToSpecified: TxTo +pub trait TxToSpecified: TxTo + AnnotatedValue> where Api: ManagedTypeApi, { - fn to_address_ref(&self) -> &ManagedAddress; - - fn into_address(self) -> ManagedAddress; } impl TxTo for ManagedAddress where Api: ManagedTypeApi {} -impl TxToSpecified for ManagedAddress -where - Api: ManagedTypeApi, -{ - fn to_address_ref(&self) -> &ManagedAddress { - self - } - - fn into_address(self) -> ManagedAddress { - self - } -} +impl TxToSpecified for ManagedAddress where Api: ManagedTypeApi {} impl TxTo for &ManagedAddress where Api: ManagedTypeApi {} -impl TxToSpecified for &ManagedAddress -where - Api: ManagedTypeApi, -{ - fn to_address_ref(&self) -> &ManagedAddress { - self - } - - fn into_address(self) -> ManagedAddress { - self.clone() - } -} +impl TxToSpecified for &ManagedAddress where Api: ManagedTypeApi {} diff --git a/framework/base/src/types/managed/basic/managed_buffer.rs b/framework/base/src/types/managed/basic/managed_buffer.rs index 7c08f32699..3e4347aa1d 100644 --- a/framework/base/src/types/managed/basic/managed_buffer.rs +++ b/framework/base/src/types/managed/basic/managed_buffer.rs @@ -159,6 +159,26 @@ where } } +impl From for ManagedBuffer +where + M: ManagedTypeApi, +{ + #[inline] + fn from(s: crate::types::heap::String) -> Self { + Self::new_from_bytes(s.as_bytes()) + } +} + +impl From<&crate::types::heap::String> for ManagedBuffer +where + M: ManagedTypeApi, +{ + #[inline] + fn from(s: &crate::types::heap::String) -> Self { + Self::new_from_bytes(s.as_bytes()) + } +} + impl Default for ManagedBuffer { #[inline] fn default() -> Self { diff --git a/framework/base/src/types/managed/wrapped/managed_address.rs b/framework/base/src/types/managed/wrapped/managed_address.rs index 1ce1524bb6..be24c8e99b 100644 --- a/framework/base/src/types/managed/wrapped/managed_address.rs +++ b/framework/base/src/types/managed/wrapped/managed_address.rs @@ -8,10 +8,12 @@ use crate::{ NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast, }, - formatter::{hex_util::encode_bytes_as_hex, FormatByteReceiver, SCLowerHex}, + formatter::{hex_util::encode_bytes_as_hex, FormatBuffer, FormatByteReceiver, SCLowerHex}, types::{heap::Address, ManagedBuffer, ManagedByteArray, ManagedType}, }; +use super::ManagedBufferCachedBuilder; + #[repr(transparent)] #[derive(Clone)] pub struct ManagedAddress { @@ -250,6 +252,14 @@ impl SCLowerHex for ManagedAddress { } } +impl ManagedAddress { + pub fn hex_expr(&self) -> ManagedBuffer { + let mut result = ManagedBufferCachedBuilder::new_from_slice(b"0x"); + result.append_lower_hex(self); + result.into_managed_buffer() + } +} + impl core::fmt::Debug for ManagedAddress { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ManagedAddress") diff --git a/framework/scenario/src/facade.rs b/framework/scenario/src/facade.rs index f53625895e..60fb9f4d83 100644 --- a/framework/scenario/src/facade.rs +++ b/framework/scenario/src/facade.rs @@ -4,6 +4,7 @@ mod scenario_world; mod scenario_world_runner; mod scenario_world_steps; mod scenario_world_steps_deprecated; +mod scenario_world_steps_tx; mod scenario_world_whitebox; mod whitebox_contract; diff --git a/framework/scenario/src/facade/contract_info.rs b/framework/scenario/src/facade/contract_info.rs index 35b3ee1afb..0e4f2619d9 100644 --- a/framework/scenario/src/facade/contract_info.rs +++ b/framework/scenario/src/facade/contract_info.rs @@ -1,5 +1,10 @@ use std::ops::{Deref, DerefMut}; +use multiversx_sc::{ + api::CallTypeApi, + types::{AnnotatedValue, ManagedBuffer, TxFrom, TxFromSpecified, TxTo, TxToSpecified}, +}; + use crate::multiversx_sc::{ api::ManagedTypeApi, codec::{CodecFrom, EncodeErrorHandler, TopEncode, TopEncodeOutput}, @@ -90,3 +95,49 @@ impl CodecFrom> for Address {} impl CodecFrom<&ContractInfo

> for Address {} impl CodecFrom> for ManagedAddress {} impl CodecFrom<&ContractInfo

> for ManagedAddress {} + +impl AnnotatedValue> for &ContractInfo

+where + Api: CallTypeApi, + P: ProxyObjBase, +{ + fn annotation(&self) -> ManagedBuffer { + self.scenario_address_expr.original.as_str().into() + } + + fn into_value(self) -> ManagedAddress { + (&self.scenario_address_expr.value).into() + } + + fn with_value_ref)>(&self, f: F) { + let ma: ManagedAddress = (&self.scenario_address_expr.value).into(); + f(&ma); + } +} +impl TxFrom for &ContractInfo

+where + Api: CallTypeApi, + P: ProxyObjBase, +{ + fn resolve_address(&self) -> ManagedAddress { + (&self.scenario_address_expr.value).into() + } +} +impl TxFromSpecified for &ContractInfo

+where + Api: CallTypeApi, + P: ProxyObjBase, +{ +} +impl TxTo for &ContractInfo

+where + Api: CallTypeApi, + P: ProxyObjBase, +{ +} +impl TxToSpecified for &ContractInfo

+where + Api: CallTypeApi, + P: ProxyObjBase, +{ +} diff --git a/framework/scenario/src/facade/scenario_world_steps_tx.rs b/framework/scenario/src/facade/scenario_world_steps_tx.rs new file mode 100644 index 0000000000..be7763bd58 --- /dev/null +++ b/framework/scenario/src/facade/scenario_world_steps_tx.rs @@ -0,0 +1,79 @@ +use std::path::PathBuf; + +use multiversx_sc::types::{ + AnnotatedValue, FunctionCall, ManagedAddress, Tx, TxBaseWithEnv, TxEnvironemnt, + TxFromSpecified, TxGas, TxPayment, TxToSpecified, +}; + +use crate::{api::StaticApi, facade::ScenarioWorld, scenario_model::ScCallStep}; + +#[derive(Default, Debug, Clone)] +pub struct ScenarioTxEnvironment { + pub context_path: PathBuf, + pub from_annotation: Option, + pub to_annotation: Option, +} + +impl TxEnvironemnt for ScenarioTxEnvironment { + fn annotate_from(&mut self, to: &From) + where + From: AnnotatedValue>, + { + self.from_annotation = Some(to.annotation().to_string()) + } + + fn annotate_to(&mut self, to: &To) + where + To: AnnotatedValue>, + { + self.to_annotation = Some(to.annotation().to_string()) + } +} + +pub type TxScenarioBase = TxBaseWithEnv; + +pub trait ScenarioTx { + fn run_as_scenario_step(self, world: &mut ScenarioWorld); +} + +impl ScenarioWorld { + fn tx_env(&self) -> ScenarioTxEnvironment { + ScenarioTxEnvironment { + context_path: self.current_dir.clone(), + ..Default::default() + } + } + + pub fn tx(&mut self, f: F) -> &mut Self + where + STx: ScenarioTx, + F: FnOnce(TxScenarioBase) -> STx, + { + let env = self.tx_env(); + let tx_base = TxScenarioBase::new_with_env(env); + let tx = f(tx_base); + tx.run_as_scenario_step(self); + self + } +} + +impl ScenarioTx + for Tx> +where + From: TxFromSpecified, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, +{ + fn run_as_scenario_step(self, world: &mut ScenarioWorld) { + let mut step = ScCallStep::new() + .from(self.env.from_annotation.unwrap().as_str()) + .to(self.env.to_annotation.unwrap().as_str()) + .function(self.data.function_name.to_string().as_str()); + for arg in self.data.arg_buffer.iter_buffers() { + step = step.argument(arg.to_vec()); + } + + world.sc_call(step); + } +} From dc2d895d31103b85b30abb20cf6848a9e275d4c1 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 7 Nov 2023 23:11:50 +0200 Subject: [PATCH 04/11] unified tx syntax - tx env refactor --- .../src/contract_base/contract_base_trait.rs | 9 +- .../base/src/types/interaction/annotated.rs | 28 +- .../interaction/contract_call_no_payment.rs | 6 +- .../src/types/interaction/expr_address.rs | 20 +- .../base/src/types/interaction/expr_sc.rs | 24 +- framework/base/src/types/interaction/mod.rs | 10 +- framework/base/src/types/interaction/tx.rs | 347 ++++++------------ .../base/src/types/interaction/tx_data.rs | 20 +- .../base/src/types/interaction/tx_env.rs | 52 +++ .../base/src/types/interaction/tx_env_sc.rs | 145 ++++++++ .../src/types/interaction/tx_environment.rs | 66 ---- .../base/src/types/interaction/tx_from.rs | 37 +- .../base/src/types/interaction/tx_gas.rs | 38 +- .../base/src/types/interaction/tx_payment.rs | 165 +++++---- framework/base/src/types/interaction/tx_to.rs | 20 +- .../scenario/src/facade/contract_info.rs | 35 +- .../src/facade/scenario_world_steps_tx.rs | 37 +- 17 files changed, 558 insertions(+), 501 deletions(-) create mode 100644 framework/base/src/types/interaction/tx_env.rs create mode 100644 framework/base/src/types/interaction/tx_env_sc.rs delete mode 100644 framework/base/src/types/interaction/tx_environment.rs diff --git a/framework/base/src/contract_base/contract_base_trait.rs b/framework/base/src/contract_base/contract_base_trait.rs index bf5fcc7654..342b9ea61d 100644 --- a/framework/base/src/contract_base/contract_base_trait.rs +++ b/framework/base/src/contract_base/contract_base_trait.rs @@ -2,7 +2,10 @@ use super::{ BlockchainWrapper, CallValueWrapper, CryptoWrapper, ErrorHelper, ManagedSerializer, SendRawWrapper, SendWrapper, StorageRawWrapper, }; -use crate::{api::VMApi, types::TxBase}; +use crate::{ + api::VMApi, + types::{Tx, TxBaseWithEnv, TxScEnv}, +}; /// Interface to be used by the actual smart contract code. /// @@ -28,8 +31,8 @@ pub trait ContractBase: Sized { /// Starts the declaration of a new transaction. #[inline] - fn tx(&self) -> TxBase { - TxBase::new() + fn tx(&self) -> TxBaseWithEnv> { + Tx::new_tx_from_sc() } /// Low-level functionality related to sending transactions from the current contract. diff --git a/framework/base/src/types/interaction/annotated.rs b/framework/base/src/types/interaction/annotated.rs index 2814f95444..9463ed7c12 100644 --- a/framework/base/src/types/interaction/annotated.rs +++ b/framework/base/src/types/interaction/annotated.rs @@ -3,47 +3,49 @@ use crate::{ types::{ManagedAddress, ManagedBuffer}, }; -pub trait AnnotatedValue +use super::TxEnv; + +pub trait AnnotatedValue where - Api: ManagedTypeApi, + Env: TxEnv, { - fn annotation(&self) -> ManagedBuffer; + fn annotation(&self, _env: &Env) -> ManagedBuffer; fn into_value(self) -> T; fn with_value_ref(&self, f: F); } -impl AnnotatedValue> for ManagedAddress +impl AnnotatedValue> for ManagedAddress where - Api: ManagedTypeApi, + Env: TxEnv, { - fn annotation(&self) -> ManagedBuffer { + fn annotation(&self, _env: &Env) -> ManagedBuffer { self.hex_expr() } - fn into_value(self) -> ManagedAddress { + fn into_value(self) -> ManagedAddress { self } - fn with_value_ref)>(&self, f: F) { + fn with_value_ref)>(&self, f: F) { f(self) } } -impl AnnotatedValue> for &ManagedAddress +impl AnnotatedValue> for &ManagedAddress where - Api: ManagedTypeApi, + Env: TxEnv, { - fn annotation(&self) -> crate::types::ManagedBuffer { + fn annotation(&self, _env: &Env) -> crate::types::ManagedBuffer { self.hex_expr() } - fn into_value(self) -> ManagedAddress { + fn into_value(self) -> ManagedAddress { self.clone() } - fn with_value_ref)>(&self, f: F) { + fn with_value_ref)>(&self, f: F) { f(self) } } diff --git a/framework/base/src/types/interaction/contract_call_no_payment.rs b/framework/base/src/types/interaction/contract_call_no_payment.rs index 5471c2a9e8..69057da3e9 100644 --- a/framework/base/src/types/interaction/contract_call_no_payment.rs +++ b/framework/base/src/types/interaction/contract_call_no_payment.rs @@ -14,7 +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, + Tx, TxScEnv, }; /// Holds metadata for calling another contract, without payments. @@ -168,7 +168,7 @@ where self.function_call } - pub fn tx(self) -> Tx> { - Tx::new().call(self.function_call) + pub fn tx(self) -> Tx, (), (), (), (), FunctionCall> { + Tx::new_tx_from_sc().call(self.function_call) } } diff --git a/framework/base/src/types/interaction/expr_address.rs b/framework/base/src/types/interaction/expr_address.rs index 305d833cc8..69a13cd6e3 100644 --- a/framework/base/src/types/interaction/expr_address.rs +++ b/framework/base/src/types/interaction/expr_address.rs @@ -5,44 +5,44 @@ use crate::{ types::{ManagedAddress, ManagedBuffer}, }; -use super::{AnnotatedValue, TxFrom, TxFromSpecified}; +use super::{AnnotatedValue, TxEnv, TxFrom, TxFromSpecified}; const ADDRESS_PREFIX: &str = "address:"; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct AddressExpr(pub &'static str); -impl AnnotatedValue> for AddressExpr +impl AnnotatedValue> for AddressExpr where - Api: CallTypeApi, + Env: TxEnv, { - fn annotation(&self) -> ManagedBuffer { + fn annotation(&self, _env: &Env) -> ManagedBuffer { let mut result = ManagedBuffer::new_from_bytes(ADDRESS_PREFIX.as_bytes()); result.append_bytes(self.0.as_bytes()); result } - fn into_value(self) -> ManagedAddress { + fn into_value(self) -> ManagedAddress { let expr: [u8; 32] = self.eval_to_array(); expr.into() } - fn with_value_ref)>(&self, f: F) { + fn with_value_ref)>(&self, f: F) { let expr: [u8; 32] = self.eval_to_array(); let ma = expr.into(); f(&ma); } } -impl TxFrom for AddressExpr +impl TxFrom for AddressExpr where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress { + fn resolve_address(&self, _env: &Env) -> ManagedAddress { let expr: [u8; 32] = self.eval_to_array(); expr.into() } } -impl TxFromSpecified for AddressExpr where Api: CallTypeApi {} +impl TxFromSpecified for AddressExpr where Env: TxEnv {} impl AddressExpr { pub const fn eval_to_array(&self) -> [u8; 32] { diff --git a/framework/base/src/types/interaction/expr_sc.rs b/framework/base/src/types/interaction/expr_sc.rs index f83bfe3149..a5ba6a5e82 100644 --- a/framework/base/src/types/interaction/expr_sc.rs +++ b/framework/base/src/types/interaction/expr_sc.rs @@ -5,46 +5,46 @@ use crate::{ types::{ManagedAddress, ManagedBuffer}, }; -use super::{AnnotatedValue, TxFrom, TxFromSpecified, TxTo, TxToSpecified}; +use super::{AnnotatedValue, TxEnv, TxFrom, TxFromSpecified, TxTo, TxToSpecified}; const SC_PREFIX: &str = "sc:"; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ScExpr<'a>(pub &'a str); -impl<'a, Api> AnnotatedValue> for ScExpr<'a> +impl<'a, Env> AnnotatedValue> for ScExpr<'a> where - Api: CallTypeApi, + Env: TxEnv, { - fn annotation(&self) -> ManagedBuffer { + fn annotation(&self, _env: &Env) -> ManagedBuffer { let mut result = ManagedBuffer::new_from_bytes(SC_PREFIX.as_bytes()); result.append_bytes(self.0.as_bytes()); result } - fn into_value(self) -> ManagedAddress { + fn into_value(self) -> ManagedAddress { let expr: [u8; 32] = self.eval_to_array(); expr.into() } - fn with_value_ref)>(&self, f: F) { + fn with_value_ref)>(&self, f: F) { let expr: [u8; 32] = self.eval_to_array(); let ma = expr.into(); f(&ma); } } -impl<'a, Api> TxFrom for ScExpr<'a> +impl<'a, Env> TxFrom for ScExpr<'a> where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress { + fn resolve_address(&self, _env: &Env) -> ManagedAddress { let expr: [u8; 32] = self.eval_to_array(); expr.into() } } -impl<'a, Api> TxFromSpecified for ScExpr<'a> where Api: CallTypeApi {} -impl<'a, Api> TxTo for ScExpr<'a> where Api: CallTypeApi {} -impl<'a, Api> TxToSpecified for ScExpr<'a> where Api: CallTypeApi {} +impl<'a, Env> TxFromSpecified for ScExpr<'a> where Env: TxEnv {} +impl<'a, Env> TxTo for ScExpr<'a> where Env: TxEnv {} +impl<'a, Env> TxToSpecified for ScExpr<'a> where Env: TxEnv {} impl<'a> ScExpr<'a> { pub const fn eval_to_array(&self) -> [u8; 32] { diff --git a/framework/base/src/types/interaction/mod.rs b/framework/base/src/types/interaction/mod.rs index b44ff66d0f..f8589bbf0d 100644 --- a/framework/base/src/types/interaction/mod.rs +++ b/framework/base/src/types/interaction/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused)] // TEMP + mod annotated; mod async_call; mod async_call_promises; @@ -19,7 +21,8 @@ mod function_call; mod managed_arg_buffer; mod tx; mod tx_data; -mod tx_environment; +mod tx_env; +mod tx_env_sc; mod tx_from; mod tx_gas; mod tx_payment; @@ -46,8 +49,11 @@ pub use function_call::FunctionCall; pub use managed_arg_buffer::ManagedArgBuffer; pub use tx::*; pub use tx_data::*; -pub use tx_environment::*; +pub use tx_env::*; +pub use tx_env_sc::*; pub use tx_from::*; pub use tx_gas::*; pub use tx_payment::*; pub use tx_to::*; + +pub type TxScBase = TxBaseWithEnv>; diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs index 93d0ff6b04..60b26b71e1 100644 --- a/framework/base/src/types/interaction/tx.rs +++ b/framework/base/src/types/interaction/tx.rs @@ -12,21 +12,19 @@ use crate::{ }; use super::{ - AsyncCall, ExplicitGas, FunctionCall, TxData, TxEnvironemnt, TxFrom, TxGas, TxPayment, TxTo, - TxToSpecified, TxFromSpecified, + AsyncCall, ExplicitGas, FunctionCall, TxData, TxEnv, TxFrom, TxFromSpecified, TxGas, TxPayment, + TxScEnv, TxTo, TxToSpecified, }; -pub struct Tx +pub struct Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Payment: TxPayment, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, { - pub(super) _phantom: PhantomData, pub env: Env, pub from: From, pub to: To, @@ -35,21 +33,19 @@ where pub data: Data, } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Payment: TxPayment, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, { /// TODO: does nothing, delete, added for easier copy-paste. #[inline] - pub fn nothing(self) -> Tx { + pub fn nothing(self) -> Tx { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -60,17 +56,15 @@ where } } -pub type TxBaseWithEnv = Tx; +pub type TxBaseWithEnv = Tx; -impl TxBaseWithEnv +impl TxBaseWithEnv where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, + Env: TxEnv, { #[inline] pub fn new_with_env(env: Env) -> Self { Tx { - _phantom: PhantomData, env, from: (), to: (), @@ -81,45 +75,35 @@ where } } -pub type TxBase = Tx; +// impl Default for TxBaseWithEnv<()> { +// #[inline] +// fn default() -> Self { +// Self::new_with_env(()) +// } +// } -impl Default for TxBase -where - Api: CallTypeApi + 'static, -{ - #[inline] - fn default() -> Self { - Self::new_with_env(()) - } -} - -impl TxBase -where - Api: CallTypeApi + 'static, -{ - #[inline] - pub fn new() -> Self { - Self::default() - } -} +// impl TxBaseWithEnv<()> { +// #[inline] +// pub fn new() -> Self { +// Self::default() +// } +// } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - To: TxTo, - Payment: TxPayment, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, { - pub fn from(self, from: From) -> Tx + pub fn from(self, from: From) -> Tx where - From: TxFromSpecified, + From: TxFromSpecified, { let mut env = self.env; env.annotate_from(&from); Tx { - _phantom: PhantomData, env, from, to: self.to, @@ -130,23 +114,21 @@ where } } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - Payment: TxPayment, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, { - pub fn to(self, to: To) -> Tx + pub fn to(self, to: To) -> Tx where - To: TxToSpecified, + To: TxToSpecified, { let mut env = self.env; env.annotate_to(&to); Tx { - _phantom: PhantomData, env, from: self.from, to, @@ -156,27 +138,25 @@ where } } - pub fn to_caller(self) -> Tx, Payment, Gas, Data> { - let caller = BlockchainWrapper::::new().get_caller(); + pub fn to_caller(self) -> Tx, Payment, Gas, Data> { + let caller = BlockchainWrapper::::new().get_caller(); self.to(caller) } } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, { pub fn egld( self, - egld_amount: BigUint, - ) -> Tx, Gas, Data> { + egld_amount: BigUint, + ) -> Tx, Gas, Data> { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -187,24 +167,22 @@ where } } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, { /// Adds a single ESDT token transfer to a transaction. /// /// Since this is the first ESDT payment, a single payment tx is produced. Can be called again for multiple payments. - pub fn esdt>>( + pub fn esdt>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -217,10 +195,9 @@ where /// Adds a collection of ESDT payments to a transaction. pub fn multi_esdt( self, - payments: MultiEsdtPayment, // TODO: references - ) -> Tx, Gas, Data> { + payments: MultiEsdtPayment, // TODO: references + ) -> Tx, Gas, Data> { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -231,27 +208,25 @@ where } } -impl Tx, Gas, Data> +impl Tx, Gas, Data> where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, { /// Adds a single ESDT token transfer to a contract call. /// /// Can be called multiple times on the same call. - pub fn with_esdt_transfer>>( + pub fn with_esdt_transfer>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { let mut payments = ManagedVec::new(); payments.push(self.payment); payments.push(payment.into()); Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -262,44 +237,38 @@ where } } -impl Tx, Gas, Data> +impl Tx, Gas, Data> where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Gas: TxGas, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, + Data: TxData, { /// Adds a single ESDT token transfer to a contract call. /// /// Can be called multiple times on the same call. - pub fn with_esdt_transfer>>( + pub fn with_esdt_transfer>>( mut self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data> { self.payment.push(payment.into()); self } } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Payment: TxPayment, - Data: TxData, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Data: TxData, { /// Sets an explicit gas limit to the call. #[inline] - pub fn with_gas_limit( - self, - gas_limit: u64, - ) -> Tx { + pub fn with_gas_limit(self, gas_limit: u64) -> Tx { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -310,22 +279,20 @@ where } } -impl Tx +impl Tx where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Payment: TxPayment, - Gas: TxGas, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, { #[inline] - pub fn call>>( + pub fn call>>( self, call: FC, - ) -> Tx> { + ) -> Tx> { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -336,12 +303,11 @@ where } #[inline] - pub fn function_name>>( + pub fn function_name>>( self, function_name: N, - ) -> Tx> { + ) -> Tx> { Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: self.to, @@ -352,14 +318,13 @@ where } } -impl Tx> +impl Tx> where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxTo, - Payment: TxPayment, - Gas: TxGas, + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, { #[inline] pub fn argument(mut self, arg: &T) -> Self { @@ -368,23 +333,22 @@ where } } -impl Tx> +impl Tx> where - Api: CallTypeApi + 'static, - Env: TxEnvironemnt, - From: TxFrom, - To: TxToSpecified, - Payment: TxPayment, - Gas: TxGas, + Env: TxEnv, + From: TxFrom, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, { pub fn normalize_tx( self, - ) -> Tx, EgldPayment, Gas, FunctionCall> { - let result = self - .payment - .convert_tx_data(&self.from, self.to.into_value(), self.data); + ) -> Tx, EgldPayment, Gas, FunctionCall> + { + let result = + self.payment + .convert_tx_data(&self.env, &self.from, self.to.into_value(), self.data); Tx { - _phantom: PhantomData, env: self.env, from: self.from, to: result.to, @@ -394,78 +358,3 @@ where } } } - -impl Tx> -where - Api: CallTypeApi + 'static, - To: TxToSpecified, - Payment: TxPayment, -{ - pub fn async_call(self) -> AsyncCall { - let normalized = self.normalize_tx(); - AsyncCall { - to: normalized.to, - egld_payment: normalized.payment.value, - function_call: normalized.data, - callback_call: None, - } - } -} - -impl Tx> -where - Api: CallTypeApi + 'static, - To: TxToSpecified, - Payment: TxPayment, -{ - #[cfg(feature = "promises")] - pub fn async_call_promise(self) -> super::AsyncCallPromises { - let explicit_gas_limit = self.gas.0; - let normalized = self.normalize_tx(); - super::AsyncCallPromises { - to: normalized.to, - egld_payment: normalized.payment.value, - function_call: normalized.data, - explicit_gas_limit, - extra_gas_for_callback: 0, - callback_call: None, - } - } -} - -impl Tx -where - Api: CallTypeApi + 'static, - From: TxFrom, - To: TxToSpecified, - Payment: TxPayment, - Gas: TxGas, - FC: TxData + Into>, -{ - pub fn transfer_execute(self) { - if self.payment.is_no_payment() && self.data.is_no_call() { - return; - } - - let gas_limit = self.gas.resolve_gas::(); - - self.to.with_value_ref(|to| { - self.payment - .perform_transfer_execute(to, gas_limit, self.data.into()); - }); - } -} - -impl Tx -where - Api: CallTypeApi + 'static, - From: TxFrom, - To: TxToSpecified, - Payment: TxPayment, - Gas: TxGas, -{ - /// Syntactic sugar, only allowed for simple transfers. - pub fn transfer(self) { - self.transfer_execute() - } -} diff --git a/framework/base/src/types/interaction/tx_data.rs b/framework/base/src/types/interaction/tx_data.rs index e49038a1f7..3f8da83eed 100644 --- a/framework/base/src/types/interaction/tx_data.rs +++ b/framework/base/src/types/interaction/tx_data.rs @@ -4,39 +4,39 @@ use crate::{ types::{ManagedBuffer, ManagedBufferCachedBuilder}, }; -use super::FunctionCall; +use super::{FunctionCall, TxEnv}; -pub trait TxData +pub trait TxData where - Api: ManagedTypeApi, + Env: TxEnv, { fn is_no_call(&self) -> bool; - fn to_call_data_string(&self) -> ManagedBuffer; + fn to_call_data_string(&self) -> ManagedBuffer; } -impl TxData for () +impl TxData for () where - Api: ManagedTypeApi, + Env: TxEnv, { fn is_no_call(&self) -> bool { true } - fn to_call_data_string(&self) -> ManagedBuffer { + fn to_call_data_string(&self) -> ManagedBuffer { ManagedBuffer::new() } } -impl TxData for FunctionCall +impl TxData for FunctionCall where - Api: ManagedTypeApi, + Env: TxEnv, { fn is_no_call(&self) -> bool { self.is_empty() } - fn to_call_data_string(&self) -> ManagedBuffer { + fn to_call_data_string(&self) -> ManagedBuffer { let mut result = ManagedBufferCachedBuilder::default(); result.append_managed_buffer(&self.function_name); for arg in self.arg_buffer.raw_arg_iter() { diff --git a/framework/base/src/types/interaction/tx_env.rs b/framework/base/src/types/interaction/tx_env.rs new file mode 100644 index 0000000000..c9746b36aa --- /dev/null +++ b/framework/base/src/types/interaction/tx_env.rs @@ -0,0 +1,52 @@ +use core::marker::PhantomData; + +use crate::{ + api::CallTypeApi, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::AnnotatedValue; + +pub trait TxEnv: Sized { + type Api: CallTypeApi; + + fn annotate_from(&mut self, from: &From) + where + From: AnnotatedValue>; + + fn annotate_to(&mut self, to: &To) + where + To: AnnotatedValue>; + + fn resolve_sender_address(&self) -> ManagedAddress; + + fn default_gas(&self) -> u64; +} + +// pub struct TxTestingEnvironemnt +// where +// Api: CallTypeApi, +// { +// pub(super) _phantom: PhantomData, +// pub from_annotation: ManagedBuffer, +// pub to_annotation: ManagedBuffer, +// } + +// impl TxEnv for TxTestingEnvironemnt +// where +// Api: CallTypeApi, +// { +// fn annotate_from(&mut self, from: &From) +// where +// From: AnnotatedValue>, +// { +// self.from_annotation = from.annotation(); +// } + +// fn annotate_to(&mut self, to: &To) +// where +// To: AnnotatedValue>, +// { +// self.to_annotation = to.annotation(); +// } +// } diff --git a/framework/base/src/types/interaction/tx_env_sc.rs b/framework/base/src/types/interaction/tx_env_sc.rs new file mode 100644 index 0000000000..b7f5a18bb4 --- /dev/null +++ b/framework/base/src/types/interaction/tx_env_sc.rs @@ -0,0 +1,145 @@ +use core::marker::PhantomData; + +use crate::{ + api::{BlockchainApiImpl, CallTypeApi}, + contract_base::BlockchainWrapper, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::{ + contract_call_exec::TRANSFER_EXECUTE_DEFAULT_LEFTOVER, AnnotatedValue, AsyncCall, ExplicitGas, + FunctionCall, Tx, TxBaseWithEnv, TxData, TxEnv, TxFrom, TxGas, TxPayment, TxToSpecified, +}; + +pub struct TxScEnv +where + Api: CallTypeApi, +{ + _phantom: PhantomData, +} + +impl Default for TxScEnv +where + Api: CallTypeApi, +{ + fn default() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl TxBaseWithEnv> +where + Api: CallTypeApi, +{ + pub fn new_tx_from_sc() -> Self { + Tx::new_with_env(TxScEnv::default()) + } +} + +impl TxEnv for TxScEnv +where + Api: CallTypeApi, +{ + type Api = Api; + + fn annotate_from(&mut self, _from: &From) + where + From: AnnotatedValue>, + { + } + + fn annotate_to(&mut self, _to: &To) + where + To: AnnotatedValue>, + { + } + + fn resolve_sender_address(&self) -> ManagedAddress { + BlockchainWrapper::::new().get_sc_address() + } + + fn default_gas(&self) -> u64 { + let mut gas_left = Api::blockchain_api_impl().get_gas_left(); + if gas_left > TRANSFER_EXECUTE_DEFAULT_LEFTOVER { + gas_left -= TRANSFER_EXECUTE_DEFAULT_LEFTOVER; + } + gas_left + } +} + +impl Tx, (), To, Payment, (), FunctionCall> +where + Api: CallTypeApi, + To: TxToSpecified>, + Payment: TxPayment>, +{ + pub fn async_call(self) -> AsyncCall { + let normalized = self.normalize_tx(); + AsyncCall { + to: normalized.to, + egld_payment: normalized.payment.value, + function_call: normalized.data, + callback_call: None, + } + } +} + +impl Tx, (), To, Payment, ExplicitGas, FunctionCall> +where + Api: CallTypeApi, + To: TxToSpecified>, + Payment: TxPayment>, +{ + #[cfg(feature = "promises")] + pub fn async_call_promise(self) -> super::AsyncCallPromises { + let explicit_gas_limit = self.gas.0; + let normalized = self.normalize_tx(); + super::AsyncCallPromises { + to: normalized.to, + egld_payment: normalized.payment.value, + function_call: normalized.data, + explicit_gas_limit, + extra_gas_for_callback: 0, + callback_call: None, + } + } +} + +impl Tx, From, To, Payment, Gas, FC> +where + Api: CallTypeApi, + From: TxFrom>, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, + FC: TxData> + Into>, +{ + pub fn transfer_execute(self) { + if self.payment.is_no_payment() && self.data.is_no_call() { + return; + } + + let gas_limit = self.gas.resolve_gas(&self.env); + + self.to.with_value_ref(|to| { + self.payment + .perform_transfer_execute(&self.env, to, gas_limit, self.data.into()); + }); + } +} + +impl Tx, From, To, Payment, Gas, ()> +where + Api: CallTypeApi, + From: TxFrom>, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, +{ + /// Syntactic sugar, only allowed for simple transfers. + pub fn transfer(self) { + self.transfer_execute() + } +} diff --git a/framework/base/src/types/interaction/tx_environment.rs b/framework/base/src/types/interaction/tx_environment.rs deleted file mode 100644 index 62c90ab47f..0000000000 --- a/framework/base/src/types/interaction/tx_environment.rs +++ /dev/null @@ -1,66 +0,0 @@ -use core::marker::PhantomData; - -use crate::{ - api::CallTypeApi, - types::{ManagedAddress, ManagedBuffer}, -}; - -use super::AnnotatedValue; - -pub trait TxEnvironemnt -where - Api: CallTypeApi, -{ - fn annotate_from(&mut self, from: &From) - where - From: AnnotatedValue>; - - fn annotate_to(&mut self, to: &To) - where - To: AnnotatedValue>; -} - -impl TxEnvironemnt for () -where - Api: CallTypeApi, -{ - fn annotate_from(&mut self, _from: &From) - where - From: AnnotatedValue>, - { - } - - fn annotate_to(&mut self, _to: &To) - where - To: AnnotatedValue>, - { - } -} - -pub struct TxTestingEnvironemnt -where - Api: CallTypeApi, -{ - pub(super) _phantom: PhantomData, - pub from_annotation: ManagedBuffer, - pub to_annotation: ManagedBuffer, -} - -impl TxEnvironemnt for TxTestingEnvironemnt -where - Api: CallTypeApi, -{ - fn annotate_from(&mut self, from: &From) - where - From: AnnotatedValue>, - { - self.from_annotation = from.annotation(); - } - - fn annotate_to(&mut self, to: &To) - where - To: AnnotatedValue>, - { - self.to_annotation = to.annotation(); - } -} diff --git a/framework/base/src/types/interaction/tx_from.rs b/framework/base/src/types/interaction/tx_from.rs index 5edba3f2a5..83db2cb85a 100644 --- a/framework/base/src/types/interaction/tx_from.rs +++ b/framework/base/src/types/interaction/tx_from.rs @@ -1,45 +1,46 @@ use crate::{api::CallTypeApi, contract_base::BlockchainWrapper, types::ManagedAddress}; -use super::AnnotatedValue; +use super::{AnnotatedValue, TxEnv}; -pub trait TxFrom +pub trait TxFrom where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress; + fn resolve_address(&self, env: &Env) -> ManagedAddress; } -pub trait TxFromSpecified: TxFrom + AnnotatedValue> +pub trait TxFromSpecified: + TxFrom + AnnotatedValue> where - Api: CallTypeApi, + Env: TxEnv, { } -impl TxFrom for () +impl TxFrom for () where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress { - BlockchainWrapper::::new().get_sc_address() + fn resolve_address(&self, env: &Env) -> ManagedAddress { + env.resolve_sender_address() } } -impl TxFrom for ManagedAddress +impl TxFrom for ManagedAddress where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress { + fn resolve_address(&self, _env: &Env) -> ManagedAddress { self.clone() } } -impl TxFromSpecified for ManagedAddress where Api: CallTypeApi {} +impl TxFromSpecified for ManagedAddress where Env: TxEnv {} -impl TxFrom for &ManagedAddress +impl TxFrom for &ManagedAddress where - Api: CallTypeApi, + Env: TxEnv, { - fn resolve_address(&self) -> ManagedAddress { + fn resolve_address(&self, _env: &Env) -> ManagedAddress { (*self).clone() } } -impl TxFromSpecified for &ManagedAddress where Api: CallTypeApi {} +impl TxFromSpecified for &ManagedAddress where Env: TxEnv {} diff --git a/framework/base/src/types/interaction/tx_gas.rs b/framework/base/src/types/interaction/tx_gas.rs index dcb37140b7..eec6693837 100644 --- a/framework/base/src/types/interaction/tx_gas.rs +++ b/framework/base/src/types/interaction/tx_gas.rs @@ -1,33 +1,31 @@ use crate::api::{BlockchainApiImpl, CallTypeApi}; -use super::contract_call_exec::TRANSFER_EXECUTE_DEFAULT_LEFTOVER; +use super::{contract_call_exec::TRANSFER_EXECUTE_DEFAULT_LEFTOVER, TxEnv}; -pub trait TxGas { - fn resolve_gas(&self) -> u64 - where - Api: CallTypeApi + 'static; +pub trait TxGas +where + Env: TxEnv, +{ + fn resolve_gas(&self, env: &Env) -> u64; } -impl TxGas for () { - fn resolve_gas(&self) -> u64 - where - Api: CallTypeApi + 'static, - { - let mut gas_left = Api::blockchain_api_impl().get_gas_left(); - if gas_left > TRANSFER_EXECUTE_DEFAULT_LEFTOVER { - gas_left -= TRANSFER_EXECUTE_DEFAULT_LEFTOVER; - } - gas_left +impl TxGas for () +where + Env: TxEnv, +{ + fn resolve_gas(&self, env: &Env) -> u64 { + env.default_gas() } } pub struct ExplicitGas(pub u64); -impl TxGas for ExplicitGas { - fn resolve_gas(&self) -> u64 - where - Api: CallTypeApi + 'static, - { +impl TxGas for ExplicitGas +where + Env: TxEnv, +{ + #[inline] + fn resolve_gas(&self, _env: &Env) -> u64 { self.0 } } diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs index 95ad62fae1..747b6ac3bf 100644 --- a/framework/base/src/types/interaction/tx_payment.rs +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -1,13 +1,15 @@ +use num_traits::Zero; + use crate::{ api::{CallTypeApi, ManagedTypeApi}, contract_base::SendRawWrapper, types::{ - EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EgldPayment, EsdtTokenPayment, + BigUint, EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EgldPayment, EsdtTokenPayment, ManagedAddress, MultiEsdtPayment, }, }; -use super::{FunctionCall, TxFrom}; +use super::{FunctionCall, TxEnv, TxFrom}; /// Temporary structure for returning a normalized transfer. pub struct PaymentConversionResult @@ -19,32 +21,34 @@ where pub fc: FunctionCall, } -pub trait TxPayment +pub trait TxPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool; fn convert_tx_data( self, + env: &Env, from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom; + From: TxFrom; fn perform_transfer_execute( self, - to: &ManagedAddress, + env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ); } -impl TxPayment for () +impl TxPayment for () where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { true @@ -52,12 +56,13 @@ where fn convert_tx_data( self, + _env: &Env, _from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { PaymentConversionResult { to, @@ -68,17 +73,19 @@ where fn perform_transfer_execute( self, - to: &ManagedAddress, + env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { - EgldPayment::no_payment().perform_transfer_execute(to, gas_limit, fc); + EgldPayment::no_payment().perform_transfer_execute(env, to, gas_limit, fc); + // perform_transfer_execute_egld(BigUint::zero(), to, gas_limit, fc); } } -impl TxPayment for EgldPayment +impl TxPayment for EgldPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { self.value == 0u32 @@ -86,12 +93,13 @@ where fn convert_tx_data( self, + _env: &Env, _from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { PaymentConversionResult { to, @@ -102,11 +110,12 @@ where fn perform_transfer_execute( self, - to: &ManagedAddress, + _env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { - let _ = SendRawWrapper::::new().direct_egld_execute( + let _ = SendRawWrapper::::new().direct_egld_execute( to, &self.value, gas_limit, @@ -116,9 +125,9 @@ where } } -impl TxPayment for EsdtTokenPayment +impl TxPayment for EsdtTokenPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { self.amount == 0u32 @@ -126,33 +135,35 @@ where fn convert_tx_data( self, + env: &Env, from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { if self.token_nonce == 0 { convert_tx_data_fungible(self, to, fc) } else { - convert_tx_data_nft(self, from.resolve_address(), to, fc) + convert_tx_data_nft(self, from.resolve_address(env), to, fc) } } fn perform_transfer_execute( self, - to: &ManagedAddress, + env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { - MultiEsdtPayment::from_single_item(self).perform_transfer_execute(to, gas_limit, fc) + MultiEsdtPayment::from_single_item(self).perform_transfer_execute(env, to, gas_limit, fc); } } -impl TxPayment for MultiEsdtPayment +impl TxPayment for MultiEsdtPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { self.is_empty() @@ -160,27 +171,29 @@ where fn convert_tx_data( self, + env: &Env, from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { match self.len() { - 0 => ().convert_tx_data(from, to, fc), - 1 => self.get(0).convert_tx_data(from, to, fc), - _ => convert_tx_data_multi(self, from.resolve_address(), to, fc), + 0 => ().convert_tx_data(env, from, to, fc), + 1 => self.get(0).convert_tx_data(env, from, to, fc), + _ => convert_tx_data_multi(self, from.resolve_address(env), to, fc), } } fn perform_transfer_execute( self, - to: &ManagedAddress, + _env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { - let _ = SendRawWrapper::::new().multi_esdt_transfer_execute( + let _ = SendRawWrapper::::new().multi_esdt_transfer_execute( to, &self, gas_limit, @@ -190,9 +203,9 @@ where } } -impl TxPayment for EgldOrEsdtTokenPayment +impl TxPayment for EgldOrEsdtTokenPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { self.amount == 0u32 @@ -200,39 +213,41 @@ where fn convert_tx_data( self, + env: &Env, from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { self.map_egld_or_esdt( (to, fc), - |(to, fc), amount| EgldPayment::from(amount).convert_tx_data(from, to, fc), - |(to, fc), esdt_payment| esdt_payment.convert_tx_data(from, to, fc), + |(to, fc), amount| EgldPayment::from(amount).convert_tx_data(env, from, to, fc), + |(to, fc), esdt_payment| esdt_payment.convert_tx_data(env, from, to, fc), ) } fn perform_transfer_execute( self, - to: &ManagedAddress, + env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { self.map_egld_or_esdt( (to, fc), |(to, fc), amount| { - EgldPayment::from(amount).perform_transfer_execute(to, gas_limit, fc) + EgldPayment::from(amount).perform_transfer_execute(env, to, gas_limit, fc) }, - |(to, fc), esdt_payment| esdt_payment.perform_transfer_execute(to, gas_limit, fc), + |(to, fc), esdt_payment| esdt_payment.perform_transfer_execute(env, to, gas_limit, fc), ) } } -impl TxPayment for EgldOrMultiEsdtPayment +impl TxPayment for EgldOrMultiEsdtPayment where - Api: CallTypeApi, + Env: TxEnv, { fn is_no_payment(&self) -> bool { self.is_empty() @@ -240,35 +255,37 @@ where fn convert_tx_data( self, + env: &Env, from: &From, - to: ManagedAddress, - fc: FunctionCall, - ) -> PaymentConversionResult + to: ManagedAddress, + fc: FunctionCall, + ) -> PaymentConversionResult where - From: TxFrom, + From: TxFrom, { match self { EgldOrMultiEsdtPayment::Egld(egld_amount) => { - EgldPayment::from(egld_amount).convert_tx_data(from, to, fc) + EgldPayment::from(egld_amount).convert_tx_data(env, from, to, fc) }, EgldOrMultiEsdtPayment::MultiEsdt(multi_esdt_payment) => { - multi_esdt_payment.convert_tx_data(from, to, fc) + multi_esdt_payment.convert_tx_data(env, from, to, fc) }, } } fn perform_transfer_execute( self, - to: &ManagedAddress, + env: &Env, + to: &ManagedAddress, gas_limit: u64, - fc: FunctionCall, + fc: FunctionCall, ) { match self { EgldOrMultiEsdtPayment::Egld(egld_amount) => { - EgldPayment::from(egld_amount).perform_transfer_execute(to, gas_limit, fc) + EgldPayment::from(egld_amount).perform_transfer_execute(env, to, gas_limit, fc) }, EgldOrMultiEsdtPayment::MultiEsdt(multi_esdt_payment) => { - multi_esdt_payment.perform_transfer_execute(to, gas_limit, fc) + multi_esdt_payment.perform_transfer_execute(env, to, gas_limit, fc) }, } } diff --git a/framework/base/src/types/interaction/tx_to.rs b/framework/base/src/types/interaction/tx_to.rs index bf7e44e2be..af733870dd 100644 --- a/framework/base/src/types/interaction/tx_to.rs +++ b/framework/base/src/types/interaction/tx_to.rs @@ -1,23 +1,23 @@ use crate::{api::ManagedTypeApi, types::ManagedAddress}; -use super::AnnotatedValue; +use super::{AnnotatedValue, TxEnv}; -pub trait TxTo +pub trait TxTo where - Api: ManagedTypeApi, + Env: TxEnv, { } -impl TxTo for () where Api: ManagedTypeApi {} +impl TxTo for () where Env: TxEnv {} -pub trait TxToSpecified: TxTo + AnnotatedValue> +pub trait TxToSpecified: TxTo + AnnotatedValue> where - Api: ManagedTypeApi, + Env: TxEnv, { } -impl TxTo for ManagedAddress where Api: ManagedTypeApi {} -impl TxToSpecified for ManagedAddress where Api: ManagedTypeApi {} +impl TxTo for ManagedAddress where Env: TxEnv {} +impl TxToSpecified for ManagedAddress where Env: TxEnv {} -impl TxTo for &ManagedAddress where Api: ManagedTypeApi {} -impl TxToSpecified for &ManagedAddress where Api: ManagedTypeApi {} +impl TxTo for &ManagedAddress where Env: TxEnv {} +impl TxToSpecified for &ManagedAddress where Env: TxEnv {} diff --git a/framework/scenario/src/facade/contract_info.rs b/framework/scenario/src/facade/contract_info.rs index 0e4f2619d9..8376e859f8 100644 --- a/framework/scenario/src/facade/contract_info.rs +++ b/framework/scenario/src/facade/contract_info.rs @@ -1,8 +1,7 @@ use std::ops::{Deref, DerefMut}; -use multiversx_sc::{ - api::CallTypeApi, - types::{AnnotatedValue, ManagedBuffer, TxFrom, TxFromSpecified, TxTo, TxToSpecified}, +use multiversx_sc::types::{ + AnnotatedValue, ManagedBuffer, TxEnv, TxFrom, TxFromSpecified, TxTo, TxToSpecified, }; use crate::multiversx_sc::{ @@ -96,48 +95,48 @@ impl CodecFrom<&ContractInfo

> for Address {} impl CodecFrom> for ManagedAddress {} impl CodecFrom<&ContractInfo

> for ManagedAddress {} -impl AnnotatedValue> for &ContractInfo

+impl AnnotatedValue> for &ContractInfo

where - Api: CallTypeApi, + Env: TxEnv, P: ProxyObjBase, { - fn annotation(&self) -> ManagedBuffer { + fn annotation(&self, _env: &Env) -> ManagedBuffer { self.scenario_address_expr.original.as_str().into() } - fn into_value(self) -> ManagedAddress { + fn into_value(self) -> ManagedAddress { (&self.scenario_address_expr.value).into() } - fn with_value_ref)>(&self, f: F) { - let ma: ManagedAddress = (&self.scenario_address_expr.value).into(); + fn with_value_ref)>(&self, f: F) { + let ma: ManagedAddress = (&self.scenario_address_expr.value).into(); f(&ma); } } -impl TxFrom for &ContractInfo

+impl TxFrom for &ContractInfo

where - Api: CallTypeApi, + Env: TxEnv, P: ProxyObjBase, { - fn resolve_address(&self) -> ManagedAddress { + fn resolve_address(&self, _env: &Env) -> ManagedAddress { (&self.scenario_address_expr.value).into() } } -impl TxFromSpecified for &ContractInfo

+impl TxFromSpecified for &ContractInfo

where - Api: CallTypeApi, + Env: TxEnv, P: ProxyObjBase, { } -impl TxTo for &ContractInfo

+impl TxTo for &ContractInfo

where - Api: CallTypeApi, + Env: TxEnv, P: ProxyObjBase, { } -impl TxToSpecified for &ContractInfo

+impl TxToSpecified for &ContractInfo

where - Api: CallTypeApi, + Env: TxEnv, P: ProxyObjBase, { } diff --git a/framework/scenario/src/facade/scenario_world_steps_tx.rs b/framework/scenario/src/facade/scenario_world_steps_tx.rs index be7763bd58..d0f6dcf499 100644 --- a/framework/scenario/src/facade/scenario_world_steps_tx.rs +++ b/framework/scenario/src/facade/scenario_world_steps_tx.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; use multiversx_sc::types::{ - AnnotatedValue, FunctionCall, ManagedAddress, Tx, TxBaseWithEnv, TxEnvironemnt, - TxFromSpecified, TxGas, TxPayment, TxToSpecified, + AnnotatedValue, FunctionCall, ManagedAddress, Tx, TxBaseWithEnv, TxEnv, TxFromSpecified, TxGas, + TxPayment, TxToSpecified, }; use crate::{api::StaticApi, facade::ScenarioWorld, scenario_model::ScCallStep}; @@ -14,23 +14,34 @@ pub struct ScenarioTxEnvironment { pub to_annotation: Option, } -impl TxEnvironemnt for ScenarioTxEnvironment { +impl TxEnv for ScenarioTxEnvironment { + type Api = StaticApi; + fn annotate_from(&mut self, to: &From) where - From: AnnotatedValue>, + From: AnnotatedValue>, { - self.from_annotation = Some(to.annotation().to_string()) + self.from_annotation = Some(to.annotation(self).to_string()) } fn annotate_to(&mut self, to: &To) where - To: AnnotatedValue>, + To: AnnotatedValue>, { - self.to_annotation = Some(to.annotation().to_string()) + self.to_annotation = Some(to.annotation(self).to_string()) + } + + fn resolve_sender_address(&self) -> ManagedAddress { + panic!("Explicit sender address expected") + } + + fn default_gas(&self) -> u64 { + // TODO: annotate + 5_000_000 } } -pub type TxScenarioBase = TxBaseWithEnv; +pub type TxScenarioBase = TxBaseWithEnv; pub trait ScenarioTx { fn run_as_scenario_step(self, world: &mut ScenarioWorld); @@ -58,12 +69,12 @@ impl ScenarioWorld { } impl ScenarioTx - for Tx> + for Tx> where - From: TxFromSpecified, - To: TxToSpecified, - Payment: TxPayment, - Gas: TxGas, + From: TxFromSpecified, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, { fn run_as_scenario_step(self, world: &mut ScenarioWorld) { let mut step = ScCallStep::new() From 32a0843336db55c09f22db585e909c72163fc230 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 9 Nov 2023 20:50:30 +0200 Subject: [PATCH 05/11] unified tx sytax - sc async callback --- .../contract_base/wrappers/send_wrapper.rs | 22 ++- .../base/src/types/interaction/async_call.rs | 62 +++----- .../src/types/interaction/callback_closure.rs | 13 +- .../types/interaction/contract_call_exec.rs | 13 +- .../interaction/contract_call_no_payment.rs | 2 +- framework/base/src/types/interaction/mod.rs | 6 + framework/base/src/types/interaction/tx.rs | 138 ++++++++++++++---- .../src/types/interaction/tx_async_call.rs | 65 +++++++++ .../interaction/tx_async_call_promises.rs | 119 +++++++++++++++ .../base/src/types/interaction/tx_callback.rs | 28 ++++ .../base/src/types/interaction/tx_data.rs | 8 + .../base/src/types/interaction/tx_env_sc.rs | 42 +----- .../src/facade/scenario_world_steps_tx.rs | 4 +- 13 files changed, 389 insertions(+), 133 deletions(-) create mode 100644 framework/base/src/types/interaction/tx_async_call.rs create mode 100644 framework/base/src/types/interaction/tx_async_call_promises.rs create mode 100644 framework/base/src/types/interaction/tx_callback.rs diff --git a/framework/base/src/contract_base/wrappers/send_wrapper.rs b/framework/base/src/contract_base/wrappers/send_wrapper.rs index 78f4f045d6..62f99b767c 100644 --- a/framework/base/src/contract_base/wrappers/send_wrapper.rs +++ b/framework/base/src/contract_base/wrappers/send_wrapper.rs @@ -15,6 +15,7 @@ use crate::{ types::{ BigUint, ContractCall, ContractCallNoPayment, EgldOrEsdtTokenIdentifier, EsdtTokenPayment, ManagedAddress, ManagedArgBuffer, ManagedBuffer, ManagedType, ManagedVec, TokenIdentifier, + Tx, }, }; @@ -314,10 +315,10 @@ where nonce: u64, amount: BigUint, ) -> ! { - ContractCallNoPayment::::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. @@ -339,10 +340,7 @@ where if amount == 0 { return; } - ContractCallNoPayment::::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. @@ -351,10 +349,10 @@ where to: ManagedAddress, payments: ManagedVec>, ) -> ! { - ContractCallNoPayment::::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. diff --git a/framework/base/src/types/interaction/async_call.rs b/framework/base/src/types/interaction/async_call.rs index 9c5554baba..bcc07bcced 100644 --- a/framework/base/src/types/interaction/async_call.rs +++ b/framework/base/src/types/interaction/async_call.rs @@ -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 -where - SA: CallTypeApi + 'static, -{ - pub(crate) to: ManagedAddress, - pub(crate) egld_payment: BigUint, - pub(crate) function_call: FunctionCall, - pub(crate) callback_call: Option>, -} +pub type AsyncCall = Tx< + TxScEnv, + (), + ManagedAddress, + EgldPayment, + (), + FunctionCall, + Option>, +>; #[allow(clippy::return_self_not_must_use)] -impl AsyncCall +impl AsyncCall where - SA: CallTypeApi, + Api: CallTypeApi, { - pub fn with_callback(self, callback_call: CallbackClosure) -> Self { - AsyncCall { - callback_call: Some(callback_call), - ..self - } + pub fn with_callback(mut self, callback_call: CallbackClosure) -> Self { + self.callback = Some(callback_call); + self } } -impl AsyncCall +impl AsyncCall where - SA: CallTypeApi, + Api: CallTypeApi + StorageWriteApi, { - pub fn call_and_exit_ignore_callback(&self) -> ! { - SendRawWrapper::::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 AsyncCall +impl AsyncCall 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::(); - } - - // 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() } } diff --git a/framework/base/src/types/interaction/callback_closure.rs b/framework/base/src/types/interaction/callback_closure.rs index 98b6fece64..a8884695f7 100644 --- a/framework/base/src/types/interaction/callback_closure.rs +++ b/framework/base/src/types/interaction/callback_closure.rs @@ -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 { +pub struct CallbackClosure +where + M: ManagedTypeApi + ErrorApi, +{ pub(super) callback_name: &'static str, pub(super) closure_args: ManagedArgBuffer, } +pub struct CallbackClosureWithGas +where + M: ManagedTypeApi + ErrorApi, +{ + pub(super) closure: CallbackClosure, + pub(super) gas_for_callback: u64, +} + /// Syntactical sugar to help macros to generate code easier. /// Unlike calling `CallbackClosure::::new`, here types can be inferred from the context. pub fn new_callback_call(callback_name: &'static str) -> CallbackClosure diff --git a/framework/base/src/types/interaction/contract_call_exec.rs b/framework/base/src/types/interaction/contract_call_exec.rs index f374d1801b..f5e875a41f 100644 --- a/framework/base/src/types/interaction/contract_call_exec.rs +++ b/framework/base/src/types/interaction/contract_call_exec.rs @@ -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, @@ -66,12 +66,11 @@ where } pub(super) fn async_call(self) -> AsyncCall { - 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) } #[cfg(feature = "promises")] diff --git a/framework/base/src/types/interaction/contract_call_no_payment.rs b/framework/base/src/types/interaction/contract_call_no_payment.rs index 69057da3e9..4fc75125a5 100644 --- a/framework/base/src/types/interaction/contract_call_no_payment.rs +++ b/framework/base/src/types/interaction/contract_call_no_payment.rs @@ -168,7 +168,7 @@ where self.function_call } - pub fn tx(self) -> Tx, (), (), (), (), FunctionCall> { + pub fn tx(self) -> Tx, (), (), (), (), FunctionCall, ()> { Tx::new_tx_from_sc().call(self.function_call) } } diff --git a/framework/base/src/types/interaction/mod.rs b/framework/base/src/types/interaction/mod.rs index f8589bbf0d..8be51394e2 100644 --- a/framework/base/src/types/interaction/mod.rs +++ b/framework/base/src/types/interaction/mod.rs @@ -20,6 +20,9 @@ mod expr_sc; mod function_call; mod managed_arg_buffer; mod tx; +mod tx_async_call; +mod tx_async_call_promises; +mod tx_callback; mod tx_data; mod tx_env; mod tx_env_sc; @@ -48,6 +51,9 @@ pub use expr_sc::ScExpr; pub use function_call::FunctionCall; pub use managed_arg_buffer::ManagedArgBuffer; pub use tx::*; +pub use tx_async_call::*; +pub use tx_async_call_promises::*; +pub use tx_callback::*; pub use tx_data::*; pub use tx_env::*; pub use tx_env_sc::*; diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs index 60b26b71e1..e24fa6d539 100644 --- a/framework/base/src/types/interaction/tx.rs +++ b/framework/base/src/types/interaction/tx.rs @@ -12,11 +12,12 @@ use crate::{ }; use super::{ - AsyncCall, ExplicitGas, FunctionCall, TxData, TxEnv, TxFrom, TxFromSpecified, TxGas, TxPayment, - TxScEnv, TxTo, TxToSpecified, + AsyncCall, ExplicitGas, FunctionCall, TxCallback, TxData, TxDataFunctionCall, TxEnv, TxFrom, + TxFromSpecified, TxGas, TxPayment, TxScEnv, TxTo, TxToSpecified, }; -pub struct Tx +#[must_use] +pub struct Tx where Env: TxEnv, From: TxFrom, @@ -24,6 +25,7 @@ where Payment: TxPayment, Gas: TxGas, Data: TxData, + Callback: TxCallback, { pub env: Env, pub from: From, @@ -31,9 +33,10 @@ where pub payment: Payment, pub gas: Gas, pub data: Data, + pub callback: Callback, } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, @@ -41,10 +44,11 @@ where Payment: TxPayment, Gas: TxGas, Data: TxData, + Callback: TxCallback, { /// TODO: does nothing, delete, added for easier copy-paste. #[inline] - pub fn nothing(self) -> Tx { + pub fn nothing(self) -> Tx { Tx { env: self.env, from: self.from, @@ -52,11 +56,12 @@ where payment: self.payment, gas: self.gas, data: self.data, + callback: self.callback, } } } -pub type TxBaseWithEnv = Tx; +pub type TxBaseWithEnv = Tx; impl TxBaseWithEnv where @@ -71,6 +76,7 @@ where payment: (), gas: (), data: (), + callback: (), } } } @@ -89,15 +95,16 @@ where // } // } -impl Tx +impl Tx where Env: TxEnv, To: TxTo, Payment: TxPayment, Gas: TxGas, Data: TxData, + Callback: TxCallback, { - pub fn from(self, from: From) -> Tx + pub fn from(self, from: From) -> Tx where From: TxFromSpecified, { @@ -110,19 +117,21 @@ where payment: self.payment, gas: self.gas, data: self.data, + callback: self.callback, } } } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, Payment: TxPayment, Gas: TxGas, Data: TxData, + Callback: TxCallback, { - pub fn to(self, to: To) -> Tx + pub fn to(self, to: To) -> Tx where To: TxToSpecified, { @@ -135,27 +144,31 @@ where payment: self.payment, gas: self.gas, data: self.data, + callback: self.callback, } } - pub fn to_caller(self) -> Tx, Payment, Gas, Data> { + pub fn to_caller( + self, + ) -> Tx, Payment, Gas, Data, Callback> { let caller = BlockchainWrapper::::new().get_caller(); self.to(caller) } } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, To: TxTo, Gas: TxGas, Data: TxData, + Callback: TxCallback, { pub fn egld( self, egld_amount: BigUint, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data, Callback> { Tx { env: self.env, from: self.from, @@ -163,17 +176,19 @@ where payment: EgldPayment { value: egld_amount }, gas: self.gas, data: self.data, + callback: self.callback, } } } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, To: TxTo, Gas: TxGas, Data: TxData, + Callback: TxCallback, { /// Adds a single ESDT token transfer to a transaction. /// @@ -181,7 +196,7 @@ where pub fn esdt>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data, Callback> { Tx { env: self.env, from: self.from, @@ -189,6 +204,7 @@ where payment: payment.into(), gas: self.gas, data: self.data, + callback: self.callback, } } @@ -196,7 +212,7 @@ where pub fn multi_esdt( self, payments: MultiEsdtPayment, // TODO: references - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data, Callback> { Tx { env: self.env, from: self.from, @@ -204,17 +220,20 @@ where payment: payments, gas: self.gas, data: self.data, + callback: self.callback, } } } -impl Tx, Gas, Data> +impl + Tx, Gas, Data, Callback> where Env: TxEnv, From: TxFrom, To: TxTo, Gas: TxGas, Data: TxData, + Callback: TxCallback, { /// Adds a single ESDT token transfer to a contract call. /// @@ -222,7 +241,7 @@ where pub fn with_esdt_transfer>>( self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data, Callback> { let mut payments = ManagedVec::new(); payments.push(self.payment); payments.push(payment.into()); @@ -233,17 +252,20 @@ where payment: payments, gas: self.gas, data: self.data, + callback: self.callback, } } } -impl Tx, Gas, Data> +impl + Tx, Gas, Data, Callback> where Env: TxEnv, From: TxFrom, To: TxTo, Gas: TxGas, Data: TxData, + Callback: TxCallback, { /// Adds a single ESDT token transfer to a contract call. /// @@ -251,23 +273,27 @@ where pub fn with_esdt_transfer>>( mut self, payment: P, - ) -> Tx, Gas, Data> { + ) -> Tx, Gas, Data, Callback> { self.payment.push(payment.into()); self } } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, To: TxTo, Payment: TxPayment, Data: TxData, + Callback: TxCallback, { /// Sets an explicit gas limit to the call. #[inline] - pub fn with_gas_limit(self, gas_limit: u64) -> Tx { + pub fn with_gas_limit( + self, + gas_limit: u64, + ) -> Tx { Tx { env: self.env, from: self.from, @@ -275,23 +301,25 @@ where payment: self.payment, gas: ExplicitGas(gas_limit), data: self.data, + callback: self.callback, } } } -impl Tx +impl Tx where Env: TxEnv, From: TxFrom, To: TxTo, Payment: TxPayment, Gas: TxGas, + Callback: TxCallback, { #[inline] pub fn call>>( self, call: FC, - ) -> Tx> { + ) -> Tx, Callback> { Tx { env: self.env, from: self.from, @@ -299,6 +327,7 @@ where payment: self.payment, gas: self.gas, data: call.into(), + callback: self.callback, } } @@ -306,7 +335,7 @@ where pub fn function_name>>( self, function_name: N, - ) -> Tx> { + ) -> Tx, Callback> { Tx { env: self.env, from: self.from, @@ -314,17 +343,20 @@ where payment: self.payment, gas: self.gas, data: FunctionCall::new(function_name), + callback: self.callback, } } } -impl Tx> +impl + Tx, Callback> where Env: TxEnv, From: TxFrom, To: TxTo, Payment: TxPayment, Gas: TxGas, + Callback: TxCallback, { #[inline] pub fn argument(mut self, arg: &T) -> Self { @@ -333,21 +365,62 @@ where } } -impl Tx> +impl Tx +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Payment: TxPayment, + Gas: TxGas, + Data: TxData, +{ + #[inline] + pub fn callback( + self, + callback: Callback, + ) -> Tx + where + Callback: TxCallback, + { + Tx { + env: self.env, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: self.data, + callback, + } + } +} + +impl Tx where Env: TxEnv, From: TxFrom, To: TxToSpecified, Payment: TxPayment, Gas: TxGas, + FC: TxDataFunctionCall, + Callback: TxCallback, { pub fn normalize_tx( self, - ) -> Tx, EgldPayment, Gas, FunctionCall> - { - let result = - self.payment - .convert_tx_data(&self.env, &self.from, self.to.into_value(), self.data); + ) -> Tx< + Env, + From, + ManagedAddress, + EgldPayment, + Gas, + FunctionCall, + Callback, + > { + let result = self.payment.convert_tx_data( + &self.env, + &self.from, + self.to.into_value(), + self.data.into(), + ); Tx { env: self.env, from: self.from, @@ -355,6 +428,7 @@ where payment: result.egld_payment, gas: self.gas, data: result.fc, + callback: self.callback, } } } diff --git a/framework/base/src/types/interaction/tx_async_call.rs b/framework/base/src/types/interaction/tx_async_call.rs new file mode 100644 index 0000000000..27d6e2a194 --- /dev/null +++ b/framework/base/src/types/interaction/tx_async_call.rs @@ -0,0 +1,65 @@ +use crate::{ + api::{CallTypeApi, StorageWriteApi}, + contract_base::SendRawWrapper, + types::{BigUint, CallbackClosure, ManagedAddress}, +}; + +use super::{ + FunctionCall, Tx, TxCallback, TxDataFunctionCall, TxEnv, TxPayment, TxScEnv, TxToSpecified, +}; + +pub trait TxAsyncCallCallback: TxCallback> +where + Api: CallTypeApi, +{ + fn save_callback_closure_to_storage(&self); +} + +impl TxAsyncCallCallback for () +where + Api: CallTypeApi, +{ + fn save_callback_closure_to_storage(&self) {} +} + +impl TxCallback> for CallbackClosure where Api: CallTypeApi {} +impl TxAsyncCallCallback for CallbackClosure +where + Api: CallTypeApi + StorageWriteApi, +{ + fn save_callback_closure_to_storage(&self) { + self.save_to_storage::(); + } +} + +impl TxCallback> for Option> where Api: CallTypeApi {} +impl TxAsyncCallCallback for Option> +where + Api: CallTypeApi + StorageWriteApi, +{ + fn save_callback_closure_to_storage(&self) { + if let Some(closure) = self { + closure.save_callback_closure_to_storage(); + } + } +} + +impl Tx, (), To, Payment, (), FC, Callback> +where + Api: CallTypeApi, + To: TxToSpecified>, + Payment: TxPayment>, + FC: TxDataFunctionCall>, + Callback: TxAsyncCallCallback, +{ + pub fn async_call_and_exit(self) -> ! { + let normalized = self.normalize_tx(); + normalized.callback.save_callback_closure_to_storage(); + SendRawWrapper::::new().async_call_raw( + &normalized.to, + &normalized.payment.value, + &normalized.data.function_name, + &normalized.data.arg_buffer, + ) + } +} diff --git a/framework/base/src/types/interaction/tx_async_call_promises.rs b/framework/base/src/types/interaction/tx_async_call_promises.rs new file mode 100644 index 0000000000..55282d708a --- /dev/null +++ b/framework/base/src/types/interaction/tx_async_call_promises.rs @@ -0,0 +1,119 @@ +use crate::{ + api::{const_handles, CallTypeApi}, + contract_base::SendRawWrapper, + types::{BigUint, CallbackClosure, ManagedAddress, ManagedBuffer, ManagedType}, +}; + +use super::{ + callback_closure::CallbackClosureWithGas, ExplicitGas, FunctionCall, Tx, TxCallback, TxGas, + TxPayment, TxScEnv, TxToSpecified, +}; + +pub trait TxPromisesCallback: TxCallback> +where + Api: CallTypeApi, +{ + fn callback_name(&self) -> &'static str; + + fn overwrite_with_serialized_args(&self, cb_closure_args_serialized: &mut ManagedBuffer); + + fn gas_for_callback(&self) -> u64; +} + +impl TxPromisesCallback for () +where + Api: CallTypeApi, +{ + fn callback_name(&self) -> &'static str { + "" + } + + fn overwrite_with_serialized_args(&self, cb_closure_args_serialized: &mut ManagedBuffer) { + cb_closure_args_serialized.overwrite(&[]); + } + + fn gas_for_callback(&self) -> u64 { + 0 + } +} + +impl TxCallback> for CallbackClosureWithGas where Api: CallTypeApi {} +impl TxPromisesCallback for CallbackClosureWithGas +where + Api: CallTypeApi, +{ + fn callback_name(&self) -> &'static str { + self.closure.callback_name + } + + fn overwrite_with_serialized_args(&self, cb_closure_args_serialized: &mut ManagedBuffer) { + self.closure + .closure_args + .serialize_overwrite(cb_closure_args_serialized); + } + + fn gas_for_callback(&self) -> u64 { + self.gas_for_callback + } +} + +impl + Tx, (), To, Payment, Gas, FunctionCall, CallbackClosure> +where + Api: CallTypeApi, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, +{ + pub fn gas_for_callback( + self, + gas: u64, + ) -> Tx, (), To, Payment, Gas, FunctionCall, CallbackClosureWithGas> + { + Tx { + env: self.env, + from: self.from, + to: self.to, + payment: self.payment, + gas: self.gas, + data: self.data, + callback: CallbackClosureWithGas { + closure: self.callback, + gas_for_callback: gas, + }, + } + } +} + +impl + Tx, (), To, Payment, ExplicitGas, FunctionCall, Callback> +where + Api: CallTypeApi, + To: TxToSpecified>, + Payment: TxPayment>, + Callback: TxPromisesCallback, +{ + // #[cfg(feature = "promises")] + pub fn async_call_promise(self) { + let callback_name = self.callback.callback_name(); + let mut cb_closure_args_serialized = + ManagedBuffer::::from_raw_handle(const_handles::MBUF_TEMPORARY_1); + self.callback + .overwrite_with_serialized_args(&mut cb_closure_args_serialized); + let extra_gas_for_callback = self.callback.gas_for_callback(); + + let normalized = self.normalize_tx(); + + SendRawWrapper::::new().create_async_call_raw( + &normalized.to, + &normalized.payment.value, + &normalized.data.function_name, + &normalized.data.arg_buffer, + callback_name, + callback_name, + normalized.gas.0, + extra_gas_for_callback, + &cb_closure_args_serialized, + ) + } +} diff --git a/framework/base/src/types/interaction/tx_callback.rs b/framework/base/src/types/interaction/tx_callback.rs new file mode 100644 index 0000000000..a7e5710e21 --- /dev/null +++ b/framework/base/src/types/interaction/tx_callback.rs @@ -0,0 +1,28 @@ +use crate::{ + api::ManagedTypeApi, + formatter::SCLowerHex, + types::{ManagedBuffer, ManagedBufferCachedBuilder}, +}; + +use super::{FunctionCall, TxEnv}; + +pub trait TxCallback +where + Env: TxEnv, +{ +} + +pub trait TxRunnableCallback: TxCallback +where + Env: TxEnv, +{ + fn run_callback(self, env: &Env); +} + +impl TxCallback for () where Env: TxEnv {} +impl TxRunnableCallback for () +where + Env: TxEnv, +{ + fn run_callback(self, _env: &Env) {} +} diff --git a/framework/base/src/types/interaction/tx_data.rs b/framework/base/src/types/interaction/tx_data.rs index 3f8da83eed..f76f9edb4a 100644 --- a/framework/base/src/types/interaction/tx_data.rs +++ b/framework/base/src/types/interaction/tx_data.rs @@ -15,6 +15,12 @@ where fn to_call_data_string(&self) -> ManagedBuffer; } +pub trait TxDataFunctionCall: TxData + Into> +where + Env: TxEnv, +{ +} + impl TxData for () where Env: TxEnv, @@ -27,6 +33,7 @@ where ManagedBuffer::new() } } +impl TxDataFunctionCall for () where Env: TxEnv {} impl TxData for FunctionCall where @@ -46,3 +53,4 @@ where result.into_managed_buffer() } } +impl TxDataFunctionCall for FunctionCall where Env: TxEnv {} diff --git a/framework/base/src/types/interaction/tx_env_sc.rs b/framework/base/src/types/interaction/tx_env_sc.rs index b7f5a18bb4..16ef20b8ab 100644 --- a/framework/base/src/types/interaction/tx_env_sc.rs +++ b/framework/base/src/types/interaction/tx_env_sc.rs @@ -69,45 +69,7 @@ where } } -impl Tx, (), To, Payment, (), FunctionCall> -where - Api: CallTypeApi, - To: TxToSpecified>, - Payment: TxPayment>, -{ - pub fn async_call(self) -> AsyncCall { - let normalized = self.normalize_tx(); - AsyncCall { - to: normalized.to, - egld_payment: normalized.payment.value, - function_call: normalized.data, - callback_call: None, - } - } -} - -impl Tx, (), To, Payment, ExplicitGas, FunctionCall> -where - Api: CallTypeApi, - To: TxToSpecified>, - Payment: TxPayment>, -{ - #[cfg(feature = "promises")] - pub fn async_call_promise(self) -> super::AsyncCallPromises { - let explicit_gas_limit = self.gas.0; - let normalized = self.normalize_tx(); - super::AsyncCallPromises { - to: normalized.to, - egld_payment: normalized.payment.value, - function_call: normalized.data, - explicit_gas_limit, - extra_gas_for_callback: 0, - callback_call: None, - } - } -} - -impl Tx, From, To, Payment, Gas, FC> +impl Tx, From, To, Payment, Gas, FC, ()> where Api: CallTypeApi, From: TxFrom>, @@ -130,7 +92,7 @@ where } } -impl Tx, From, To, Payment, Gas, ()> +impl Tx, From, To, Payment, Gas, (), ()> where Api: CallTypeApi, From: TxFrom>, diff --git a/framework/scenario/src/facade/scenario_world_steps_tx.rs b/framework/scenario/src/facade/scenario_world_steps_tx.rs index d0f6dcf499..436339cde4 100644 --- a/framework/scenario/src/facade/scenario_world_steps_tx.rs +++ b/framework/scenario/src/facade/scenario_world_steps_tx.rs @@ -69,7 +69,7 @@ impl ScenarioWorld { } impl ScenarioTx - for Tx> + for Tx, ()> where From: TxFromSpecified, To: TxToSpecified, @@ -85,6 +85,6 @@ where step = step.argument(arg.to_vec()); } - world.sc_call(step); + world.sc_call(&mut step); } } From 31c051eb2b99dd74fe722f286f3f7dd74b78501a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 9 Nov 2023 20:51:34 +0200 Subject: [PATCH 06/11] clippy fix --- framework/base/src/types/managed/wrapped/managed_option.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/base/src/types/managed/wrapped/managed_option.rs b/framework/base/src/types/managed/wrapped/managed_option.rs index 99160ff5cd..1fe4366eb0 100644 --- a/framework/base/src/types/managed/wrapped/managed_option.rs +++ b/framework/base/src/types/managed/wrapped/managed_option.rs @@ -75,6 +75,8 @@ where /// Assumes that value is Some and unwraps without checking. /// + /// # Safety + /// /// Must always be called under an `if` checking `.is_some()`, otherwise will lead to undefined behaviour. pub unsafe fn unwrap_no_check(self) -> T { T::from_handle(self.handle) From ea6c6bd321ad98506ad2d564dd827f13fad98012 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 10 Nov 2023 02:28:11 +0200 Subject: [PATCH 07/11] unified tx sytax - transfer-execute fix --- framework/base/src/types/interaction/mod.rs | 1 + .../base/src/types/interaction/tx_async_te.rs | 58 +++++++++++++++++++ .../base/src/types/interaction/tx_env_sc.rs | 37 ------------ 3 files changed, 59 insertions(+), 37 deletions(-) create mode 100644 framework/base/src/types/interaction/tx_async_te.rs diff --git a/framework/base/src/types/interaction/mod.rs b/framework/base/src/types/interaction/mod.rs index 8be51394e2..ebb0d4d67f 100644 --- a/framework/base/src/types/interaction/mod.rs +++ b/framework/base/src/types/interaction/mod.rs @@ -22,6 +22,7 @@ mod managed_arg_buffer; mod tx; mod tx_async_call; mod tx_async_call_promises; +mod tx_async_te; mod tx_callback; mod tx_data; mod tx_env; diff --git a/framework/base/src/types/interaction/tx_async_te.rs b/framework/base/src/types/interaction/tx_async_te.rs new file mode 100644 index 0000000000..4f7dfc292b --- /dev/null +++ b/framework/base/src/types/interaction/tx_async_te.rs @@ -0,0 +1,58 @@ +use core::marker::PhantomData; + +use crate::{ + api::{BlockchainApiImpl, CallTypeApi}, + contract_base::BlockchainWrapper, + types::{ManagedAddress, ManagedBuffer}, +}; + +use super::{ + contract_call_exec::TRANSFER_EXECUTE_DEFAULT_LEFTOVER, AnnotatedValue, AsyncCall, ExplicitGas, + FunctionCall, Tx, TxBaseWithEnv, TxData, TxEnv, TxFrom, TxGas, TxPayment, TxScEnv, + TxToSpecified, +}; + +impl Tx, From, To, Payment, Gas, FC, ()> +where + Api: CallTypeApi, + From: TxFrom>, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, + FC: TxData> + Into>, +{ + fn transfer_execute_with_gas(self, gas_limit: u64) { + self.to.with_value_ref(|to| { + self.payment + .perform_transfer_execute(&self.env, to, gas_limit, self.data.into()); + }); + } + + pub fn transfer_execute(self) { + let gas_limit: u64; + if self.data.is_no_call() { + if self.payment.is_no_payment() { + return; + } else { + gas_limit = 0; + } + } else { + gas_limit = self.gas.resolve_gas(&self.env); + } + + self.transfer_execute_with_gas(gas_limit); + } +} + +impl Tx, From, To, Payment, (), (), ()> +where + Api: CallTypeApi, + From: TxFrom>, + To: TxToSpecified>, + Payment: TxPayment>, +{ + /// Only allowed for simple transfers. + pub fn transfer(self) { + self.transfer_execute_with_gas(0) + } +} diff --git a/framework/base/src/types/interaction/tx_env_sc.rs b/framework/base/src/types/interaction/tx_env_sc.rs index 16ef20b8ab..05fa0265be 100644 --- a/framework/base/src/types/interaction/tx_env_sc.rs +++ b/framework/base/src/types/interaction/tx_env_sc.rs @@ -68,40 +68,3 @@ where gas_left } } - -impl Tx, From, To, Payment, Gas, FC, ()> -where - Api: CallTypeApi, - From: TxFrom>, - To: TxToSpecified>, - Payment: TxPayment>, - Gas: TxGas>, - FC: TxData> + Into>, -{ - pub fn transfer_execute(self) { - if self.payment.is_no_payment() && self.data.is_no_call() { - return; - } - - let gas_limit = self.gas.resolve_gas(&self.env); - - self.to.with_value_ref(|to| { - self.payment - .perform_transfer_execute(&self.env, to, gas_limit, self.data.into()); - }); - } -} - -impl Tx, From, To, Payment, Gas, (), ()> -where - Api: CallTypeApi, - From: TxFrom>, - To: TxToSpecified>, - Payment: TxPayment>, - Gas: TxGas>, -{ - /// Syntactic sugar, only allowed for simple transfers. - pub fn transfer(self) { - self.transfer_execute() - } -} From d8ebcf5cca385d11c5259f838bcaf8d06bbe9c6a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 10 Nov 2023 18:07:46 +0200 Subject: [PATCH 08/11] unified tx sytax - test callback prototype --- .../tests/adder_blackbox_with_values_test.rs | 3 +++ framework/base/src/types/interaction/tx.rs | 14 ------------- framework/scenario/src/facade.rs | 3 +++ .../scenario/src/facade/scenario_callbacks.rs | 19 +++++++++++++++++ .../src/facade/scenario_world_steps_tx.rs | 21 +++++++++++++------ framework/scenario/src/lib.rs | 2 +- 6 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 framework/scenario/src/facade/scenario_callbacks.rs diff --git a/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs b/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs index 061432c95f..62e74e77a8 100644 --- a/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs +++ b/contracts/examples/adder/tests/adder_blackbox_with_values_test.rs @@ -48,6 +48,9 @@ fn adder_blackbox_with_values() { 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() diff --git a/framework/base/src/types/interaction/tx.rs b/framework/base/src/types/interaction/tx.rs index e24fa6d539..b115b5e297 100644 --- a/framework/base/src/types/interaction/tx.rs +++ b/framework/base/src/types/interaction/tx.rs @@ -81,20 +81,6 @@ where } } -// impl Default for TxBaseWithEnv<()> { -// #[inline] -// fn default() -> Self { -// Self::new_with_env(()) -// } -// } - -// impl TxBaseWithEnv<()> { -// #[inline] -// pub fn new() -> Self { -// Self::default() -// } -// } - impl Tx where Env: TxEnv, diff --git a/framework/scenario/src/facade.rs b/framework/scenario/src/facade.rs index 60fb9f4d83..b6e2eef3a8 100644 --- a/framework/scenario/src/facade.rs +++ b/framework/scenario/src/facade.rs @@ -1,5 +1,6 @@ mod contract_info; mod debugger_backend; +mod scenario_callbacks; mod scenario_world; mod scenario_world_runner; mod scenario_world_steps; @@ -9,5 +10,7 @@ mod scenario_world_whitebox; mod whitebox_contract; pub use contract_info::ContractInfo; +pub use scenario_callbacks::*; pub use scenario_world::ScenarioWorld; +pub use scenario_world_steps_tx::*; pub use whitebox_contract::WhiteboxContract; diff --git a/framework/scenario/src/facade/scenario_callbacks.rs b/framework/scenario/src/facade/scenario_callbacks.rs new file mode 100644 index 0000000000..1429f7ed16 --- /dev/null +++ b/framework/scenario/src/facade/scenario_callbacks.rs @@ -0,0 +1,19 @@ +use multiversx_sc::types::{TxCallback, TxRunnableCallback}; + +use crate::scenario_model::TxResponse; + +use super::ScenarioTxEnvironment; + +pub struct WithTxResult(pub F) +where + F: FnOnce(&TxResponse); + +impl TxCallback for WithTxResult where F: FnOnce(&TxResponse) {} +impl TxRunnableCallback for WithTxResult +where + F: FnOnce(&TxResponse), +{ + fn run_callback(self, env: &ScenarioTxEnvironment) { + (self.0)(env.response.as_ref().unwrap()) + } +} diff --git a/framework/scenario/src/facade/scenario_world_steps_tx.rs b/framework/scenario/src/facade/scenario_world_steps_tx.rs index 436339cde4..ad8134edca 100644 --- a/framework/scenario/src/facade/scenario_world_steps_tx.rs +++ b/framework/scenario/src/facade/scenario_world_steps_tx.rs @@ -2,16 +2,21 @@ use std::path::PathBuf; use multiversx_sc::types::{ AnnotatedValue, FunctionCall, ManagedAddress, Tx, TxBaseWithEnv, TxEnv, TxFromSpecified, TxGas, - TxPayment, TxToSpecified, + TxPayment, TxRunnableCallback, TxToSpecified, }; -use crate::{api::StaticApi, facade::ScenarioWorld, scenario_model::ScCallStep}; +use crate::{ + api::StaticApi, + facade::ScenarioWorld, + scenario_model::{ScCallStep, TxResponse}, +}; #[derive(Default, Debug, Clone)] pub struct ScenarioTxEnvironment { pub context_path: PathBuf, pub from_annotation: Option, pub to_annotation: Option, + pub response: Option, } impl TxEnv for ScenarioTxEnvironment { @@ -68,23 +73,27 @@ impl ScenarioWorld { } } -impl ScenarioTx - for Tx, ()> +impl ScenarioTx + for Tx, Callback> where From: TxFromSpecified, To: TxToSpecified, Payment: TxPayment, Gas: TxGas, + Callback: TxRunnableCallback, { fn run_as_scenario_step(self, world: &mut ScenarioWorld) { + let mut env = self.env; let mut step = ScCallStep::new() - .from(self.env.from_annotation.unwrap().as_str()) - .to(self.env.to_annotation.unwrap().as_str()) + .from(env.from_annotation.as_ref().unwrap().as_str()) + .to(env.to_annotation.as_ref().unwrap().as_str()) .function(self.data.function_name.to_string().as_str()); for arg in self.data.arg_buffer.iter_buffers() { step = step.argument(arg.to_vec()); } world.sc_call(&mut step); + env.response = step.response; + self.callback.run_callback(&env); } } diff --git a/framework/scenario/src/lib.rs b/framework/scenario/src/lib.rs index d46d27fc68..17c13a2f5c 100644 --- a/framework/scenario/src/lib.rs +++ b/framework/scenario/src/lib.rs @@ -42,7 +42,7 @@ pub use crate::scenario as mandos_system; // Re-exporting the whole mandos crate for easier use in tests. pub use multiversx_chain_scenario_format as scenario_format; -pub use facade::{ContractInfo, ScenarioWorld, WhiteboxContract}; +pub use facade::{ContractInfo, ScenarioWorld, WhiteboxContract, WithTxResult}; use std::path::Path; From 3b1f9140b494305efd8d776c283634d13a895ee0 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 27 Nov 2023 18:15:29 +0200 Subject: [PATCH 09/11] cleanup --- .../base/src/types/interaction/expr_sc.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/framework/base/src/types/interaction/expr_sc.rs b/framework/base/src/types/interaction/expr_sc.rs index a5ba6a5e82..56fed5a5e3 100644 --- a/framework/base/src/types/interaction/expr_sc.rs +++ b/framework/base/src/types/interaction/expr_sc.rs @@ -88,22 +88,4 @@ pub mod tests { b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012", ); } - - // #[test] - // fn test_sc_address() { - // let context = InterpreterContext::default(); - // assert_eq!( - // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a_____________________".to_vec(), - // interpret_string("sc:a", &context) - // ); - // assert_eq!( - // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(), - // interpret_string("sc:12345678901234567890120s", &context) - // ); - // // trims excess - // assert_eq!( - // b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(), - // interpret_string("sc:12345678901234567890120sx", &context) - // ); - // } } From b7102b273db13f55d362df489bea4b33a66be8df Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 27 Nov 2023 18:34:24 +0200 Subject: [PATCH 10/11] cleanup --- framework/base/src/types/interaction/tx_payment.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs index 747b6ac3bf..66fa7b6785 100644 --- a/framework/base/src/types/interaction/tx_payment.rs +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -79,7 +79,6 @@ where fc: FunctionCall, ) { EgldPayment::no_payment().perform_transfer_execute(env, to, gas_limit, fc); - // perform_transfer_execute_egld(BigUint::zero(), to, gas_limit, fc); } } From 9639b7fa6028f04dcc1ce0e72eaa3ef2432771cb Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 27 Nov 2023 18:45:05 +0200 Subject: [PATCH 11/11] cleanup --- .../base/src/types/interaction/tx_env.rs | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/framework/base/src/types/interaction/tx_env.rs b/framework/base/src/types/interaction/tx_env.rs index c9746b36aa..59e5406970 100644 --- a/framework/base/src/types/interaction/tx_env.rs +++ b/framework/base/src/types/interaction/tx_env.rs @@ -22,31 +22,3 @@ pub trait TxEnv: Sized { fn default_gas(&self) -> u64; } - -// pub struct TxTestingEnvironemnt -// where -// Api: CallTypeApi, -// { -// pub(super) _phantom: PhantomData, -// pub from_annotation: ManagedBuffer, -// pub to_annotation: ManagedBuffer, -// } - -// impl TxEnv for TxTestingEnvironemnt -// where -// Api: CallTypeApi, -// { -// fn annotate_from(&mut self, from: &From) -// where -// From: AnnotatedValue>, -// { -// self.from_annotation = from.annotation(); -// } - -// fn annotate_to(&mut self, to: &To) -// where -// To: AnnotatedValue>, -// { -// self.to_annotation = to.annotation(); -// } -// }