diff --git a/e2e/tests/contracts.rs b/e2e/tests/contracts.rs index e01f3a75c..2c4e9020a 100644 --- a/e2e/tests/contracts.rs +++ b/e2e/tests/contracts.rs @@ -224,6 +224,44 @@ async fn test_multi_call_beginner() -> Result<()> { Ok(()) } +#[tokio::test] +async fn test_multi_call_simulate_vec() -> Result<()> { + setup_program_test!( + Wallets("wallet"), + Abigen(Contract( + name = "TestContract", + project = "e2e/sway/contracts/contract_test" + )), + Deploy( + name = "contract_instance", + contract = "TestContract", + wallet = "wallet", + random_salt = false, + ), + ); + + let contract_methods = contract_instance.methods(); + let call_handler_1 = contract_methods.get_single(7); + let call_handler_2 = contract_methods.get_single(42); + let call_handler_3 = contract_methods.get_single(1212); + + let mut multi_call_handler = CallHandler::new_multi_call(wallet.clone()) + .add_call(call_handler_1) + .add_call(call_handler_2) + .add_call(call_handler_3); + + let values: Vec = multi_call_handler + .simulate_vec(Execution::state_read_only()) + .await? + .value; + + assert_eq!(values[0], 7); + assert_eq!(values[1], 42); + assert_eq!(values[2], 1212); + + Ok(()) +} + #[tokio::test] async fn test_multi_call_pro() -> Result<()> { setup_program_test!( diff --git a/packages/fuels-accounts/src/provider/retryable_client.rs b/packages/fuels-accounts/src/provider/retryable_client.rs index c9d460330..339c962a3 100644 --- a/packages/fuels-accounts/src/provider/retryable_client.rs +++ b/packages/fuels-accounts/src/provider/retryable_client.rs @@ -59,7 +59,10 @@ impl CacheableRpcs for RetryableClient { } impl RetryableClient { - pub(crate) async fn connect_with_fallbacks(urls: &[ impl AsRef], retry_config: RetryConfig) -> Result { + pub(crate) async fn connect_with_fallbacks( + urls: &[impl AsRef], + retry_config: RetryConfig, + ) -> Result { let client = FuelClient::with_urls(&urls).map_err(|e| error!(Provider, "{e}"))?; let node_info = client.node_info().await?; diff --git a/packages/fuels-programs/src/calls/call_handler.rs b/packages/fuels-programs/src/calls/call_handler.rs index 96f696587..172ea4c82 100644 --- a/packages/fuels-programs/src/calls/call_handler.rs +++ b/packages/fuels-programs/src/calls/call_handler.rs @@ -528,6 +528,35 @@ where self.get_response(tx_status) } + /// Call contract methods on the node, in a simulated manner, meaning the state of the + /// blockchain is *not* modified but simulated. + /// Returns a Vec of values where all calls must return the same type `T`. + pub async fn simulate_vec( + &mut self, + Execution { + execution_type, + at_height, + }: Execution, + ) -> Result>> { + let provider = self.account.try_provider()?; + + let tx_status = if let ExecutionType::StateReadOnly = execution_type { + let tx = self + .transaction_builder() + .await? + .with_build_strategy(ScriptBuildStrategy::StateReadOnly) + .build(provider) + .await?; + + provider.dry_run_opt(tx, false, Some(0), at_height).await? + } else { + let tx = self.build_tx().await?; + provider.dry_run_opt(tx, true, None, at_height).await? + }; + + self.get_response_vec(tx_status) + } + /// Simulates a call without needing to resolve the generic for the return type async fn simulate_without_decode(&self) -> Result<()> { let provider = self.account.try_provider()?; @@ -562,6 +591,32 @@ where }) } + /// Create a [`CallResponse`] from `TxStatus` returning a Vec of values. + /// All calls must return the same type `T`. + pub fn get_response_vec( + &self, + tx_status: TxStatus, + ) -> Result>> { + let success = tx_status.take_success_checked(Some(&self.log_decoder))?; + let mut receipt_parser = ReceiptParser::new(&success.receipts, self.decoder_config); + + let values = self + .call + .iter() + .map(|call| { + let token = receipt_parser.parse_call(call.contract_id, &call.output_param)?; + T::from_token(token) + }) + .collect::>>()?; + + Ok(CallResponse { + value: values, + log_decoder: self.log_decoder.clone(), + tx_id: self.cached_tx_id, + tx_status: success, + }) + } + /// Simulates the call and attempts to resolve missing contract outputs. /// Forwards the received error if it cannot be fixed. pub async fn determine_missing_contracts(mut self) -> Result {