From cbcdd193d2a8917d2cf0c0baf535ce609d7139d5 Mon Sep 17 00:00:00 2001 From: Mihai Calin Luca Date: Wed, 25 Jun 2025 17:56:39 +0300 Subject: [PATCH] chain simulator tests with payments --- .../forwarder-interactor/src/interact.rs | 199 +++++++++++++----- .../forwarder-interactor/src/proxy.rs | 6 +- .../forwarder-interactor/state.toml | 2 +- .../tests/interact_cs_tests.rs | 111 ++++++++++ .../forwarder/src/forwarder_proxy.rs | 6 +- .../forwarder/src/fwd_call_async.rs | 1 + .../forwarder/src/fwd_call_transf_exec.rs | 1 + 7 files changed, 269 insertions(+), 57 deletions(-) diff --git a/contracts/feature-tests/composability/forwarder-interactor/src/interact.rs b/contracts/feature-tests/composability/forwarder-interactor/src/interact.rs index 83921f981a..f26a7cbba9 100644 --- a/contracts/feature-tests/composability/forwarder-interactor/src/interact.rs +++ b/contracts/feature-tests/composability/forwarder-interactor/src/interact.rs @@ -4,6 +4,7 @@ mod config; mod proxy; pub use config::Config; +use forwarder::vault_proxy; use multiversx_sc_snippets::imports::*; pub use proxy::Color; use serde::{Deserialize, Serialize}; @@ -23,6 +24,8 @@ pub const FORWARDER_UPDATE_TOKEN_INTERACTOR_TRACE_PATH: &str = "scenarios/forwarder_update_token_scenario.scen.json"; pub const FORWARDER_MODIFY_CREATOR_INTERACTOR_TRACE_PATH: &str = "scenarios/forwarder_modify_creator_scenario.scen.json"; +const VAULT_CODE: MxscPath = + MxscPath::new("../contracts/feature-tests/composability/vault/output/vault.mxsc.json"); pub async fn forwarder_cli() { env_logger::init(); @@ -32,7 +35,7 @@ pub async fn forwarder_cli() { let cmd = args.next().expect("at least one argument required"); let mut interact = ContractInteract::new(Config::new(), None).await; match cmd.as_str() { - "deploy" => interact.deploy().await, + // "deploy" => interact.deploy().await, "send_egld" => interact.send_egld().await, "echo_arguments_sync" => interact.echo_arguments_sync().await, "echo_arguments_sync_twice" => interact.echo_arguments_sync_twice().await, @@ -87,12 +90,12 @@ pub async fn forwarder_cli() { .await }, "transf_exec_multi_accept_funds" => interact.transf_exec_multi_accept_funds().await, - "forward_transf_exec_reject_funds_multi_transfer" => { - interact - .forward_transf_exec_reject_funds_multi_transfer() - .await - }, - "transf_exec_multi_reject_funds" => interact.transf_exec_multi_reject_funds().await, + // "forward_transf_exec_reject_funds_multi_transfer" => { + // interact + // .forward_transf_exec_reject_funds_multi_transfer() + // .await + // }, + // "transf_exec_multi_reject_funds" => interact.transf_exec_multi_reject_funds().await, "changeOwnerAddress" => interact.change_owner().await, "deploy_contract" => interact.deploy_contract().await, "deploy_two_contracts" => interact.deploy_two_contracts().await, @@ -175,7 +178,7 @@ impl Drop for State { } pub struct ContractInteract { - interactor: Interactor, + pub interactor: Interactor, pub wallet_address: Address, contract_code: BytesValue, pub state: State, @@ -211,8 +214,8 @@ impl ContractInteract { } } - pub async fn deploy(&mut self) { - let new_address = self + pub async fn deploy(&mut self) -> (Bech32Address, u64) { + let (new_address, gas_used) = self .interactor .tx() .from(&self.wallet_address) @@ -221,14 +224,18 @@ impl ContractInteract { .init() .code(&self.contract_code) .returns(ReturnsNewAddress) + .returns(ReturnsGasUsed) .run() .await; + let new_address_bech32 = bech32::encode(&new_address); self.state.set_address(Bech32Address::from_bech32_string( new_address_bech32.clone(), )); println!("new address: {new_address_bech32}"); + + (new_address.into(), gas_used) } pub async fn send_egld(&mut self) { @@ -916,58 +923,68 @@ impl ContractInteract { println!("Result: {response:?}"); } - pub async fn forward_transf_exec_reject_funds_multi_transfer(&mut self) { - let to = Address::zero(); - let token_payments = MultiValueVec::from(vec![MultiValue3::< - TokenIdentifier, - u64, - BigUint, - >::from(( - TokenIdentifier::from_esdt_bytes(&b""[..]), - 0u64, - BigUint::::from(0u128), - ))]); + // pub async fn forward_transf_exec_reject_funds_multi_transfer(&mut self) { + // let to = Address::zero(); + // let token_payments = MultiValueVec::from(vec![MultiValue3::< + // TokenIdentifier, + // u64, + // BigUint, + // >::from(( + // TokenIdentifier::from_esdt_bytes(&b""[..]), + // 0u64, + // BigUint::::from(0u128), + // ))]); - let response = self - .interactor - .tx() - .from(&self.wallet_address) - .to(self.state.current_address()) - .gas(80_000_000u64) - .typed(proxy::ForwarderProxy) - .forward_transf_exec_reject_funds_multi_transfer(to, token_payments) - .returns(ReturnsResultUnmanaged) - .run() - .await; + // let response = self + // .interactor + // .tx() + // .from(&self.wallet_address) + // .to(self.state.current_address()) + // .gas(80_000_000u64) + // .typed(proxy::ForwarderProxy) + // .forward_transf_exec_reject_funds_multi_transfer(to, token_payments) + // .returns(ReturnsResultUnmanaged) + // .run() + // .await; - println!("Result: {response:?}"); - } + // println!("Result: {response:?}"); + // } - pub async fn transf_exec_multi_reject_funds(&mut self) { - let to = Address::zero(); - let token_payments = MultiValueVec::from(vec![MultiValue3::< - TokenIdentifier, - u64, - BigUint, - >::from(( - TokenIdentifier::from_esdt_bytes(&b""[..]), - 0u64, - BigUint::::from(0u128), - ))]); + pub async fn transf_exec_multi_reject_funds( + &mut self, + to: Bech32Address, + vec_of_payments: Vec>, + ) -> u64 { + let vec = vec_of_payments + .iter() + .map(|e| { + MultiValue3::from((e.token_identifier.clone(), e.token_nonce, e.amount.clone())) + }) + .collect::, u64, BigUint>, + >>(); - let response = self + let vec_param = + ManagedVec::>::from(vec_of_payments); + + let (response, gas_used) = self .interactor .tx() .from(&self.wallet_address) .to(self.state.current_address()) .gas(80_000_000u64) .typed(proxy::ForwarderProxy) - .transf_exec_multi_reject_funds(to, token_payments) - .returns(ReturnsResultUnmanaged) + .transf_exec_multi_reject_funds(to, vec) + .payment(vec_param) + .returns(ExpectMessage("reject_funds")) + .returns(ReturnsGasUsed) .run() .await; println!("Result: {response:?}"); + + gas_used } pub async fn change_owner(&mut self) { @@ -2102,4 +2119,90 @@ impl ContractInteract { .run() .await; } + + pub async fn deploy_vault(&mut self) -> (Bech32Address, u64) { + let (new_address, gas_used) = self + .interactor + .tx() + .from(&self.wallet_address) + .gas(300_000_000u64) + .typed(vault_proxy::VaultProxy) + .init(OptionalValue::>::None) + .code(VAULT_CODE) + .returns(ReturnsNewAddress) + .returns(ReturnsGasUsed) + .run() + .await; + let new_address_bech32 = bech32::encode(&new_address); + + println!("new vault address: {new_address_bech32}"); + + (new_address.into(), gas_used) + } + + pub async fn forward_send_async_reject_multi_transfer( + &mut self, + to: Bech32Address, + vec_of_payments: Vec>, + ) { + let vec = vec_of_payments + .iter() + .map(|e| { + MultiValue3::from((e.token_identifier.clone(), e.token_nonce, e.amount.clone())) + }) + .collect::, u64, BigUint>, + >>(); + + let vec_param = + ManagedVec::>::from(vec_of_payments); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(80_000_000u64) + .typed(proxy::ForwarderProxy) + .send_async_reject_multi_transfer(to.into_address(), vec) + .payment(vec_param) + .returns(ReturnsHandledOrError::new().returns(ExpectMessage("reject_funds"))) + .run() + .await; + + println!("Result: {response:?}"); + } + + pub async fn issue_fungible_token_from_wallet( + &mut self, + token_display_name: &[u8], + token_ticker: &[u8], + initial_supply: u64, + ) -> (String, u64) { + let egld_amount = BigUint::::from(5_000_000_000_000_000_0u128); + + let (response, gas_used) = self + .interactor + .tx() + .from(&self.wallet_address) + .to(ESDTSystemSCAddress) + .gas(80_000_000u64) + .typed(system_proxy::ESDTSystemSCProxy) + .issue_fungible( + egld_amount, + token_display_name, + token_ticker, + BigUint::from(initial_supply), + FungibleTokenProperties::default(), + ) + .returns(ReturnsNewTokenIdentifier) + .returns(ReturnsGasUsed) + .run() + .await; + + println!("Result: {response:?}"); + + (response, gas_used) + } } diff --git a/contracts/feature-tests/composability/forwarder-interactor/src/proxy.rs b/contracts/feature-tests/composability/forwarder-interactor/src/proxy.rs index d0f865d802..ae7f73f7da 100644 --- a/contracts/feature-tests/composability/forwarder-interactor/src/proxy.rs +++ b/contracts/feature-tests/composability/forwarder-interactor/src/proxy.rs @@ -366,9 +366,8 @@ where self, to: Arg0, payment_args: Arg1, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx - .payment(NotPayable) .raw_call("send_async_reject_multi_transfer") .argument(&to) .argument(&payment_args) @@ -482,9 +481,8 @@ where self, to: Arg0, payment_args: Arg1, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx - .payment(NotPayable) .raw_call("transf_exec_multi_reject_funds") .argument(&to) .argument(&payment_args) diff --git a/contracts/feature-tests/composability/forwarder-interactor/state.toml b/contracts/feature-tests/composability/forwarder-interactor/state.toml index 2db4efcb4e..5e4ecfae52 100644 --- a/contracts/feature-tests/composability/forwarder-interactor/state.toml +++ b/contracts/feature-tests/composability/forwarder-interactor/state.toml @@ -1 +1 @@ -contract_address = "erd1qqqqqqqqqqqqqpgqm6d7cp70y7ft6xe78f7np9agdm7vpce6d8sskuuxnu" +contract_address = "erd1qqqqqqqqqqqqqpgqlz32muzjtu40pp9lapy35n0cvrdxll47d8ss9ne0ta" diff --git a/contracts/feature-tests/composability/forwarder-interactor/tests/interact_cs_tests.rs b/contracts/feature-tests/composability/forwarder-interactor/tests/interact_cs_tests.rs index 62a9c9d1a0..d53fbca5c6 100644 --- a/contracts/feature-tests/composability/forwarder-interactor/tests/interact_cs_tests.rs +++ b/contracts/feature-tests/composability/forwarder-interactor/tests/interact_cs_tests.rs @@ -1,6 +1,11 @@ use forwarder_interact::{Config, ContractInteract}; use multiversx_sc_snippets::imports::*; +const MOCK_AMOUNT: u64 = 1_000u64; +const MOCK_TOKEN_DISPLAY_NAME: &[u8] = b"TESTTKN"; +const MOCK_TOKEN_TICKER: &[u8] = b"TEST"; +const ONE_EGLD: u64 = 1_000_000_000_000_000_000u64; + // Simple deploy test that runs using the chain simulator configuration. // In order for this test to work, make sure that the `config.toml` file contains the chain simulator config (or choose it manually) // The chain simulator should already be installed and running before attempting to run this test. @@ -13,3 +18,109 @@ async fn deploy_test_forwarder_cs() { interactor.deploy().await; } + +#[tokio::test] +#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] +async fn forward_send_async_reject_multi_transfer() { + let mut interact = ContractInteract::new(Config::chain_simulator_config(), None).await; + + // deploy fwd + let (forwarder_address, _) = interact.deploy().await; + + // deploy vault + let (vault_address, _) = interact.deploy_vault().await; + + // mint tokens + let (token_id, _) = interact + .issue_fungible_token_from_wallet(MOCK_TOKEN_DISPLAY_NAME, MOCK_TOKEN_TICKER, MOCK_AMOUNT) + .await; + + // build token payment + let egld_payment = EgldOrEsdtTokenPayment::egld_payment(BigUint::from(ONE_EGLD)); + let esdt_payment = EgldOrEsdtTokenPayment::new( + EgldOrEsdtTokenIdentifier::esdt(token_id.as_bytes()), + 0u64, + BigUint::from(MOCK_AMOUNT), + ); + + let vec_of_payments = vec![egld_payment, esdt_payment]; + + // send call + interact + .forward_send_async_reject_multi_transfer(vault_address, vec_of_payments) + .await; + + // verify balance + let fwd_account = interact + .interactor + .get_account(forwarder_address.as_address()) + .await; + assert!(fwd_account.balance == ONE_EGLD.to_string()); + + let esdts = interact + .interactor + .get_account_esdt(forwarder_address.as_address()) + .await; + + assert!(esdts.contains_key(&token_id)); + assert_eq!( + esdts.get(&token_id).unwrap().balance, + MOCK_AMOUNT.to_string() + ); +} + +#[tokio::test] +#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] +async fn forward_transf_exec_reject_multi_transfer() { + let mut interact = ContractInteract::new(Config::chain_simulator_config(), None).await; + + let alice = interact.wallet_address.clone(); + let initial_alice_balance = interact.interactor.get_account(&alice).await.balance; + let mut gas_used = 0u64; + + // deploy fwd + gas_used += interact.deploy().await.1; + + // deploy vault + let (vault_address, v_gas_used) = interact.deploy_vault().await; + gas_used += v_gas_used; + + // mint tokens + let (token_id, t_gas_used) = interact + .issue_fungible_token_from_wallet(MOCK_TOKEN_DISPLAY_NAME, MOCK_TOKEN_TICKER, MOCK_AMOUNT) + .await; + gas_used += t_gas_used; + + // build token payment + let egld_payment = EgldOrEsdtTokenPayment::egld_payment(BigUint::from(ONE_EGLD)); + let esdt_payment = EgldOrEsdtTokenPayment::new( + EgldOrEsdtTokenIdentifier::esdt(token_id.as_bytes()), + 0u64, + BigUint::from(MOCK_AMOUNT), + ); + + let vec_of_payments = vec![egld_payment, esdt_payment]; + + // send call + gas_used += interact + .transf_exec_multi_reject_funds(vault_address, vec_of_payments) + .await; + + // verify balance in wallet + let current_alice_balance = interact.interactor.get_account(&alice).await.balance; + + // current = initial - gas_used + // 0.240244346985 ???? + let current_balance: u128 = current_alice_balance.parse().unwrap(); + let initial_balance: u128 = initial_alice_balance.parse().unwrap(); + + assert_eq!(current_balance, initial_balance - gas_used as u128); + + let esdts = interact.interactor.get_account_esdt(&alice).await; + + assert!(esdts.contains_key(&token_id)); + assert_eq!( + esdts.get(&token_id).unwrap().balance, + MOCK_AMOUNT.to_string() + ); +} diff --git a/contracts/feature-tests/composability/forwarder/src/forwarder_proxy.rs b/contracts/feature-tests/composability/forwarder/src/forwarder_proxy.rs index d0f865d802..ae7f73f7da 100644 --- a/contracts/feature-tests/composability/forwarder/src/forwarder_proxy.rs +++ b/contracts/feature-tests/composability/forwarder/src/forwarder_proxy.rs @@ -366,9 +366,8 @@ where self, to: Arg0, payment_args: Arg1, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx - .payment(NotPayable) .raw_call("send_async_reject_multi_transfer") .argument(&to) .argument(&payment_args) @@ -482,9 +481,8 @@ where self, to: Arg0, payment_args: Arg1, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx - .payment(NotPayable) .raw_call("transf_exec_multi_reject_funds") .argument(&to) .argument(&payment_args) diff --git a/contracts/feature-tests/composability/forwarder/src/fwd_call_async.rs b/contracts/feature-tests/composability/forwarder/src/fwd_call_async.rs index b25883073c..52b00504a0 100644 --- a/contracts/feature-tests/composability/forwarder/src/fwd_call_async.rs +++ b/contracts/feature-tests/composability/forwarder/src/fwd_call_async.rs @@ -187,6 +187,7 @@ pub trait ForwarderAsyncCallModule { .async_call_and_exit(); } + #[payable("*")] #[endpoint] fn send_async_reject_multi_transfer( &self, diff --git a/contracts/feature-tests/composability/forwarder/src/fwd_call_transf_exec.rs b/contracts/feature-tests/composability/forwarder/src/fwd_call_transf_exec.rs index 73178bc58b..9161d9b576 100644 --- a/contracts/feature-tests/composability/forwarder/src/fwd_call_transf_exec.rs +++ b/contracts/feature-tests/composability/forwarder/src/fwd_call_transf_exec.rs @@ -105,6 +105,7 @@ pub trait ForwarderTransferExecuteModule { .transfer_execute() } + #[payable("*")] #[endpoint] fn transf_exec_multi_reject_funds( &self,