diff --git a/contracts/examples/adder/interact/src/basic_interact.rs b/contracts/examples/adder/interact/src/basic_interact.rs index 3dd78d048a..a5687aaede 100644 --- a/contracts/examples/adder/interact/src/basic_interact.rs +++ b/contracts/examples/adder/interact/src/basic_interact.rs @@ -2,26 +2,24 @@ mod basic_interact_cli; mod basic_interact_config; mod basic_interact_state; -use adder::ProxyTrait; +use adder::{temp_proxy, ProxyTrait}; use basic_interact_config::Config; use basic_interact_state::State; use clap::Parser; use multiversx_sc_snippets::{ env_logger, - multiversx_sc::{storage::mappers::SingleValue, types::Address}, + multiversx_sc::types::{Address, ReturnsNewAddress, ReturnsSimilar}, multiversx_sc_scenario::{ api::StaticApi, bech32, mandos_system::ScenarioRunner, num_bigint::BigUint, scenario_format::interpret_trait::{InterpretableFrom, InterpreterContext}, - scenario_model::{ - BytesValue, ScCallStep, ScDeployStep, ScQueryStep, Scenario, TransferStep, TxExpect, - }, + scenario_model::{BytesValue, ScDeployStep, Scenario}, standalone::retrieve_account_as_scenario_set_state, - test_wallets, ContractInfo, + test_wallets, ContractInfo, WithRawTxResponse, }, - tokio, Interactor, StepBuffer, + tokio, Interactor, InteractorPrepareAsync, StepBuffer, }; const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; @@ -100,28 +98,32 @@ impl AdderInteract { async fn deploy(&mut self) { self.set_state().await; - self.interactor - .sc_deploy_use_result( - ScDeployStep::new() - .call(self.state.default_adder().init(BigUint::from(0u64))) - .from(&self.wallet_address) - .code(&self.adder_code), - |new_address, tr| { - tr.result.unwrap_or_else(|err| { - panic!( - "deploy failed: status: {}, message: {}", - err.status, err.message - ) - }); - - let new_address_bech32 = bech32::encode(&new_address); - println!("new address: {new_address_bech32}"); - - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_adder_address(&new_address_expr); - }, - ) + let new_address = self + .interactor + .tx() + .from(&self.wallet_address) + .typed(temp_proxy::AdderProxy) + .init(0u32) + .code(&self.adder_code) + .with_result(WithRawTxResponse(|response| { + let err = &response.tx_error; + assert!( + err.is_success(), + "deploy failed: status: {}, message: {}", + err.status, + err.message + ); + })) + .returns(ReturnsNewAddress) + .prepare_async() + .run() .await; + + let new_address_bech32 = bech32::encode(&new_address.to_address()); + println!("new address: {new_address_bech32}"); + + let new_address_expr = format!("bech32:{new_address_bech32}"); + self.state.set_adder_address(&new_address_expr); } async fn multi_deploy(&mut self, count: &u8) { @@ -164,38 +166,42 @@ impl AdderInteract { } async fn feed_contract_egld(&mut self) { - let _ = self - .interactor - .transfer( - TransferStep::new() - .from(&self.wallet_address) - .to(self.state.adder()) - .egld_value("0,050000000000000000"), - ) + self.interactor + .tx() + .from(&self.wallet_address) + .to(self.state.adder().to_address()) + .egld(50000000000000000u64.into()) // TODO: annotate "0,050000000000000000" + .prepare_async() + .run() .await; } async fn add(&mut self, value: u64) { self.interactor - .sc_call( - ScCallStep::new() - .call(self.state.adder().add(value)) - .from(&self.wallet_address) - .expect( - TxExpect::ok().additional_error_message("performing add failed with: "), - ), - ) + .tx() + .from(&self.wallet_address) + .to(self.state.adder().to_address()) + .typed(temp_proxy::AdderProxy) + .add(value) + .prepare_async() + .run() .await; println!("successfully performed add"); } async fn print_sum(&mut self) { - self.interactor - .sc_query_use_result(ScQueryStep::new().call(self.state.adder().sum()), |tr| { - let sum: SingleValue = tr.result.unwrap(); - println!("sum: {}", sum.into()); - }) + let sum = self + .interactor + .query() + .to(self.state.adder().to_address()) + .typed(temp_proxy::AdderProxy) + .sum() + .returns(ReturnsSimilar::::new()) + .prepare_async() + .run() .await; + + println!("sum: {sum}"); } } diff --git a/framework/base/src/types/interaction/annotated.rs b/framework/base/src/types/interaction/annotated.rs index 878cdb5e1f..d190f65631 100644 --- a/framework/base/src/types/interaction/annotated.rs +++ b/framework/base/src/types/interaction/annotated.rs @@ -1,4 +1,4 @@ -use crate::types::{ManagedAddress, ManagedBuffer}; +use crate::types::{heap::Address, ManagedAddress, ManagedBuffer}; use super::TxEnv; @@ -37,6 +37,32 @@ where } } +impl AnnotatedValue> for Address +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer { + ManagedAddress::from(self).hex_expr() + } + + fn into_value(self, _env: &Env) -> ManagedAddress { + self.into() + } +} + +impl AnnotatedValue> for &Address +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer { + ManagedAddress::from(*self).hex_expr() + } + + fn into_value(self, _env: &Env) -> ManagedAddress { + self.into() + } +} + impl AnnotatedValue> for ManagedBuffer where Env: TxEnv, diff --git a/framework/base/src/types/interaction/tx_from.rs b/framework/base/src/types/interaction/tx_from.rs index ef6cbb1462..039cb2c34c 100644 --- a/framework/base/src/types/interaction/tx_from.rs +++ b/framework/base/src/types/interaction/tx_from.rs @@ -1,4 +1,4 @@ -use crate::types::ManagedAddress; +use crate::types::{heap::Address, ManagedAddress}; use super::{AnnotatedValue, TxEnv}; @@ -44,3 +44,25 @@ where } } impl TxFromSpecified for &ManagedAddress where Env: TxEnv {} + +impl TxFrom for Address +where + Env: TxEnv, +{ + fn resolve_address(&self, _env: &Env) -> ManagedAddress { + self.into() + } +} + +impl TxFromSpecified for Address where Env: TxEnv {} + +impl TxFrom for &Address +where + Env: TxEnv, +{ + fn resolve_address(&self, _env: &Env) -> ManagedAddress { + ManagedAddress::from_address(self) + } +} + +impl TxFromSpecified for &Address where Env: TxEnv {} diff --git a/framework/base/src/types/interaction/tx_payment.rs b/framework/base/src/types/interaction/tx_payment.rs index 0f50f50e53..d7f7fd90f5 100644 --- a/framework/base/src/types/interaction/tx_payment.rs +++ b/framework/base/src/types/interaction/tx_payment.rs @@ -1,6 +1,8 @@ use crate::{ api::ManagedTypeApi, contract_base::SendRawWrapper, + formatter::FormatBuffer, + imports::{BigUint, ManagedBuffer, ManagedBufferCachedBuilder, ManagedVec}, types::{ EgldOrEsdtTokenPayment, EgldOrMultiEsdtPayment, EgldPayment, EsdtTokenPayment, ManagedAddress, MultiEsdtPayment, @@ -19,6 +21,50 @@ where pub fc: FunctionCall, } +#[derive(Clone)] +pub struct AnnotatedEgldPayment +where + Api: ManagedTypeApi, +{ + pub value: BigUint, + pub annotation: ManagedBuffer, +} + +impl AnnotatedEgldPayment +where + Api: ManagedTypeApi, +{ + pub fn new_egld(value: BigUint) -> Self { + let mut annotation = ManagedBufferCachedBuilder::default(); + annotation.append_display(&value); + AnnotatedEgldPayment { + value, + annotation: annotation.into_managed_buffer(), + } + } +} + +#[derive(Clone)] +pub struct FullPaymentData +where + Api: ManagedTypeApi, +{ + pub egld: Option>, + pub multi_esdt: MultiEsdtPayment, +} + +impl Default for FullPaymentData +where + Api: ManagedTypeApi, +{ + fn default() -> Self { + Self { + egld: None, + multi_esdt: Default::default(), + } + } +} + /// Describes a payment that is part of a transaction. pub trait TxPayment where @@ -44,6 +90,8 @@ where gas_limit: u64, fc: FunctionCall, ); + + fn into_full_payment_data(self) -> FullPaymentData; } /// Marks a payment object that only contains EGLD or nothing at all. @@ -88,6 +136,10 @@ where ) { EgldPayment::no_payment().perform_transfer_execute(env, to, gas_limit, fc); } + + fn into_full_payment_data(self) -> FullPaymentData { + FullPaymentData::default() + } } impl TxPaymentEgldOnly for () @@ -139,6 +191,13 @@ where &fc.arg_buffer, ); } + + fn into_full_payment_data(self) -> FullPaymentData<::Api> { + FullPaymentData { + egld: Some(AnnotatedEgldPayment::new_egld(self.value)), + multi_esdt: ManagedVec::new(), + } + } } impl TxPaymentEgldOnly for EgldPayment @@ -184,6 +243,13 @@ where ) { MultiEsdtPayment::from_single_item(self).perform_transfer_execute(env, to, gas_limit, fc); } + + fn into_full_payment_data(self) -> FullPaymentData<::Api> { + FullPaymentData { + egld: None, + multi_esdt: MultiEsdtPayment::from_single_item(self), + } + } } impl TxPayment for MultiEsdtPayment @@ -226,6 +292,13 @@ where &fc.arg_buffer, ); } + + fn into_full_payment_data(self) -> FullPaymentData<::Api> { + FullPaymentData { + egld: None, + multi_esdt: self, + } + } } impl TxPayment for EgldOrEsdtTokenPayment @@ -268,6 +341,14 @@ where |(to, fc), esdt_payment| esdt_payment.perform_transfer_execute(env, to, gas_limit, fc), ) } + + fn into_full_payment_data(self) -> FullPaymentData { + self.map_egld_or_esdt( + (), + |(), amount| TxPayment::::into_full_payment_data(EgldPayment::from(amount)), + |(), esdt_payment| TxPayment::::into_full_payment_data(esdt_payment), + ) + } } impl TxPayment for EgldOrMultiEsdtPayment @@ -314,6 +395,17 @@ where }, } } + + fn into_full_payment_data(self) -> FullPaymentData { + match self { + EgldOrMultiEsdtPayment::Egld(egld_amount) => { + TxPayment::::into_full_payment_data(EgldPayment::from(egld_amount)) + }, + EgldOrMultiEsdtPayment::MultiEsdt(multi_esdt_payment) => { + TxPayment::::into_full_payment_data(multi_esdt_payment) + }, + } + } } 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 ecba5ad1f6..984587a6ed 100644 --- a/framework/base/src/types/interaction/tx_to.rs +++ b/framework/base/src/types/interaction/tx_to.rs @@ -1,4 +1,4 @@ -use crate::types::ManagedAddress; +use crate::types::{heap::Address, ManagedAddress}; use super::{AnnotatedValue, TxEnv}; @@ -39,3 +39,25 @@ where f(self) } } + +impl TxTo for Address where Env: TxEnv {} +impl TxToSpecified for Address +where + Env: TxEnv, +{ + fn with_address_ref)>(&self, _env: &Env, f: F) { + let managed_address = ManagedAddress::from(self); + f(&managed_address) + } +} + +impl TxTo for &Address where Env: TxEnv {} +impl TxToSpecified for &Address +where + Env: TxEnv, +{ + fn with_address_ref)>(&self, _env: &Env, f: F) { + let managed_address = ManagedAddress::from(*self); + f(&managed_address) + } +} diff --git a/framework/scenario/src/facade/world_tx.rs b/framework/scenario/src/facade/world_tx.rs index 8c2314767e..92f842cf55 100644 --- a/framework/scenario/src/facade/world_tx.rs +++ b/framework/scenario/src/facade/world_tx.rs @@ -5,7 +5,7 @@ mod scenario_env; mod scenario_env_deploy; mod scenario_env_exec; mod scenario_env_query; -mod scenario_env_util; +pub mod scenario_env_util; mod scenario_rh_list; mod scenario_rh_list_item; mod with_tx_raw_response; diff --git a/framework/scenario/src/facade/world_tx/scenario_env.rs b/framework/scenario/src/facade/world_tx/scenario_env.rs index 0392fa2bb6..758cff63f1 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env.rs @@ -48,13 +48,3 @@ pub trait ScenarioTxRun { fn run(self) -> Self::Returns; } - -/// Provides a method to run scenario steps and txs, which also takes a `ScenarioWorld` argument. -/// -/// It is used for chaining methods that can't include the reference to the ScenarioWorld in the environment -/// for reasons imposed by lifetimes/the borrow checker. -pub trait ScenarioTxRunOnWorld { - type Returns; - - fn run_on_world(self, world: &mut ScenarioWorld) -> Self::Returns; -} diff --git a/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs b/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs index 103228c3e2..d9317a094c 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs @@ -13,7 +13,7 @@ use multiversx_sc::{ use crate::{ api::StaticApi, scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, - ScenarioEnvExec, ScenarioTxEnv, ScenarioTxRun, ScenarioTxRunOnWorld, ScenarioWorld, + ScenarioEnvExec, ScenarioTxEnv, ScenarioTxRun, ScenarioWorld, }; use super::{scenario_env_util::*, RHListScenario, ScenarioTxEnvData}; diff --git a/framework/scenario/src/facade/world_tx/scenario_env_exec.rs b/framework/scenario/src/facade/world_tx/scenario_env_exec.rs index 844bbf9c97..3758673006 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_exec.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_exec.rs @@ -13,7 +13,7 @@ use multiversx_sc::{ use crate::{ api::StaticApi, scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, - ScenarioTxEnv, ScenarioTxRun, ScenarioTxRunOnWorld, ScenarioWorld, + ScenarioTxEnv, ScenarioTxRun, ScenarioWorld, }; use super::{scenario_env_util::*, RHListScenario, ScenarioTxEnvData}; diff --git a/framework/scenario/src/facade/world_tx/scenario_env_query.rs b/framework/scenario/src/facade/world_tx/scenario_env_query.rs index f95f3cce33..e7d73ec729 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_query.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_query.rs @@ -10,7 +10,7 @@ use multiversx_sc::{ use crate::{ api::StaticApi, scenario_model::TxResponse, RHListScenario, ScenarioTxEnv, ScenarioTxEnvData, - ScenarioTxRun, ScenarioTxRunOnWorld, ScenarioWorld, + ScenarioTxRun, ScenarioWorld, }; use super::scenario_env_util::*; diff --git a/framework/scenario/src/facade/world_tx/scenario_env_util.rs b/framework/scenario/src/facade/world_tx/scenario_env_util.rs index e05a4de177..19776e9a0d 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_util.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_util.rs @@ -10,11 +10,14 @@ use multiversx_sc::{ use crate::{ api::StaticApi, - scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, ScQueryStep, TxResponse}, + scenario_model::{ + AddressValue, BigUintValue, BytesValue, ScCallStep, ScDeployStep, ScQueryStep, + TransferStep, TxResponse, + }, RHListScenario, ScenarioEnvExec, ScenarioWorld, }; -pub(super) fn address_annotated(env: &Env, from: Addr) -> AddressValue +pub fn address_annotated(env: &Env, from: Addr) -> AddressValue where Env: TxEnv, Addr: AnnotatedValue>, @@ -26,7 +29,7 @@ where } } -pub(super) fn code_annotated(env: &Env, code: Code) -> BytesValue +pub fn code_annotated(env: &Env, code: Code) -> BytesValue where Env: TxEnv, CodeValue: TxCodeValue, @@ -38,11 +41,11 @@ where } } -pub(super) fn tx_to_sc_call_step( +pub fn tx_to_sc_call_step( env: &Env, from: From, to: To, - _payment: Payment, + payment: Payment, _gas: Gas, data: FunctionCall, ) -> ScCallStep @@ -61,13 +64,18 @@ where step.tx.arguments.push(arg.to_vec().into()); } + let full_payment_data = payment.into_full_payment_data(); + if let Some(annotated_egld_payment) = full_payment_data.egld { + step.tx.egld_value = annotated_egld_payment.into(); + } + step } -pub(super) fn tx_to_sc_deploy_step( +pub fn tx_to_sc_deploy_step( env: &Env, from: From, - _payment: Payment, + payment: Payment, _gas: Gas, data: DeployCall>, ) -> ScDeployStep @@ -85,14 +93,15 @@ where step.tx.arguments.push(arg.to_vec().into()); } + let full_payment_data = payment.into_full_payment_data(); + if let Some(annotated_egld_payment) = full_payment_data.egld { + step.tx.egld_value = annotated_egld_payment.into(); + } + step } -pub(super) fn tx_to_sc_query_step( - env: &Env, - to: To, - data: FunctionCall, -) -> ScQueryStep +pub fn tx_to_sc_query_step(env: &Env, to: To, data: FunctionCall) -> ScQueryStep where Env: TxEnv, To: TxToSpecified, @@ -107,7 +116,33 @@ where step } -pub(super) fn process_result( +pub fn tx_to_transfer_step( + env: &Env, + from: From, + to: To, + payment: Payment, + _gas: Gas, +) -> TransferStep +where + Env: TxEnv, + From: TxFromSpecified, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, +{ + let mut step = TransferStep::new() + .from(address_annotated(env, from)) + .to(address_annotated(env, to)); + + let full_payment_data = payment.into_full_payment_data(); + if let Some(annotated_egld_payment) = full_payment_data.egld { + step.tx.egld_value = annotated_egld_payment.into(); + } + + step +} + +pub fn process_result( response: Option, result_handler: RH, ) -> ::Unpacked diff --git a/framework/scenario/src/facade/world_tx/scenario_rh_list_item.rs b/framework/scenario/src/facade/world_tx/scenario_rh_list_item.rs index 4de678b586..36440e0313 100644 --- a/framework/scenario/src/facade/world_tx/scenario_rh_list_item.rs +++ b/framework/scenario/src/facade/world_tx/scenario_rh_list_item.rs @@ -1,7 +1,7 @@ use multiversx_sc::{ codec::{CodecFrom, TopDecodeMulti, TopEncodeMulti}, types::{ - ManagedAddress, RHList, RHListItem, ReturnsExact, ReturnsSimilar, TxEnv, + ManagedAddress, RHList, RHListItem, ReturnsExact, ReturnsNewAddress, ReturnsSimilar, TxEnv, WithResultNewAddress, WithResultSimilar, }, }; @@ -63,6 +63,20 @@ where } } +impl RHListItemScenario for ReturnsNewAddress +where + Env: TxEnv, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns { + let new_address = tx_response + .new_deployed_address + .clone() + .expect("missing returned address"); + + new_address.into() + } +} + impl RHListItemScenario for WithResultNewAddress where Env: TxEnv, diff --git a/framework/scenario/src/scenario/model/value/value_set_big_uint.rs b/framework/scenario/src/scenario/model/value/value_set_big_uint.rs index c21c913851..003370509d 100644 --- a/framework/scenario/src/scenario/model/value/value_set_big_uint.rs +++ b/framework/scenario/src/scenario/model/value/value_set_big_uint.rs @@ -95,7 +95,7 @@ impl From<&BigUint> for BigUintValue { impl From> for BigUintValue { fn from(from: crate::multiversx_sc::types::BigUint) -> Self { - let value = BigUint::from_bytes_be(from.to_bytes_be().as_slice()); + let value = from.to_alloc(); BigUintValue { original: ValueSubTree::Str(value.to_string()), value, @@ -103,6 +103,17 @@ impl From> for BigUin } } +impl From> + for BigUintValue +{ + fn from(from: crate::multiversx_sc::types::AnnotatedEgldPayment) -> Self { + BigUintValue { + value: from.value.to_alloc(), + original: ValueSubTree::Str(from.annotation.to_string()), + } + } +} + impl From<&str> for BigUintValue { fn from(from: &str) -> Self { BigUintValue::interpret_from(from, &InterpreterContext::default()) diff --git a/framework/scenario/src/scenario/model/value/value_set_bytes.rs b/framework/scenario/src/scenario/model/value/value_set_bytes.rs index 33fb896d01..eaed19cea0 100644 --- a/framework/scenario/src/scenario/model/value/value_set_bytes.rs +++ b/framework/scenario/src/scenario/model/value/value_set_bytes.rs @@ -1,3 +1,5 @@ +use multiversx_sc::types::{AnnotatedValue, ManagedBuffer, TxCodeValue, TxEnv}; + use crate::scenario_format::{ interpret_trait::{InterpretableFrom, InterpreterContext, IntoRaw}, serde_raw::ValueSubTree, @@ -134,3 +136,33 @@ impl fmt::Display for BytesValue { self.original.fmt(f) } } + +impl AnnotatedValue> for BytesValue +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer<::Api> { + self.original.to_concatenated_string().into() + } + + fn into_value(self, _env: &Env) -> ManagedBuffer { + self.value.into() + } +} + +impl TxCodeValue for BytesValue where Env: TxEnv {} + +impl AnnotatedValue> for &BytesValue +where + Env: TxEnv, +{ + fn annotation(&self, _env: &Env) -> ManagedBuffer<::Api> { + self.original.to_concatenated_string().into() + } + + fn into_value(self, _env: &Env) -> ManagedBuffer { + self.value.clone().into() + } +} + +impl TxCodeValue for &BytesValue where Env: TxEnv {} diff --git a/framework/snippets/src/interactor.rs b/framework/snippets/src/interactor.rs index 7034932d91..346d117f80 100644 --- a/framework/snippets/src/interactor.rs +++ b/framework/snippets/src/interactor.rs @@ -8,7 +8,11 @@ use multiversx_sdk::{ data::{address::Address as ErdrsAddress, network_config::NetworkConfig}, wallet::Wallet, }; -use std::{collections::HashMap, path::Path, time::Duration}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + time::Duration, +}; use crate::Sender; @@ -22,6 +26,8 @@ pub struct Interactor { pub(crate) waiting_time_ms: u64, pub pre_runners: ScenarioRunnerList, pub post_runners: ScenarioRunnerList, + + pub current_dir: PathBuf, } impl Interactor { @@ -35,6 +41,7 @@ impl Interactor { waiting_time_ms: 0, pre_runners: ScenarioRunnerList::empty(), post_runners: ScenarioRunnerList::empty(), + current_dir: PathBuf::default(), } } diff --git a/framework/snippets/src/itx.rs b/framework/snippets/src/itx.rs new file mode 100644 index 0000000000..dd34244999 --- /dev/null +++ b/framework/snippets/src/itx.rs @@ -0,0 +1,17 @@ +#![allow(unused)] // TEMP + +mod interactor_env; +mod interactor_env_deploy; +mod interactor_env_exec; +mod interactor_env_query; +mod interactor_env_transf; +mod interactor_rh_list; +mod interactor_rh_list_item; + +pub use interactor_env::*; +pub use interactor_env_deploy::*; +pub use interactor_env_exec::InteractorEnvExec; +pub use interactor_env_query::InteractorEnvQuery; +pub use interactor_env_transf::*; +pub use interactor_rh_list::*; +pub use interactor_rh_list_item::*; diff --git a/framework/snippets/src/itx/interactor_env.rs b/framework/snippets/src/itx/interactor_env.rs new file mode 100644 index 0000000000..521389a713 --- /dev/null +++ b/framework/snippets/src/itx/interactor_env.rs @@ -0,0 +1,24 @@ +use std::path::PathBuf; + +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::types::{AnnotatedValue, ManagedAddress, TxBaseWithEnv, TxEnv}, + scenario_model::TxResponse, + ScenarioTxEnvData, ScenarioWorld, +}; + +use crate::Interactor; + +impl Interactor { + pub(crate) fn new_env_data(&self) -> ScenarioTxEnvData { + ScenarioTxEnvData { + context_path: self.current_dir.clone(), + } + } +} + +pub trait InteractorPrepareAsync { + type Exec; + + fn prepare_async(self) -> Self::Exec; +} diff --git a/framework/snippets/src/itx/interactor_env_deploy.rs b/framework/snippets/src/itx/interactor_env_deploy.rs new file mode 100644 index 0000000000..ea11ecc338 --- /dev/null +++ b/framework/snippets/src/itx/interactor_env_deploy.rs @@ -0,0 +1,135 @@ +use std::path::PathBuf; + +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + tuple_util::NestedTupleFlatten, + types::{ + AnnotatedValue, Code, DeployCall, FunctionCall, ManagedAddress, ManagedBuffer, + RHListSync, Tx, TxBaseWithEnv, TxCodeSource, TxCodeSourceSpecified, TxCodeValue, TxEnv, + TxFromSpecified, TxGas, TxPayment, TxToSpecified, + }, + }, + scenario_env_util::*, + scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, + RHListScenario, ScenarioEnvExec, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, + ScenarioWorld, +}; + +use crate::{Interactor, InteractorPrepareAsync}; + +use super::InteractorEnvExec; + +pub struct InteractorDeployStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + world: &'w mut Interactor, + sc_deploy_step: ScDeployStep, + result_handler: RH, +} + +impl<'w, From, Payment, Gas, CodeValue, RH> InteractorPrepareAsync + for Tx< + InteractorEnvExec<'w>, + From, + (), + Payment, + Gas, + DeployCall, Code>, + RH, + > +where + From: TxFromSpecified>, + Payment: TxPayment>, + Gas: TxGas>, + CodeValue: TxCodeValue>, + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + type Exec = InteractorDeployStep<'w, RH>; + + fn prepare_async(self) -> Self::Exec { + let mut sc_deploy_step = + tx_to_sc_deploy_step(&self.env, self.from, self.payment, self.gas, self.data); + InteractorDeployStep { + world: self.env.world, + sc_deploy_step, + result_handler: self.result_handler, + } + } +} + +impl<'w, RH> InteractorDeployStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + pub async fn run(self) -> ::Unpacked { + let mut sc_deploy_step = self.sc_deploy_step; + self.world.sc_deploy(&mut sc_deploy_step).await; + process_result(sc_deploy_step.response, self.result_handler) + } +} + +impl Interactor { + pub async fn chain_deploy(&mut self, f: F) -> &mut Self + where + From: TxFromSpecified, + Payment: TxPayment, + Gas: TxGas, + CodeValue: TxCodeValue, + RH: RHListScenario, + F: FnOnce( + TxBaseWithEnv, + ) -> Tx< + ScenarioTxEnvData, + From, + (), + Payment, + Gas, + DeployCall>, + RH, + >, + { + let env = self.new_env_data(); + let tx_base = TxBaseWithEnv::new_with_env(env); + let tx = f(tx_base); + let mut step = tx_to_sc_deploy_step(&tx.env, tx.from, tx.payment, tx.gas, tx.data); + self.sc_deploy(&mut step).await; + process_result(step.response, tx.result_handler); + self + } + + pub async fn run_deploy( + &mut self, + f: F, + ) -> ::Unpacked + where + From: TxFromSpecified, + Payment: TxPayment, + Gas: TxGas, + CodeValue: TxCodeValue, + RH: RHListScenario, + RH::ListReturns: NestedTupleFlatten, + F: FnOnce( + TxBaseWithEnv, + ) -> Tx< + ScenarioTxEnvData, + From, + (), + Payment, + Gas, + DeployCall>, + RH, + >, + { + let env = self.new_env_data(); + let tx_base = TxBaseWithEnv::new_with_env(env); + let tx = f(tx_base); + let mut step = tx_to_sc_deploy_step(&tx.env, tx.from, tx.payment, tx.gas, tx.data); + self.sc_deploy(&mut step).await; + process_result(step.response, tx.result_handler) + } +} diff --git a/framework/snippets/src/itx/interactor_env_exec.rs b/framework/snippets/src/itx/interactor_env_exec.rs new file mode 100644 index 0000000000..dc5269e6e9 --- /dev/null +++ b/framework/snippets/src/itx/interactor_env_exec.rs @@ -0,0 +1,122 @@ +use std::path::PathBuf; + +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + tuple_util::NestedTupleFlatten, + types::{ + AnnotatedValue, Code, DeployCall, FunctionCall, ManagedAddress, ManagedBuffer, + RHListSync, Tx, TxBaseWithEnv, TxCodeSource, TxCodeSourceSpecified, TxCodeValue, TxEnv, + TxFromSpecified, TxGas, TxPayment, TxToSpecified, + }, + }, + scenario_env_util::*, + scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, + RHListScenario, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, +}; + +use crate::{Interactor, InteractorPrepareAsync}; + +/// Environment for executing transactions. +pub struct InteractorEnvExec<'w> { + pub world: &'w mut Interactor, + pub data: ScenarioTxEnvData, +} + +impl<'w> TxEnv for InteractorEnvExec<'w> { + type Api = StaticApi; + + fn resolve_sender_address(&self) -> ManagedAddress { + panic!("Explicit sender address expected") + } + + fn default_gas(&self) -> u64 { + self.data.default_gas() + } +} + +impl<'w> ScenarioTxEnv for InteractorEnvExec<'w> { + fn env_data(&self) -> &ScenarioTxEnvData { + &self.data + } +} + +pub struct InteractorCallStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + world: &'w mut Interactor, + sc_call_step: ScCallStep, + result_handler: RH, +} + +impl<'w, From, To, Payment, Gas, RH> InteractorPrepareAsync + for Tx, From, To, Payment, Gas, FunctionCall, RH> +where + From: TxFromSpecified>, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + type Exec = InteractorCallStep<'w, RH>; + + fn prepare_async(self) -> Self::Exec { + let mut sc_call_step = tx_to_sc_call_step( + &self.env, + self.from, + self.to, + self.payment, + self.gas, + self.data, + ); + InteractorCallStep { + world: self.env.world, + sc_call_step, + result_handler: self.result_handler, + } + } +} + +impl<'w, RH> InteractorCallStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + pub async fn run(self) -> ::Unpacked { + let mut sc_call_step = self.sc_call_step; + self.world.sc_call(&mut sc_call_step).await; + process_result(sc_call_step.response, self.result_handler) + } +} + +impl Interactor { + pub fn tx(&mut self) -> TxBaseWithEnv> { + let data = self.new_env_data(); + let env = InteractorEnvExec { world: self, data }; + Tx::new_with_env(env) + } + + pub async fn chain_call(&mut self, f: F) -> &mut Self + where + From: TxFromSpecified, + To: TxToSpecified, + Payment: TxPayment, + Gas: TxGas, + RH: RHListScenario, + F: FnOnce( + TxBaseWithEnv, + ) + -> Tx, RH>, + { + let env = self.new_env_data(); + let tx_base = TxBaseWithEnv::new_with_env(env); + let tx = f(tx_base); + let mut step = tx_to_sc_call_step(&tx.env, tx.from, tx.to, tx.payment, tx.gas, tx.data); + self.sc_call(&mut step).await; + process_result(step.response, tx.result_handler); + self + } +} diff --git a/framework/snippets/src/itx/interactor_env_query.rs b/framework/snippets/src/itx/interactor_env_query.rs new file mode 100644 index 0000000000..20e61e571f --- /dev/null +++ b/framework/snippets/src/itx/interactor_env_query.rs @@ -0,0 +1,106 @@ +use std::path::PathBuf; + +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + tuple_util::NestedTupleFlatten, + types::{ + AnnotatedValue, FunctionCall, ManagedAddress, Tx, TxBaseWithEnv, TxEnv, + TxFromSpecified, TxGas, TxPayment, TxToSpecified, + }, + }, + scenario_env_util::*, + scenario_model::{ScQueryStep, TxResponse}, + RHListScenario, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, +}; + +use crate::{Interactor, InteractorPrepareAsync}; + +pub struct InteractorEnvQuery<'w> { + pub world: &'w mut Interactor, + pub data: ScenarioTxEnvData, +} + +impl<'w> TxEnv for InteractorEnvQuery<'w> { + type Api = StaticApi; + + fn resolve_sender_address(&self) -> ManagedAddress { + panic!("Explicit sender address expected") + } + + fn default_gas(&self) -> u64 { + self.data.default_gas() + } +} + +impl<'w> ScenarioTxEnv for InteractorEnvQuery<'w> { + fn env_data(&self) -> &ScenarioTxEnvData { + &self.data + } +} + +pub struct InteractorQueryStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + world: &'w mut Interactor, + sc_query_step: ScQueryStep, + result_handler: RH, +} + +impl<'w, To, RH> InteractorPrepareAsync + for Tx, (), To, (), (), FunctionCall, RH> +where + To: TxToSpecified>, + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + type Exec = InteractorQueryStep<'w, RH>; + + fn prepare_async(self) -> Self::Exec { + let mut sc_query_step = tx_to_sc_query_step(&self.env, self.to, self.data); + InteractorQueryStep { + world: self.env.world, + sc_query_step, + result_handler: self.result_handler, + } + } +} + +impl<'w, RH> InteractorQueryStep<'w, RH> +where + RH: RHListScenario>, + RH::ListReturns: NestedTupleFlatten, +{ + pub async fn run(self) -> ::Unpacked { + let mut sc_call_step = self.sc_query_step; + self.world.sc_query(&mut sc_call_step).await; + process_result(sc_call_step.response, self.result_handler) + } +} + +impl Interactor { + pub fn query(&mut self) -> TxBaseWithEnv> { + let data = self.new_env_data(); + let env = InteractorEnvQuery { world: self, data }; + Tx::new_with_env(env) + } + + pub async fn chain_query(&mut self, f: F) -> &mut Self + where + To: TxToSpecified, + RH: RHListScenario, + F: FnOnce( + TxBaseWithEnv, + ) -> Tx, RH>, + { + let env = self.new_env_data(); + let tx_base = TxBaseWithEnv::new_with_env(env); + let tx = f(tx_base); + let mut step = tx_to_sc_query_step(&tx.env, tx.to, tx.data); + self.sc_query(&mut step).await; + process_result(step.response, tx.result_handler); + self + } +} diff --git a/framework/snippets/src/itx/interactor_env_transf.rs b/framework/snippets/src/itx/interactor_env_transf.rs new file mode 100644 index 0000000000..993b2b04d6 --- /dev/null +++ b/framework/snippets/src/itx/interactor_env_transf.rs @@ -0,0 +1,51 @@ +use std::path::PathBuf; + +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + tuple_util::NestedTupleFlatten, + types::{ + AnnotatedValue, Code, DeployCall, FunctionCall, ManagedAddress, ManagedBuffer, + RHListSync, Tx, TxBaseWithEnv, TxCodeSource, TxCodeSourceSpecified, TxCodeValue, TxEnv, + TxFromSpecified, TxGas, TxPayment, TxToSpecified, + }, + }, + scenario_env_util::*, + scenario_model::{ + AddressValue, BytesValue, ScCallStep, ScDeployStep, TransferStep, TxResponse, + }, + RHListScenario, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, +}; + +use crate::{Interactor, InteractorEnvExec, InteractorPrepareAsync}; + +pub struct InteractorTransferStep<'w> { + world: &'w mut Interactor, + step: TransferStep, +} + +impl<'w, From, To, Payment, Gas> InteractorPrepareAsync + for Tx, From, To, Payment, Gas, (), ()> +where + From: TxFromSpecified>, + To: TxToSpecified>, + Payment: TxPayment>, + Gas: TxGas>, +{ + type Exec = InteractorTransferStep<'w>; + + fn prepare_async(self) -> Self::Exec { + let mut sc_call_step = + tx_to_transfer_step(&self.env, self.from, self.to, self.payment, self.gas); + InteractorTransferStep { + world: self.env.world, + step: sc_call_step, + } + } +} + +impl<'w> InteractorTransferStep<'w> { + pub async fn run(self) { + self.world.transfer(self.step).await; + } +} diff --git a/framework/snippets/src/itx/interactor_rh_list.rs b/framework/snippets/src/itx/interactor_rh_list.rs new file mode 100644 index 0000000000..e48c94dc5f --- /dev/null +++ b/framework/snippets/src/itx/interactor_rh_list.rs @@ -0,0 +1,51 @@ +use multiversx_sc_scenario::{ + multiversx_sc::types::{ConsNoRet, ConsRet, OriginalResultMarker, RHList, RHListItem, TxEnv}, + scenario_model::TxResponse, + RHListItemScenario, +}; + +pub trait RHListScenario: RHList +where + Env: TxEnv, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::ListReturns; +} + +impl RHListScenario for () +where + Env: TxEnv, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::ListReturns {} +} + +impl RHListScenario for OriginalResultMarker +where + Env: TxEnv, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::ListReturns {} +} + +impl RHListScenario for ConsRet +where + Env: TxEnv, + Head: RHListItemScenario, + Tail: RHListScenario, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::ListReturns { + let head_result = self.head.item_scenario_result(tx_response); + let tail_result = self.tail.item_scenario_result(tx_response); + (head_result, tail_result) + } +} + +impl RHListScenario for ConsNoRet +where + Env: TxEnv, + Head: RHListItemScenario, + Tail: RHListScenario, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::ListReturns { + self.head.item_scenario_result(tx_response); + self.tail.item_scenario_result(tx_response) + } +} diff --git a/framework/snippets/src/itx/interactor_rh_list_item.rs b/framework/snippets/src/itx/interactor_rh_list_item.rs new file mode 100644 index 0000000000..7a6063fbc6 --- /dev/null +++ b/framework/snippets/src/itx/interactor_rh_list_item.rs @@ -0,0 +1,76 @@ +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + codec::{CodecFrom, TopDecodeMulti, TopEncodeMulti}, + types::{ + ManagedAddress, RHList, RHListItem, ReturnsExact, ReturnsSimilar, TxEnv, + WithResultNewAddress, WithResultSimilar, + }, + }, + scenario_model::{TxResponse, TypedResponse}, +}; + +pub trait RHListItemScenario: RHListItem +where + Env: TxEnv, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns; +} + +impl RHListItemScenario for ReturnsExact +where + Env: TxEnv, + Original: TopDecodeMulti, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns { + let response = TypedResponse::::from_raw(tx_response); + response + .result + .expect("ReturnsExact expects that transaction is successful") + } +} + +impl RHListItemScenario for ReturnsSimilar +where + Env: TxEnv, + Original: TopEncodeMulti, + T: CodecFrom, +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns { + let response = TypedResponse::::from_raw(tx_response); + response + .result + .expect("ReturnsSimilar expects that transaction is successful") + } +} + +impl RHListItemScenario for WithResultSimilar +where + Env: TxEnv, + Original: TopEncodeMulti, + T: CodecFrom, + F: FnOnce(T), +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns { + let response = TypedResponse::::from_raw(tx_response); + let value = response + .result + .expect("ReturnsExact expects that transaction is successful"); + (self.f)(value); + } +} + +impl RHListItemScenario for WithResultNewAddress +where + Env: TxEnv, + F: FnOnce(&ManagedAddress), +{ + fn item_scenario_result(self, tx_response: &TxResponse) -> Self::Returns { + let new_address = tx_response + .new_deployed_address + .clone() + .expect("missing returned address"); + + (self.f)(&ManagedAddress::from_address(&new_address)); + } +} diff --git a/framework/snippets/src/lib.rs b/framework/snippets/src/lib.rs index 2081c11a58..9bf2c07e33 100644 --- a/framework/snippets/src/lib.rs +++ b/framework/snippets/src/lib.rs @@ -10,6 +10,7 @@ mod interactor_sc_transfer; mod interactor_sender; mod interactor_tx_spec; mod interactor_vm_query; +mod itx; mod step_buffer; pub use env_logger; @@ -18,6 +19,7 @@ pub use interactor::*; pub use interactor_dns::*; pub use interactor_sender::*; pub use interactor_tx_spec::*; +pub use itx::*; pub use log; pub use multiversx_sc_scenario::{self, multiversx_sc}; pub use multiversx_sdk as erdrs; // TODO: remove