sal/src/vault/ethereum/contract_utils.rs
despiegk e47e163285
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
...
2025-05-12 06:14:16 +03:00

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(&param.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
}
}
}