184 lines
6.6 KiB
Rust
184 lines
6.6 KiB
Rust
//! 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<Token, String> {
|
|
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<Vec<Token>, 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
|
|
}
|
|
}
|
|
}
|