From e3e0047d1492755dda6bfe6294c90f545c916dae Mon Sep 17 00:00:00 2001 From: Prithvish Baidya Date: Thu, 31 Jul 2025 12:42:53 +0530 Subject: [PATCH 1/2] convert tuple parameters correctly for dyn-abi --- server/src/http/dyn_contract.rs | 85 ++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/server/src/http/dyn_contract.rs b/server/src/http/dyn_contract.rs index 3114982..a9c8da7 100644 --- a/server/src/http/dyn_contract.rs +++ b/server/src/http/dyn_contract.rs @@ -1,4 +1,5 @@ use alloy::hex; +use alloy::json_abi::Param; use alloy::{ dyn_abi::{DynSolType, DynSolValue, JsonAbiExt}, json_abi::{Function, JsonAbi}, @@ -213,6 +214,54 @@ impl ContractCall { }) } + fn json_to_sol( + json_values: &[JsonValue], + json_abi_params: &[Param], + ) -> Result, String> { + if json_values.len() != json_abi_params.len() { + return Err(format!( + "Parameter count mismatch: expected {}, got {}", + json_abi_params.len(), + json_values.len() + )); + } + + let mut parsed_params = Vec::new(); + + for (json_value, json_abi_param) in json_values.iter().zip(json_abi_params.iter()) { + if json_abi_param.is_complex_type() { + let json_value = json_value + .as_array() + .ok_or_else(|| "Expected array for complex type".to_string())?; + + let dyn_sol_value = Self::json_to_sol(json_value, &json_abi_param.components)?; + + parsed_params.push(DynSolValue::Tuple(dyn_sol_value)); + } else { + let sol_type: DynSolType = json_abi_param + .ty + .parse() + .map_err(|e| format!("Invalid Solidity type '{}': {}", json_abi_param.ty, e))?; + + let parsed_value: DynSolValue = sol_type + .coerce_json(json_value) + .map_err(|e| format!("Failed to parse parameter as DynSolValue: {}", e))?; + + if !parsed_value.matches(&sol_type) { + return Err(format!( + "Parameter type mismatch: expected {}, got {:?}", + json_abi_param.ty, + parsed_value.as_type() + )); + } + + parsed_params.push(parsed_value); + } + } + + Ok(parsed_params) + } + /// Encodes parameters using serde to directly deserialize into DynSolValue pub fn encode_parameters( &self, @@ -231,39 +280,9 @@ impl ContractCall { )); } - let mut parsed_params = Vec::new(); - - for (param, input) in self.params.iter().zip(function.inputs.iter()) { - let sol_type: DynSolType = input.ty.parse().map_err(|e| { - EngineError::contract_preparation_error( - Some(self.contract_address), - chain_id, - format!("Invalid Solidity type '{}': {}", input.ty, e), - ) - })?; - - let parsed_value: DynSolValue = sol_type.coerce_json(param).map_err(|e| { - EngineError::contract_preparation_error( - Some(self.contract_address), - chain_id, - format!("Failed to parse parameter as DynSolValue: {}", e), - ) - })?; - - if !parsed_value.matches(&sol_type) { - return Err(EngineError::contract_preparation_error( - Some(self.contract_address), - chain_id, - format!( - "Parameter type mismatch: expected {}, got {:?}", - input.ty, - parsed_value.as_type() - ), - )); - } - - parsed_params.push(parsed_value); - } + let parsed_params = Self::json_to_sol(&self.params, &function.inputs).map_err(|e| { + EngineError::contract_preparation_error(Some(self.contract_address), chain_id, e) + })?; function.abi_encode_input(&parsed_params).map_err(|e| { EngineError::contract_preparation_error( From d482033308c548a716b77202965e8a58584c8030 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:15:10 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`pb/?= =?UTF-8?q?tuple-abi-encoding`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @d4mr. * https://github.com/thirdweb-dev/engine-core/pull/24#issuecomment-3138814275 The following files were modified: * `server/src/http/dyn_contract.rs` --- server/src/http/dyn_contract.rs | 35 +++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/server/src/http/dyn_contract.rs b/server/src/http/dyn_contract.rs index a9c8da7..0719af5 100644 --- a/server/src/http/dyn_contract.rs +++ b/server/src/http/dyn_contract.rs @@ -195,7 +195,9 @@ impl ContractCall { Ok(abi) } - /// Extracts function name from method string + /// Extracts the function name from a method string, handling optional "function" prefix and parameter lists. + /// + /// Returns the function name as a string, or an error if the format is invalid. fn extract_function_name(&self, method: &str) -> Result { let trimmed = method.trim(); @@ -214,6 +216,30 @@ impl ContractCall { }) } + /// Converts a slice of JSON values into dynamic Solidity values according to ABI parameter definitions. + /// + /// Validates parameter count, recursively parses complex types (such as tuples), and coerces each JSON value into a `DynSolValue` matching the expected Solidity type. Returns an error if parameter counts do not match, if a value cannot be coerced, or if a type mismatch is detected. + /// + /// # Arguments + /// + /// * `json_values` - The JSON values representing function arguments. + /// * `json_abi_params` - The ABI parameter definitions describing expected Solidity types. + /// + /// # Returns + /// + /// A vector of `DynSolValue` instances corresponding to the parsed and validated parameters, or an error message if parsing fails. + /// + /// # Examples + /// + /// ``` + /// use serde_json::json; + /// use alloy_sol_types::{DynSolValue, Param}; + /// + /// let json_values = vec![json!("0x1234..."), json!(42)]; + /// let abi_params = vec![Param { name: "to".into(), ty: "address".into(), components: vec![] }, Param { name: "amount".into(), ty: "uint256".into(), components: vec![] }]; + /// let result = ContractCall::json_to_sol(&json_values, &abi_params); + /// assert!(result.is_ok()); + /// ``` fn json_to_sol( json_values: &[JsonValue], json_abi_params: &[Param], @@ -262,7 +288,12 @@ impl ContractCall { Ok(parsed_params) } - /// Encodes parameters using serde to directly deserialize into DynSolValue + /// Encodes the contract call parameters for a given function using the contract's JSON parameters and ABI. + /// + /// Validates parameter count, converts JSON values to Solidity types, and ABI-encodes the input data for transaction submission. + /// + /// # Returns + /// Encoded function call data as a byte vector on success, or an `EngineError` if parameter validation or encoding fails. pub fn encode_parameters( &self, function: &Function,