//! Utility functions for smart contract interactions. use ethers::abi::{Abi, Token, ParamType}; use ethers::types::{Address, U256}; use std::str::FromStr; use rhai::{Dynamic, Array}; /// Convert Rhai Dynamic values to ethers Token types pub fn convert_rhai_to_token(value: &Dynamic, expected_type: Option<&ParamType>) -> Result { match value { // Handle integers v if v.is_int() => { let i = v.as_int().unwrap(); if let Some(param_type) = expected_type { match param_type { ParamType::Uint(_) => Ok(Token::Uint(U256::from(i as u64))), ParamType::Int(_) => { // Convert to I256 - in a real implementation, we would handle this properly // For now, we'll just use U256 for both types Ok(Token::Uint(U256::from(i as u64))) }, _ => Err(format!("Expected {}, got integer", param_type)) } } else { // Default to Uint256 if no type info Ok(Token::Uint(U256::from(i as u64))) } }, // Handle strings and addresses v if v.is_string() => { let s = v.to_string(); if let Some(param_type) = expected_type { match param_type { ParamType::Address => { match Address::from_str(&s) { Ok(addr) => Ok(Token::Address(addr)), Err(e) => Err(format!("Invalid address format: {}", e)) } }, ParamType::String => Ok(Token::String(s)), ParamType::Bytes => { // Handle hex string conversion to bytes if s.starts_with("0x") { match ethers::utils::hex::decode(&s[2..]) { Ok(bytes) => Ok(Token::Bytes(bytes)), Err(e) => Err(format!("Invalid hex string: {}", e)) } } else { Ok(Token::Bytes(s.as_bytes().to_vec())) } }, _ => Err(format!("Expected {}, got string", param_type)) } } else { // Try to detect type from string format if s.starts_with("0x") && s.len() == 42 { // Likely an address match Address::from_str(&s) { Ok(addr) => Ok(Token::Address(addr)), Err(_) => Ok(Token::String(s)) } } else { Ok(Token::String(s)) } } }, // Handle booleans v if v.is_bool() => { let b = v.as_bool().unwrap(); if let Some(param_type) = expected_type { if matches!(param_type, ParamType::Bool) { Ok(Token::Bool(b)) } else { Err(format!("Expected {}, got boolean", param_type)) } } else { Ok(Token::Bool(b)) } }, // Handle arrays v if v.is_array() => { let arr = v.clone().into_array().unwrap(); if let Some(ParamType::Array(inner_type)) = expected_type { let mut tokens = Vec::new(); for item in arr.iter() { match convert_rhai_to_token(item, Some(inner_type)) { Ok(token) => tokens.push(token), Err(e) => return Err(e) } } Ok(Token::Array(tokens)) } else { Err("Array type mismatch or no type information available".to_string()) } }, // Handle other types or return error _ => Err(format!("Unsupported Rhai type: {:?}", value)) } } /// Validate and convert arguments based on function ABI pub fn prepare_function_arguments( abi: &Abi, function_name: &str, args: &Array ) -> Result, String> { // Get the function from the ABI let function = abi.function(function_name) .map_err(|e| format!("Function not found in ABI: {}", e))?; // Check if number of arguments matches if function.inputs.len() != args.len() { return Err(format!( "Wrong number of arguments for function '{}': expected {}, got {}", function_name, function.inputs.len(), args.len() )); } // Convert each argument according to the expected type let mut tokens = Vec::new(); for (i, (param, arg)) in function.inputs.iter().zip(args.iter()).enumerate() { match convert_rhai_to_token(arg, Some(¶m.kind)) { Ok(token) => tokens.push(token), Err(e) => return Err(format!("Error converting argument {}: {}", i, e)) } } Ok(tokens) } /// Convert ethers Token to Rhai Dynamic value pub fn convert_token_to_rhai(tokens: &[Token]) -> Dynamic { if tokens.is_empty() { return Dynamic::UNIT; } // If there's only one return value, return it directly if tokens.len() == 1 { return token_to_dynamic(&tokens[0]); } // If there are multiple return values, return them as an array let mut array = Array::new(); for token in tokens { array.push(token_to_dynamic(token)); } Dynamic::from(array) } /// Convert a single token to a Dynamic value pub fn token_to_dynamic(token: &Token) -> Dynamic { match token { Token::Address(addr) => Dynamic::from(format!("{:?}", addr)), Token::Bytes(bytes) => Dynamic::from(ethers::utils::hex::encode(bytes)), Token::Int(i) => Dynamic::from(i.to_string()), Token::Uint(u) => Dynamic::from(u.to_string()), Token::Bool(b) => Dynamic::from(*b), Token::String(s) => Dynamic::from(s.clone()), Token::Array(arr) => { let mut rhai_arr = Array::new(); for item in arr { rhai_arr.push(token_to_dynamic(item)); } Dynamic::from(rhai_arr) }, Token::Tuple(tuple) => { let mut rhai_arr = Array::new(); for item in tuple { rhai_arr.push(token_to_dynamic(item)); } Dynamic::from(rhai_arr) }, // Handle other token types _ => { log::warn!("Unsupported token type: {:?}", token); Dynamic::UNIT } } }