use std::sync::Arc;
use alloy_dyn_abi::DynSolValue;
use alloy_primitives::{Address, B256, I256, U256};
use eyre::{bail, Result};
use revm::database::CacheDB;
use revm::{Database, DatabaseCommit, DatabaseRef};
use solang_parser::pt::{Expression, Identifier, Loc, Parameter, Type};
use crate::eval::handlers::debug::create_debug_handlers;
use crate::eval::handlers::edb::EdbHandler;
use crate::EngineContext;
use super::handlers::EvaluatorHandlers;
use super::utils::parse_input;
#[derive(Clone)]
pub struct ExpressionEvaluator {
handlers: EvaluatorHandlers,
}
impl ExpressionEvaluator {
pub fn new(handlers: EvaluatorHandlers) -> Self {
Self { handlers }
}
pub fn new_default() -> Self {
Self { handlers: EvaluatorHandlers::new() }
}
pub fn new_debug() -> Self {
let handlers = create_debug_handlers();
Self { handlers }
}
pub fn new_edb<DB>(context: Arc<EngineContext<DB>>) -> Self
where
DB: Database + DatabaseCommit + DatabaseRef + Clone + Send + Sync + 'static,
<CacheDB<DB> as Database>::Error: Clone + Send + Sync,
<DB as Database>::Error: Clone + Send + Sync,
{
let handlers = EdbHandler::create_handlers(context);
Self { handlers }
}
pub fn eval(&self, expr: &str, snapshot_id: usize) -> Result<DynSolValue> {
let parsed_expr =
parse_input(expr).map_err(|_| eyre::eyre!("Invalid expression \"{expr}\""))?;
let value = self.evaluate_expression(&parsed_expr, snapshot_id)?;
if let Some(validation_handler) = &self.handlers.validation_handler {
validation_handler.validate_value(value)
} else {
Ok(value)
}
}
fn evaluate_expression(&self, expr: &Expression, snapshot_id: usize) -> Result<DynSolValue> {
match expr {
Expression::NumberLiteral(_, value, _, ident) => {
if ident.is_none() {
self.evaluate_number_literal(value)
} else {
bail!("Invalid number literal")
}
}
Expression::HexNumberLiteral(_, value, ident) => {
if ident.is_none() {
self.evaluate_number_literal(value)
} else {
bail!("Invalid hex number literal")
}
}
Expression::StringLiteral(literals) => self.evaluate_string_literal(literals),
Expression::BoolLiteral(_, value) => Ok(DynSolValue::Bool(*value)),
Expression::AddressLiteral(_, addr) => self.evaluate_address_literal(addr),
Expression::Variable(ident) => self.evaluate_variable(ident, snapshot_id),
Expression::MemberAccess(_, base, member) => {
self.evaluate_member_access(base, member, snapshot_id)
}
Expression::ArraySubscript(_, array, index) => self.evaluate_array_or_mapping_access(
array,
index.as_ref().map(|v| &**v),
snapshot_id,
),
Expression::FunctionCall(_, func, args) => {
self.evaluate_function_call(func, args, snapshot_id)
}
Expression::Add(_, left, right) => {
self.evaluate_binary_arithmetic(left, right, snapshot_id, ArithmeticOp::Add)
}
Expression::Subtract(_, left, right) => {
self.evaluate_binary_arithmetic(left, right, snapshot_id, ArithmeticOp::Subtract)
}
Expression::Multiply(_, left, right) => {
self.evaluate_binary_arithmetic(left, right, snapshot_id, ArithmeticOp::Multiply)
}
Expression::Divide(_, left, right) => {
self.evaluate_binary_arithmetic(left, right, snapshot_id, ArithmeticOp::Divide)
}
Expression::Modulo(_, left, right) => {
self.evaluate_binary_arithmetic(left, right, snapshot_id, ArithmeticOp::Modulo)
}
Expression::Power(_, base, exp) => {
self.evaluate_binary_arithmetic(base, exp, snapshot_id, ArithmeticOp::Power)
}
Expression::BitwiseAnd(_, left, right) => {
self.evaluate_bitwise(left, right, snapshot_id, BitwiseOp::And)
}
Expression::BitwiseOr(_, left, right) => {
self.evaluate_bitwise(left, right, snapshot_id, BitwiseOp::Or)
}
Expression::BitwiseXor(_, left, right) => {
self.evaluate_bitwise(left, right, snapshot_id, BitwiseOp::Xor)
}
Expression::BitwiseNot(_, operand) => self.evaluate_bitwise_not(operand, snapshot_id),
Expression::ShiftLeft(_, left, right) => {
self.evaluate_bitwise(left, right, snapshot_id, BitwiseOp::ShiftLeft)
}
Expression::ShiftRight(_, left, right) => {
self.evaluate_bitwise(left, right, snapshot_id, BitwiseOp::ShiftRight)
}
Expression::Equal(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::Equal)
}
Expression::NotEqual(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::NotEqual)
}
Expression::Less(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::Less)
}
Expression::More(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::Greater)
}
Expression::LessEqual(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::LessEqual)
}
Expression::MoreEqual(_, left, right) => {
self.evaluate_comparison(left, right, snapshot_id, ComparisonOp::GreaterEqual)
}
Expression::And(_, left, right) => {
self.evaluate_logical(left, right, snapshot_id, LogicalOp::And)
}
Expression::Or(_, left, right) => {
self.evaluate_logical(left, right, snapshot_id, LogicalOp::Or)
}
Expression::Not(_, operand) => self.evaluate_logical_not(operand, snapshot_id),
Expression::UnaryPlus(_, operand) => self.evaluate_expression(operand, snapshot_id),
Expression::Negate(_, operand) => self.evaluate_unary_minus(operand, snapshot_id),
Expression::ConditionalOperator(_, condition, true_expr, false_expr) => {
self.evaluate_conditional(condition, true_expr, false_expr, snapshot_id)
}
Expression::Parenthesis(_, inner) => self.evaluate_expression(inner, snapshot_id),
Expression::ArraySlice(_, array, start, end) => {
self.evaluate_array_slice(array, start.as_deref(), end.as_deref(), snapshot_id)
}
Expression::HexLiteral(literals) => self.evaluate_hex_literal(literals),
Expression::ArrayLiteral(_, elements) => {
self.evaluate_array_literal(elements, snapshot_id)
}
Expression::List(_, parameters) => {
self.evaluate_list_parameters(parameters, snapshot_id)
}
Expression::New(..)
| Expression::Delete(..)
| Expression::PostDecrement(..)
| Expression::PostIncrement(..)
| Expression::PreDecrement(..)
| Expression::PreIncrement(..)
| Expression::Assign(..)
| Expression::AssignAdd(..)
| Expression::AssignSubtract(..)
| Expression::AssignMultiply(..)
| Expression::AssignDivide(..)
| Expression::AssignModulo(..)
| Expression::AssignShiftLeft(..)
| Expression::AssignShiftRight(..)
| Expression::AssignAnd(..)
| Expression::AssignOr(..)
| Expression::AssignXor(..)
| Expression::FunctionCallBlock(..)
| Expression::NamedFunctionCall(..)
| Expression::RationalNumberLiteral(..)
| Expression::Type(..) => bail!("Unsupported expression type: {:?}", expr),
}
}
fn evaluate_number_literal(&self, value: &str) -> Result<DynSolValue> {
let cleaned = value.replace('_', "");
let val = if cleaned.starts_with("0x") || cleaned.starts_with("0X") {
U256::from_str_radix(&cleaned[2..], 16)?
} else {
U256::from_str_radix(&cleaned, 10)?
};
Ok(DynSolValue::Uint(val, 256))
}
fn evaluate_string_literal(
&self,
literals: &[solang_parser::pt::StringLiteral],
) -> Result<DynSolValue> {
let mut result = String::new();
for lit in literals {
result.push_str(&lit.string);
}
Ok(DynSolValue::String(result))
}
fn evaluate_address_literal(&self, addr: &str) -> Result<DynSolValue> {
let address = addr.parse::<Address>()?;
Ok(DynSolValue::Address(address))
}
fn evaluate_variable(&self, ident: &Identifier, snapshot_id: usize) -> Result<DynSolValue> {
match ident.name.as_str() {
"msg" | "tx" | "block" => {
bail!("Cannot evaluate {} directly, use member access", ident.name)
}
name => {
self.get_variable_value(name, snapshot_id)
}
}
}
fn evaluate_member_access(
&self,
base: &Expression,
member: &Identifier,
snapshot_id: usize,
) -> Result<DynSolValue> {
if let Expression::Variable(base_ident) = base {
match (base_ident.name.as_str(), member.name.as_str()) {
("msg", "sender") => return self.get_msg_sender(snapshot_id),
("msg", "value") => return self.get_msg_value(snapshot_id),
("tx", "origin") => return self.get_tx_origin(snapshot_id),
("block", "number") => return self.get_block_number(snapshot_id),
("block", "timestamp") => return self.get_block_timestamp(snapshot_id),
_ => {}
}
}
let base_value = self.evaluate_expression(base, snapshot_id)?;
if let Some(builtin_result) = self.handle_builtin_property(&base_value, &member.name)? {
return Ok(builtin_result);
}
if let DynSolValue::CustomStruct { name, prop_names, tuple } = base_value {
if let Some((idx, _)) = prop_names.iter().enumerate().find(|(_, n)| *n == &member.name)
{
if let Some(value) = tuple.get(idx) {
return Ok(value.clone());
} else {
bail!("Struct {} has no member named {}", name, member.name);
}
} else {
bail!("Struct {} has no member named {}", name, member.name);
}
}
self.access_member(base_value, &member.name, snapshot_id)
}
fn evaluate_array_or_mapping_access(
&self,
base: &Expression,
index: Option<&Expression>,
snapshot_id: usize,
) -> Result<DynSolValue> {
let index = index.ok_or_else(|| eyre::eyre!("Array/mapping access requires an index"))?;
let (root, indices) = self.collect_access_chain(base, vec![index], snapshot_id)?;
self.get_mapping_or_array_value(root, indices, snapshot_id)
}
fn collect_access_chain<'a>(
&self,
expr: &'a Expression,
mut indices: Vec<&'a Expression>,
snapshot_id: usize,
) -> Result<(DynSolValue, Vec<DynSolValue>)> {
match expr {
Expression::ArraySubscript(_, base, Some(index)) => {
indices.insert(0, index);
self.collect_access_chain(base, indices, snapshot_id)
}
_ => {
let root = self.evaluate_expression(expr, snapshot_id)?;
let evaluated_indices = indices
.into_iter()
.map(|idx| self.evaluate_expression(idx, snapshot_id))
.collect::<Result<Vec<_>>>()?;
Ok((root, evaluated_indices))
}
}
}
fn evaluate_function_call(
&self,
func: &Expression,
args: &[Expression],
snapshot_id: usize,
) -> Result<DynSolValue> {
let arg_values = args
.iter()
.map(|arg| self.evaluate_expression(arg, snapshot_id))
.collect::<Result<Vec<_>>>()?;
let (func_name, callee) = match func {
Expression::Variable(ident) => (ident.name.clone(), None),
Expression::MemberAccess(_, base, member) => {
let callee = self.evaluate_expression(base, snapshot_id)?;
if let Some(result) =
self.handle_builtin_member_call(&member.name, &callee, &arg_values)?
{
return Ok(result);
}
(member.name.clone(), Some(callee))
}
Expression::Type(_, ty) if arg_values.len() == 1 => {
let arg = arg_values.into_iter().next().unwrap();
return self.evaluate_type_casting(ty, arg);
}
_ => bail!("Unsupported function call expression: {}", func),
};
self.call_function(&func_name, &arg_values, callee.as_ref(), snapshot_id)
}
fn handle_builtin_property(
&self,
value: &DynSolValue,
property_name: &str,
) -> Result<Option<DynSolValue>> {
match (property_name, value) {
("length", DynSolValue::String(s)) => {
Ok(Some(DynSolValue::Uint(U256::from(s.len()), 256)))
}
("length", DynSolValue::Bytes(b)) => {
Ok(Some(DynSolValue::Uint(U256::from(b.len()), 256)))
}
("length", DynSolValue::FixedBytes(_, size)) => {
Ok(Some(DynSolValue::Uint(U256::from(*size), 256)))
}
("length", DynSolValue::Tuple(arr)) => {
Ok(Some(DynSolValue::Uint(U256::from(arr.len()), 256)))
}
("balance", DynSolValue::Address(_)) => {
Ok(None)
}
("code", DynSolValue::Address(_)) => {
Ok(None)
}
("codehash", DynSolValue::Address(_)) => {
Ok(None)
}
("codesize", DynSolValue::Address(_)) => {
Ok(None)
}
("abs", DynSolValue::Int(val, bits)) => {
let abs_val = if val.is_negative() { val.wrapping_neg() } else { *val };
Ok(Some(DynSolValue::Uint(abs_val.into_raw(), *bits)))
}
("isZero", DynSolValue::Uint(val, _bits)) => Ok(Some(DynSolValue::Bool(val.is_zero()))),
("isZero", DynSolValue::Int(val, _bits)) => Ok(Some(DynSolValue::Bool(val.is_zero()))),
("isZero", DynSolValue::Address(addr)) => {
Ok(Some(DynSolValue::Bool(*addr == Address::ZERO)))
}
_ => Ok(None), }
}
fn handle_builtin_member_call(
&self,
member_name: &str,
callee: &DynSolValue,
args: &[DynSolValue],
) -> Result<Option<DynSolValue>> {
match (member_name, callee, args.len()) {
("push", DynSolValue::Tuple(arr), 1) => {
let new_length = arr.len() + 1;
Ok(Some(DynSolValue::Uint(U256::from(new_length), 256)))
}
("push", DynSolValue::Tuple(arr), 0) => {
let new_length = arr.len() + 1;
Ok(Some(DynSolValue::Uint(U256::from(new_length), 256)))
}
("pop", DynSolValue::Tuple(arr), 0) => {
if arr.is_empty() {
bail!("Cannot pop from empty array")
} else {
Ok(Some(arr.last().unwrap().clone()))
}
}
("concat", DynSolValue::String(s), 1) => {
if let DynSolValue::String(other) = &args[0] {
Ok(Some(DynSolValue::String(format!("{s}{other}"))))
} else {
Ok(None)
}
}
("slice", DynSolValue::String(s), 1) => {
if let DynSolValue::Uint(start, _) = &args[0] {
let start_idx = start.to::<usize>();
if start_idx <= s.len() {
Ok(Some(DynSolValue::String(s[start_idx..].to_string())))
} else {
Ok(Some(DynSolValue::String(String::new())))
}
} else {
Ok(None)
}
}
("slice", DynSolValue::String(s), 2) => {
if let (DynSolValue::Uint(start, _), DynSolValue::Uint(end, _)) =
(&args[0], &args[1])
{
let start_idx = start.to::<usize>();
let end_idx = end.to::<usize>();
if start_idx <= end_idx && end_idx <= s.len() {
Ok(Some(DynSolValue::String(s[start_idx..end_idx].to_string())))
} else {
Ok(Some(DynSolValue::String(String::new())))
}
} else {
Ok(None)
}
}
("concat", DynSolValue::Bytes(b1), 1) => {
if let DynSolValue::Bytes(b2) = &args[0] {
let mut result = b1.clone();
result.extend_from_slice(b2);
Ok(Some(DynSolValue::Bytes(result)))
} else {
Ok(None)
}
}
("slice", DynSolValue::Bytes(b), 1) => {
if let DynSolValue::Uint(start, _) = &args[0] {
let start_idx = start.to::<usize>();
if start_idx <= b.len() {
Ok(Some(DynSolValue::Bytes(b[start_idx..].to_vec())))
} else {
Ok(Some(DynSolValue::Bytes(Vec::new())))
}
} else {
Ok(None)
}
}
("slice", DynSolValue::Bytes(b), 2) => {
if let (DynSolValue::Uint(start, _), DynSolValue::Uint(end, _)) =
(&args[0], &args[1])
{
let start_idx = start.to::<usize>();
let end_idx = end.to::<usize>();
if start_idx <= end_idx && end_idx <= b.len() {
Ok(Some(DynSolValue::Bytes(b[start_idx..end_idx].to_vec())))
} else {
Ok(Some(DynSolValue::Bytes(Vec::new())))
}
} else {
Ok(None)
}
}
("min", DynSolValue::Uint(a, bits), 1) => {
if let DynSolValue::Uint(b, _) = &args[0] {
Ok(Some(DynSolValue::Uint((*a).min(*b), *bits)))
} else {
Ok(None)
}
}
("max", DynSolValue::Uint(a, bits), 1) => {
if let DynSolValue::Uint(b, _) = &args[0] {
Ok(Some(DynSolValue::Uint((*a).max(*b), *bits)))
} else {
Ok(None)
}
}
("isEmpty", DynSolValue::String(s), 0) => Ok(Some(DynSolValue::Bool(s.is_empty()))),
("isEmpty", DynSolValue::Bytes(b), 0) => Ok(Some(DynSolValue::Bool(b.is_empty()))),
("isEmpty", DynSolValue::Tuple(arr), 0) => Ok(Some(DynSolValue::Bool(arr.is_empty()))),
_ => Ok(None), }
}
fn evaluate_binary_arithmetic(
&self,
left: &Expression,
right: &Expression,
snapshot_id: usize,
op: ArithmeticOp,
) -> Result<DynSolValue> {
let left_val = self.evaluate_expression(left, snapshot_id)?;
let right_val = self.evaluate_expression(right, snapshot_id)?;
self.apply_arithmetic_op(left_val, right_val, op)
}
fn apply_arithmetic_op(
&self,
left: DynSolValue,
right: DynSolValue,
op: ArithmeticOp,
) -> Result<DynSolValue> {
match (left, right) {
(DynSolValue::Uint(l, bits1), DynSolValue::Uint(r, bits2)) => {
let bits = bits1.max(bits2);
let result = match op {
ArithmeticOp::Add => l.saturating_add(r),
ArithmeticOp::Subtract => l.saturating_sub(r),
ArithmeticOp::Multiply => l.saturating_mul(r),
ArithmeticOp::Divide => {
if r.is_zero() {
bail!("Division by zero");
}
l / r
}
ArithmeticOp::Modulo => {
if r.is_zero() {
bail!("Modulo by zero");
}
l % r
}
ArithmeticOp::Power => l.saturating_pow(r),
};
Ok(DynSolValue::Uint(result, bits))
}
(DynSolValue::Int(l, bits1), DynSolValue::Int(r, bits2)) => {
let bits = bits1.max(bits2);
let result = match op {
ArithmeticOp::Add => l.saturating_add(r),
ArithmeticOp::Subtract => l.saturating_sub(r),
ArithmeticOp::Multiply => l.saturating_mul(r),
ArithmeticOp::Divide => {
if r.is_zero() {
bail!("Division by zero");
}
l / r
}
ArithmeticOp::Modulo => {
if r.is_zero() {
bail!("Modulo by zero");
}
l % r
}
ArithmeticOp::Power => {
let base_u = U256::from_le_bytes(l.to_le_bytes::<32>());
let exp_u = U256::from_le_bytes(r.to_le_bytes::<32>());
let result_u = base_u.saturating_pow(exp_u);
I256::from_le_bytes(result_u.to_le_bytes::<32>())
}
};
Ok(DynSolValue::Int(result, bits))
}
_ => bail!("Cannot apply arithmetic operation to non-numeric types"),
}
}
fn evaluate_bitwise(
&self,
left: &Expression,
right: &Expression,
snapshot_id: usize,
op: BitwiseOp,
) -> Result<DynSolValue> {
let left_val = self.evaluate_expression(left, snapshot_id)?;
let right_val = self.evaluate_expression(right, snapshot_id)?;
self.apply_bitwise_op(left_val, right_val, op)
}
fn apply_bitwise_op(
&self,
left: DynSolValue,
right: DynSolValue,
op: BitwiseOp,
) -> Result<DynSolValue> {
match (left, right) {
(DynSolValue::Uint(l, bits1), DynSolValue::Uint(r, bits2)) => {
let bits = bits1.max(bits2);
let result = match op {
BitwiseOp::And => l & r,
BitwiseOp::Or => l | r,
BitwiseOp::Xor => l ^ r,
BitwiseOp::ShiftLeft => l << r,
BitwiseOp::ShiftRight => l >> r,
};
Ok(DynSolValue::Uint(result, bits))
}
_ => bail!("Bitwise operations require unsigned integer types"),
}
}
fn evaluate_bitwise_not(
&self,
operand: &Expression,
snapshot_id: usize,
) -> Result<DynSolValue> {
let val = self.evaluate_expression(operand, snapshot_id)?;
match val {
DynSolValue::Uint(v, bits) => Ok(DynSolValue::Uint(!v, bits)),
_ => bail!("Bitwise NOT requires unsigned integer type"),
}
}
fn evaluate_comparison(
&self,
left: &Expression,
right: &Expression,
snapshot_id: usize,
op: ComparisonOp,
) -> Result<DynSolValue> {
let left_val = self.evaluate_expression(left, snapshot_id)?;
let right_val = self.evaluate_expression(right, snapshot_id)?;
let result = self.apply_comparison_op(left_val, right_val, op)?;
Ok(DynSolValue::Bool(result))
}
fn apply_comparison_op(
&self,
left: DynSolValue,
right: DynSolValue,
op: ComparisonOp,
) -> Result<bool> {
match (left, right) {
(DynSolValue::Uint(l, _), DynSolValue::Uint(r, _)) => Ok(match op {
ComparisonOp::Equal => l == r,
ComparisonOp::NotEqual => l != r,
ComparisonOp::Less => l < r,
ComparisonOp::Greater => l > r,
ComparisonOp::LessEqual => l <= r,
ComparisonOp::GreaterEqual => l >= r,
}),
(DynSolValue::Int(l, _), DynSolValue::Int(r, _)) => Ok(match op {
ComparisonOp::Equal => l == r,
ComparisonOp::NotEqual => l != r,
ComparisonOp::Less => l < r,
ComparisonOp::Greater => l > r,
ComparisonOp::LessEqual => l <= r,
ComparisonOp::GreaterEqual => l >= r,
}),
(DynSolValue::Bool(l), DynSolValue::Bool(r)) => Ok(match op {
ComparisonOp::Equal => l == r,
ComparisonOp::NotEqual => l != r,
_ => bail!("Cannot compare booleans with <, >, <=, >="),
}),
(DynSolValue::Address(l), DynSolValue::Address(r)) => Ok(match op {
ComparisonOp::Equal => l == r,
ComparisonOp::NotEqual => l != r,
_ => bail!("Cannot compare addresses with <, >, <=, >="),
}),
(DynSolValue::String(l), DynSolValue::String(r)) => Ok(match op {
ComparisonOp::Equal => l == r,
ComparisonOp::NotEqual => l != r,
_ => bail!("Cannot compare strings with <, >, <=, >="),
}),
_ => bail!("Cannot compare values of different types"),
}
}
fn evaluate_logical(
&self,
left: &Expression,
right: &Expression,
snapshot_id: usize,
op: LogicalOp,
) -> Result<DynSolValue> {
let left_val = self.evaluate_expression(left, snapshot_id)?;
match op {
LogicalOp::And => {
if !self.to_bool(&left_val)? {
return Ok(DynSolValue::Bool(false));
}
let right_val = self.evaluate_expression(right, snapshot_id)?;
Ok(DynSolValue::Bool(self.to_bool(&right_val)?))
}
LogicalOp::Or => {
if self.to_bool(&left_val)? {
return Ok(DynSolValue::Bool(true));
}
let right_val = self.evaluate_expression(right, snapshot_id)?;
Ok(DynSolValue::Bool(self.to_bool(&right_val)?))
}
}
}
fn evaluate_logical_not(
&self,
operand: &Expression,
snapshot_id: usize,
) -> Result<DynSolValue> {
let val = self.evaluate_expression(operand, snapshot_id)?;
Ok(DynSolValue::Bool(!self.to_bool(&val)?))
}
fn to_bool(&self, val: &DynSolValue) -> Result<bool> {
match val {
DynSolValue::Bool(b) => Ok(*b),
DynSolValue::Uint(v, _) => Ok(!v.is_zero()),
DynSolValue::Int(v, _) => Ok(!v.is_zero()),
_ => bail!("Cannot convert {:?} to boolean", val),
}
}
fn evaluate_unary_minus(
&self,
operand: &Expression,
snapshot_id: usize,
) -> Result<DynSolValue> {
let val = self.evaluate_expression(operand, snapshot_id)?;
match val {
DynSolValue::Uint(v, bits) => {
let signed = I256::from_raw(v);
Ok(DynSolValue::Int(-signed, bits))
}
DynSolValue::Int(v, bits) => Ok(DynSolValue::Int(-v, bits)),
_ => bail!("Unary minus requires numeric type"),
}
}
fn evaluate_conditional(
&self,
condition: &Expression,
true_expr: &Expression,
false_expr: &Expression,
snapshot_id: usize,
) -> Result<DynSolValue> {
let cond_val = self.evaluate_expression(condition, snapshot_id)?;
if self.to_bool(&cond_val)? {
self.evaluate_expression(true_expr, snapshot_id)
} else {
self.evaluate_expression(false_expr, snapshot_id)
}
}
fn evaluate_array_slice(
&self,
array: &Expression,
start: Option<&Expression>,
end: Option<&Expression>,
snapshot_id: usize,
) -> Result<DynSolValue> {
let array_val = self.evaluate_expression(array, snapshot_id)?;
let elements = match array_val {
DynSolValue::Array(elements) => elements,
DynSolValue::FixedArray(elements) => elements,
_ => bail!("Array slice can only be applied to arrays, got {:?}", array_val),
};
let start_idx = if let Some(start_expr) = start {
let start_val = self.evaluate_expression(start_expr, snapshot_id)?;
match start_val {
DynSolValue::Uint(v, _) => v.to::<usize>(),
_ => bail!("Array slice start index must be an unsigned integer"),
}
} else {
0
};
let end_idx = if let Some(end_expr) = end {
let end_val = self.evaluate_expression(end_expr, snapshot_id)?;
match end_val {
DynSolValue::Uint(v, _) => v.to::<usize>(),
_ => bail!("Array slice end index must be an unsigned integer"),
}
} else {
elements.len()
};
if start_idx > end_idx {
bail!("Array slice start index {} is greater than end index {}", start_idx, end_idx);
}
if end_idx > elements.len() {
bail!("Array slice end index {} exceeds array length {}", end_idx, elements.len());
}
let slice = elements[start_idx..end_idx].to_vec();
Ok(DynSolValue::Array(slice))
}
fn evaluate_hex_literal(
&self,
literals: &[solang_parser::pt::HexLiteral],
) -> Result<DynSolValue> {
let mut bytes = Vec::new();
for lit in literals {
let hex_str = &lit.hex;
let cleaned = hex_str.replace(['_', ' '], "");
let hex = if cleaned.len() % 2 != 0 { format!("0{cleaned}") } else { cleaned };
for chunk in hex.as_bytes().chunks(2) {
let hex_byte = std::str::from_utf8(chunk)?;
let byte = u8::from_str_radix(hex_byte, 16)?;
bytes.push(byte);
}
}
Ok(DynSolValue::Bytes(bytes))
}
fn evaluate_array_literal(
&self,
elements: &[Expression],
snapshot_id: usize,
) -> Result<DynSolValue> {
let mut values = Vec::new();
for element in elements {
let val = self.evaluate_expression(element, snapshot_id)?;
values.push(val);
}
Ok(DynSolValue::Array(values))
}
fn evaluate_list_parameters(
&self,
parameters: &[(Loc, Option<Parameter>)],
snapshot_id: usize,
) -> Result<DynSolValue> {
let mut values = Vec::new();
for (_, param_opt) in parameters {
if let Some(param) = param_opt {
values.push(self.evaluate_expression(¶m.ty, snapshot_id)?);
} else {
bail!("Invalid none parameter in list");
}
}
Ok(DynSolValue::Tuple(values))
}
fn evaluate_type_casting(&self, target_type: &Type, value: DynSolValue) -> Result<DynSolValue> {
match target_type {
Type::Address => match value {
DynSolValue::Address(addr) => Ok(DynSolValue::Address(addr)),
DynSolValue::Uint(val, _) => {
let addr_bytes = val.to_be_bytes::<32>();
let addr = Address::from_slice(&addr_bytes[12..]);
Ok(DynSolValue::Address(addr))
}
DynSolValue::FixedBytes(bytes, 20) => {
let addr = Address::from_slice(&bytes[..20]);
Ok(DynSolValue::Address(addr))
}
_ => bail!("Cannot cast {:?} to address", value),
},
Type::Uint(bits) => {
let target_bits = *bits as usize;
let mask = if target_bits >= 256 {
U256::MAX
} else {
(U256::from(1) << target_bits) - U256::from(1)
};
match value {
DynSolValue::Uint(val, _) => {
let truncated = val & mask;
Ok(DynSolValue::Uint(truncated, target_bits))
}
DynSolValue::Int(val, _) => {
let unsigned = val.into_raw() & mask;
Ok(DynSolValue::Uint(unsigned, target_bits))
}
DynSolValue::Address(addr) => {
let val = U256::from_be_slice(addr.as_slice()) & mask;
Ok(DynSolValue::Uint(val, target_bits))
}
DynSolValue::Bool(b) => {
let val = if b { U256::from(1) } else { U256::ZERO };
Ok(DynSolValue::Uint(val, target_bits))
}
_ => bail!("Cannot cast {:?} to uint{}", value, target_bits),
}
}
Type::Int(bits) => {
let target_bits = *bits as usize;
let truncate_signed = |val: I256| -> I256 {
if target_bits >= 256 {
return val;
}
let mask = (U256::from(1) << target_bits) - U256::from(1);
let truncated = val.into_raw() & mask;
let sign_bit = U256::from(1) << (target_bits - 1);
if truncated & sign_bit != U256::ZERO {
let extension = U256::MAX ^ mask;
I256::from_raw(truncated | extension)
} else {
I256::from_raw(truncated)
}
};
match value {
DynSolValue::Int(val, _) => {
let truncated = truncate_signed(val);
Ok(DynSolValue::Int(truncated, target_bits))
}
DynSolValue::Uint(val, _) => {
let signed = I256::from_raw(val);
let truncated = truncate_signed(signed);
Ok(DynSolValue::Int(truncated, target_bits))
}
DynSolValue::Bool(b) => {
let val = if b { I256::from_raw(U256::from(1)) } else { I256::ZERO };
Ok(DynSolValue::Int(val, target_bits))
}
_ => bail!("Cannot cast {:?} to int{}", value, target_bits),
}
}
Type::Bool => match value {
DynSolValue::Bool(b) => Ok(DynSolValue::Bool(b)),
DynSolValue::Uint(val, _) => Ok(DynSolValue::Bool(!val.is_zero())),
DynSolValue::Int(val, _) => Ok(DynSolValue::Bool(!val.is_zero())),
_ => bail!("Cannot cast {:?} to bool", value),
},
Type::Bytes(size) => {
let target_size = *size as usize;
match value {
DynSolValue::Bytes(bytes) => {
let mut fixed_bytes = vec![0u8; 32];
let copy_len = bytes.len().min(target_size);
fixed_bytes[..copy_len].copy_from_slice(&bytes[..copy_len]);
Ok(DynSolValue::FixedBytes(B256::from_slice(&fixed_bytes), target_size))
}
DynSolValue::FixedBytes(bytes, _) => {
let mut fixed_bytes = vec![0u8; 32];
let copy_len = bytes.len().min(target_size).min(32);
fixed_bytes[..copy_len].copy_from_slice(&bytes[..copy_len]);
Ok(DynSolValue::FixedBytes(B256::from_slice(&fixed_bytes), target_size))
}
DynSolValue::String(s) => {
let bytes = s.as_bytes();
let mut fixed_bytes = vec![0u8; 32];
let copy_len = bytes.len().min(target_size);
fixed_bytes[..copy_len].copy_from_slice(&bytes[..copy_len]);
Ok(DynSolValue::FixedBytes(B256::from_slice(&fixed_bytes), target_size))
}
DynSolValue::Uint(val, _) => {
let mut fixed_bytes = vec![0u8; 32];
let be_bytes = val.to_be_bytes::<32>();
if target_size < 32 {
let src_start = 32 - target_size;
fixed_bytes[..target_size].copy_from_slice(&be_bytes[src_start..]);
} else {
fixed_bytes = be_bytes.to_vec();
}
Ok(DynSolValue::FixedBytes(B256::from_slice(&fixed_bytes), target_size))
}
_ => bail!("Cannot cast {:?} to bytes{}", value, target_size),
}
}
Type::DynamicBytes => match value {
DynSolValue::Bytes(bytes) => Ok(DynSolValue::Bytes(bytes)),
DynSolValue::FixedBytes(bytes, _) => Ok(DynSolValue::Bytes(bytes.to_vec())),
DynSolValue::String(s) => Ok(DynSolValue::Bytes(s.into_bytes())),
DynSolValue::Uint(val, _) => Ok(DynSolValue::Bytes(val.to_be_bytes_vec())),
_ => bail!("Cannot cast {:?} to bytes", value),
},
Type::String => match value {
DynSolValue::String(s) => Ok(DynSolValue::String(s)),
DynSolValue::Bytes(bytes) => {
let s =
String::from_utf8(bytes).map_err(|_| eyre::eyre!("Invalid UTF-8 bytes"))?;
Ok(DynSolValue::String(s))
}
DynSolValue::FixedBytes(bytes, _) => {
let s = String::from_utf8(bytes.to_vec())
.map_err(|_| eyre::eyre!("Invalid UTF-8 bytes"))?;
Ok(DynSolValue::String(s))
}
_ => bail!("Cannot cast {:?} to string", value),
},
_ => bail!("Type casting to {:?} is not yet supported", target_type),
}
}
fn get_variable_value(&self, name: &str, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.variable_handler {
Some(handler) => handler.get_variable_value(name, snapshot_id),
None => bail!("No variable handler configured"),
}
}
fn get_mapping_or_array_value(
&self,
root: DynSolValue,
indices: Vec<DynSolValue>,
snapshot_id: usize,
) -> Result<DynSolValue> {
match &self.handlers.mapping_array_handler {
Some(handler) => handler.get_mapping_or_array_value(root, indices, snapshot_id),
None => bail!("No mapping/array handler configured"),
}
}
fn call_function(
&self,
name: &str,
args: &[DynSolValue],
callee: Option<&DynSolValue>,
snapshot_id: usize,
) -> Result<DynSolValue> {
match &self.handlers.function_call_handler {
Some(handler) => handler.call_function(name, args, callee, snapshot_id),
None => bail!("No function call handler configured"),
}
}
fn access_member(
&self,
value: DynSolValue,
member: &str,
snapshot_id: usize,
) -> Result<DynSolValue> {
match &self.handlers.member_access_handler {
Some(handler) => handler.access_member(value, member, snapshot_id),
None => bail!("No member access handler configured"),
}
}
fn get_msg_sender(&self, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.msg_handler {
Some(handler) => handler.get_msg_sender(snapshot_id),
None => bail!("No msg handler configured"),
}
}
fn get_msg_value(&self, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.msg_handler {
Some(handler) => handler.get_msg_value(snapshot_id),
None => bail!("No msg handler configured"),
}
}
fn get_tx_origin(&self, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.tx_handler {
Some(handler) => handler.get_tx_origin(snapshot_id),
None => bail!("No tx handler configured"),
}
}
fn get_block_number(&self, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.block_handler {
Some(handler) => handler.get_block_number(snapshot_id),
None => bail!("No block handler configured"),
}
}
fn get_block_timestamp(&self, snapshot_id: usize) -> Result<DynSolValue> {
match &self.handlers.block_handler {
Some(handler) => handler.get_block_timestamp(snapshot_id),
None => bail!("No block handler configured"),
}
}
}
#[derive(Debug)]
enum ArithmeticOp {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Power,
}
#[derive(Debug)]
enum BitwiseOp {
And,
Or,
Xor,
ShiftLeft,
ShiftRight,
}
#[derive(Debug)]
enum ComparisonOp {
Equal,
NotEqual,
Less,
Greater,
LessEqual,
GreaterEqual,
}
#[derive(Debug)]
enum LogicalOp {
And,
Or,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eval::handlers::debug::{create_debug_handlers, create_simulation_debug_handlers};
use crate::eval::handlers::{MappingArrayHandler, MemberAccessHandler};
use alloy_primitives::{address, U256};
#[test]
fn test_evaluate_number_literal() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.evaluate_number_literal("0x1234");
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(0x1234));
assert_eq!(bits, 256);
}
let result = evaluator.evaluate_number_literal("42");
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(42));
assert_eq!(bits, 256);
}
let result = evaluator.evaluate_number_literal("1_000_000");
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1000000));
assert_eq!(bits, 256);
}
}
#[test]
fn test_apply_arithmetic_ops() {
let evaluator = ExpressionEvaluator::new_default();
let left = DynSolValue::Uint(U256::from(10), 256);
let right = DynSolValue::Uint(U256::from(20), 256);
let result = evaluator.apply_arithmetic_op(left, right, ArithmeticOp::Add);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(30));
}
let left = DynSolValue::Uint(U256::from(30), 256);
let right = DynSolValue::Uint(U256::from(10), 256);
let result = evaluator.apply_arithmetic_op(left, right, ArithmeticOp::Subtract);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(20));
}
let left = DynSolValue::Uint(U256::from(5), 256);
let right = DynSolValue::Uint(U256::from(6), 256);
let result = evaluator.apply_arithmetic_op(left, right, ArithmeticOp::Multiply);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(30));
}
let left = DynSolValue::Uint(U256::from(100), 256);
let right = DynSolValue::Uint(U256::from(5), 256);
let result = evaluator.apply_arithmetic_op(left, right, ArithmeticOp::Divide);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(20));
}
let left = DynSolValue::Uint(U256::from(100), 256);
let right = DynSolValue::Uint(U256::from(0), 256);
let result = evaluator.apply_arithmetic_op(left, right, ArithmeticOp::Divide);
assert!(result.is_err());
}
#[test]
fn test_comparison_ops() {
let evaluator = ExpressionEvaluator::new_default();
let left = DynSolValue::Uint(U256::from(10), 256);
let right = DynSolValue::Uint(U256::from(10), 256);
let result = evaluator.apply_comparison_op(left, right, ComparisonOp::Equal);
assert_eq!(result.unwrap(), true);
let left = DynSolValue::Uint(U256::from(10), 256);
let right = DynSolValue::Uint(U256::from(20), 256);
let result = evaluator.apply_comparison_op(left, right, ComparisonOp::NotEqual);
assert_eq!(result.unwrap(), true);
let left = DynSolValue::Uint(U256::from(10), 256);
let right = DynSolValue::Uint(U256::from(20), 256);
let result = evaluator.apply_comparison_op(left, right, ComparisonOp::Less);
assert_eq!(result.unwrap(), true);
let left = DynSolValue::Uint(U256::from(30), 256);
let right = DynSolValue::Uint(U256::from(20), 256);
let result = evaluator.apply_comparison_op(left, right, ComparisonOp::Greater);
assert_eq!(result.unwrap(), true);
}
#[test]
fn test_to_bool() {
let evaluator = ExpressionEvaluator::new_default();
assert_eq!(evaluator.to_bool(&DynSolValue::Bool(true)).unwrap(), true);
assert_eq!(evaluator.to_bool(&DynSolValue::Bool(false)).unwrap(), false);
assert_eq!(evaluator.to_bool(&DynSolValue::Uint(U256::from(1), 256)).unwrap(), true);
assert_eq!(evaluator.to_bool(&DynSolValue::Uint(U256::from(0), 256)).unwrap(), false);
assert_eq!(evaluator.to_bool(&DynSolValue::Uint(U256::from(100), 256)).unwrap(), true);
assert_eq!(
evaluator.to_bool(&DynSolValue::Int(I256::from_raw(U256::from(1)), 256)).unwrap(),
true
);
assert_eq!(
evaluator.to_bool(&DynSolValue::Int(I256::from_raw(U256::from(0)), 256)).unwrap(),
false
);
assert_eq!(
evaluator
.to_bool(&DynSolValue::Int(I256::from_raw(U256::from(1)).wrapping_neg(), 256))
.unwrap(),
true
);
}
#[test]
fn test_eval_number_literals() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(42));
assert_eq!(bits, 256);
}
let result = evaluator.eval("0x1234", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(0x1234));
assert_eq!(bits, 256);
}
let result = evaluator.eval("1_000_000", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1_000_000));
assert_eq!(bits, 256);
}
let result =
evaluator.eval("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::MAX);
assert_eq!(bits, 256);
}
}
#[test]
fn test_eval_string_literals() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("\"hello world\"", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(s)) = result {
assert_eq!(s, "hello world");
}
let result = evaluator.eval("\"\"", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(s)) = result {
assert_eq!(s, "");
}
}
#[test]
fn test_eval_bool_literals() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("true", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("false", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
}
#[test]
fn test_eval_arithmetic_expressions() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("10 + 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(30));
}
let result = evaluator.eval("50 - 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(30));
}
let result = evaluator.eval("6 * 7", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42));
}
let result = evaluator.eval("100 / 5", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(20));
}
let result = evaluator.eval("17 % 5", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(2));
}
let result = evaluator.eval("2 ** 8", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(256));
}
}
#[test]
fn test_eval_complex_arithmetic() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("2 + 3 * 4", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(14));
}
let result = evaluator.eval("(2 + 3) * 4", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(20));
}
let result = evaluator.eval("(10 + 5) * 2 - 8 / 4", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(28));
}
}
#[test]
fn test_eval_unary_operations() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("+42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42));
}
let result = evaluator.eval("-42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(val, _)) = result {
assert_eq!(val, I256::from_raw(U256::from(42)).wrapping_neg());
}
let result = evaluator.eval("!true", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("!42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("!0", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
}
#[test]
fn test_eval_comparison_operations() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("10 == 10", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("10 == 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("10 != 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("10 < 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("30 > 20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("10 <= 10", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("20 >= 10", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
}
#[test]
fn test_eval_logical_operations() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("true && true", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("true && false", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("true || false", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("false || false", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("0 && 42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("1 || 0", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
}
#[test]
fn test_eval_ternary_operator() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("true ? 42 : 99", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42));
}
let result = evaluator.eval("false ? 42 : 99", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(99));
}
let result = evaluator.eval("(10 > 5) ? \"yes\" : \"no\"", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(s)) = result {
assert_eq!(s, "yes");
}
let result = evaluator.eval("true ? (false ? 1 : 2) : 3", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(2));
}
}
#[test]
fn test_eval_error_cases() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("10 / 0", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Division by zero"));
let result = evaluator.eval("10 % 0", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Modulo by zero"));
let result = evaluator.eval("10 +", 0);
assert!(result.is_err());
let result = evaluator.eval("someVariable", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("No variable handler configured"));
let result = evaluator.eval("msg.sender", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("No msg handler configured"));
}
#[test]
fn test_eval_complex_expressions() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("(2 + 3) * 4 - 1", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(19)); }
let result = evaluator.eval("(5 > 3) && (2 == 2) || false", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("5 > 3 ? (2 + 2) : (3 * 3)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(4));
}
}
#[test]
fn test_eval_with_debug_handlers() {
let handlers = create_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("someVariable", 0);
assert!(result.is_err());
let error = result.unwrap_err().to_string();
assert!(error.contains("DebugHandler::get_variable_value"));
assert!(error.contains("name='someVariable'"));
assert!(error.contains("snapshot_id=0"));
let result = evaluator.eval("someFunction(1, 2)", 0);
assert!(result.is_err());
let error = result.unwrap_err().to_string();
assert!(error.contains("DebugHandler::call_function"));
assert!(error.contains("name='someFunction'"));
}
#[test]
fn test_eval_type_casting_comprehensive() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("address(0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::ZERO);
}
let result = evaluator.eval("address(0x1234567890123456789012345678901234567890)", 0);
assert!(result.is_ok());
let result = evaluator.eval("uint256(true)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1));
assert_eq!(bits, 256);
}
let result = evaluator.eval("uint256(false)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::ZERO);
assert_eq!(bits, 256);
}
let result = evaluator.eval("uint8(257)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1)); assert_eq!(bits, 8);
}
let result = evaluator.eval("uint16(65537)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1)); assert_eq!(bits, 16);
}
let result = evaluator.eval("uint32(0xFFFFFFFF)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(0xFFFFFFFF_u32));
assert_eq!(bits, 32);
}
let result = evaluator.eval("int256(true)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(val, bits)) = result {
assert_eq!(val, I256::from_raw(U256::from(1)));
assert_eq!(bits, 256);
}
let result = evaluator.eval("int8(127)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(val, bits)) = result {
assert_eq!(val, I256::from_raw(U256::from(127)));
assert_eq!(bits, 8);
}
let result = evaluator.eval("int8(128)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(_val, bits)) = result {
assert_eq!(bits, 8);
}
let result = evaluator.eval("bool(1)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("bool(0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false);
}
let result = evaluator.eval("bool(0xFFFFFFFFFFFFFFFF)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("bool(int256(1))", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("bool(uint8(257))", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("uint256(true) + uint256(false)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1)); }
let result = evaluator.eval("uint8(300) == uint8(44)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("true ? uint256(1) : uint256(0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1));
}
let result = evaluator.eval("uint256(uint8(uint16(65793)))", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, bits)) = result {
assert_eq!(val, U256::from(1));
assert_eq!(bits, 256);
}
let result = evaluator.eval("uint256(address(0x1234)) + 1", 0);
assert!(result.is_ok());
let result = evaluator.eval("bool(1) && bool(0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, false); }
let result = evaluator.eval("uint8(255) + 1", 0);
assert!(result.is_ok());
}
#[test]
fn test_eval_error_propagation() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("5 / 0", 0);
eprintln!("Error: {:?}", result);
assert!(result.unwrap_err().to_string().to_lowercase().contains("division by zero"));
let result = evaluator.eval("5 + true", 0);
assert!(result.is_err());
let result = evaluator.eval("unknownVar", 0);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.to_lowercase()
.contains("no variable handler configured"));
}
#[test]
fn test_eval_edge_cases() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval(
"115792089237316195423570985008687907853269984665640564039457584007913129639935",
0,
);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::MAX);
}
let result = evaluator.eval("-42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(val, _)) = result {
assert!(val < I256::ZERO);
}
let result = evaluator.eval("\"hello world\"", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(s)) = result {
assert_eq!(s, "hello world");
}
let result = evaluator.eval("0x1234567890123456789012345678901234567890", 0);
assert!(result.is_ok());
}
#[test]
fn test_eval_array_literals() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator_with_handlers = ExpressionEvaluator::new(handlers);
let result = evaluator_with_handlers.eval("arrayVar.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(10)); }
let result = evaluator_with_handlers.eval("someAddress.balance", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000)); }
let result = evaluator_with_handlers.eval("contractAddr.code", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bytes(_)) = result {
}
let log = debug_handler.get_log();
assert!(!log.is_empty());
assert!(log.iter().any(|entry| entry.contains("access_member")));
let member_accesses = log.iter().filter(|entry| entry.contains("access_member")).count();
assert!(member_accesses >= 3, "Should have logged at least 3 member accesses");
}
#[test]
fn test_eval_hex_literals() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("0x42", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(0x42));
}
let result = evaluator.eval("0x1234ABCD", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(0x1234ABCD));
}
let result = evaluator.eval("0xFF", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(255));
}
let result = evaluator.eval("0xDEADBEEF", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(0xDEADBEEFu32));
}
let result = evaluator.eval("0x10 + 0x20", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(0x30)); }
let result = evaluator.eval("address(0x0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::ZERO);
}
let result = evaluator.eval("address(0x1234567890123456789012345678901234567890)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(_)) = result {
} else {
panic!("Failed to cast hex literal to address");
}
}
#[test]
fn test_eval_list_tuples() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("multiReturnFunc()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Tuple(values)) = result {
assert!(!values.is_empty());
}
let result = evaluator.eval("tupleVar", 0);
assert!(result.is_ok());
let result = evaluator.eval("structVar.field1", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42)); }
let result = evaluator.eval("nestedStruct.inner.value", 0);
assert!(result.is_ok());
let result = evaluator.eval("pairData.first", 0);
assert!(result.is_ok());
let result = evaluator.eval("pairData.second", 0);
assert!(result.is_ok());
let log = debug_handler.get_log();
assert!(!log.is_empty());
let function_calls = log.iter().filter(|entry| entry.contains("call_function")).count();
let member_accesses = log.iter().filter(|entry| entry.contains("access_member")).count();
assert!(function_calls >= 1, "Should have logged at least 1 function call");
assert!(member_accesses >= 4, "Should have logged at least 4 member accesses");
}
#[test]
fn test_simulation_debug_handler_basic() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("balance", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000)); }
let result = evaluator.eval("msg.sender", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::from([0x42; 20])); }
let log = debug_handler.get_log();
assert!(!log.is_empty());
assert!(log.iter().any(|entry| entry.contains("get_variable_value")));
assert!(log.iter().any(|entry| entry.contains("get_msg_sender")));
}
#[test]
fn test_simulation_debug_handler_custom_values() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("myBalance", DynSolValue::Uint(U256::from(5000), 256));
debug_handler.set_variable("myAddress", DynSolValue::Address(Address::from([0x99; 20])));
let result = evaluator.eval("myBalance", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(5000));
}
let result = evaluator.eval("myAddress", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::from([0x99; 20]));
}
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("returning stored value")));
}
#[test]
fn test_simulation_debug_handler_complex_expressions() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("userBalance", DynSolValue::Uint(U256::from(2000), 256));
debug_handler.set_function("balanceOf", DynSolValue::Uint(U256::from(1500), 256));
debug_handler.clear_log();
let result = evaluator.eval("userBalance + balanceOf(msg.sender)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(3500)); }
let log = debug_handler.get_log();
println!("Execution log:");
for entry in &log {
println!(" {}", entry);
}
assert!(log.iter().any(|entry| entry.contains("get_variable_value: name='userBalance'")));
assert!(log.iter().any(|entry| entry.contains("call_function: name='balanceOf'")));
assert!(log.iter().any(|entry| entry.contains("get_msg_sender")));
}
#[test]
fn test_simulation_debug_handler_mapping_access() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let _evaluator = ExpressionEvaluator::new(handlers);
debug_handler.clear_log();
let mock_mapping = DynSolValue::Uint(U256::from(0), 256); let indices = vec![DynSolValue::Address(Address::from([0x11; 20]))];
let result = debug_handler.get_mapping_or_array_value(mock_mapping, indices, 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000)); }
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("get_mapping_or_array_value")));
}
#[test]
fn test_simulation_debug_handler_member_access() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let _evaluator = ExpressionEvaluator::new(handlers);
let mock_object = DynSolValue::Address(Address::from([0x42; 20]));
let result = debug_handler.access_member(mock_object, "balance", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000)); }
let result = debug_handler.access_member(DynSolValue::Uint(U256::ZERO, 256), "length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(10)); }
}
#[test]
fn test_debug_handler_comparison() {
let error_handlers = create_debug_handlers();
let error_evaluator = ExpressionEvaluator::new(error_handlers);
let result = error_evaluator.eval("someVar", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("DebugHandler::get_variable_value"));
let (sim_handlers, _sim_debug) = create_simulation_debug_handlers();
let sim_evaluator = ExpressionEvaluator::new(sim_handlers);
let result = sim_evaluator.eval("someVar", 0);
assert!(result.is_ok()); if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42)); }
}
#[test]
fn test_eval_mixed_type_operations() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("(10 + 5) == 15", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("!(10 == 5)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
let result = evaluator.eval("(2 * 3 > 5) && (10 / 2 == 5)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(b)) = result {
assert_eq!(b, true);
}
}
#[test]
fn test_eval_type_casting() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("address(0x0)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, address!("0x0000000000000000000000000000000000000000"));
}
}
#[test]
fn test_eval_parentheses_precedence() {
let evaluator = ExpressionEvaluator::new_default();
let result = evaluator.eval("((2 + 3) * (4 - 1))", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(15)); }
let result = evaluator.eval("(((10)))", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(10));
}
let result = evaluator.eval("-(2 + 3)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Int(val, _)) = result {
assert_eq!(val, I256::from_raw(U256::from(5)).wrapping_neg());
}
}
#[test]
fn test_advanced_simulation_scenarios() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("token", DynSolValue::Address(Address::from([0xAB; 20])));
debug_handler.set_function("balanceOf", DynSolValue::Uint(U256::from(50000), 256));
debug_handler.set_function("totalSupply", DynSolValue::Uint(U256::from(1000000), 256));
let result = evaluator.eval("balanceOf()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(50000));
}
let result = evaluator.eval("(balanceOf() * 100) / totalSupply()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(5)); }
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("balanceOf")));
assert!(log.iter().any(|entry| entry.contains("totalSupply")));
}
#[test]
fn test_blockchain_context_simulation() {
let (handlers, _debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("msg.sender", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::from([0x42; 20]));
}
let result = evaluator.eval("msg.value", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000000000000000u64)); }
let result = evaluator.eval("block.number", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(18500000));
}
let result = evaluator.eval("block.timestamp", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1700000000));
}
let result = evaluator.eval("tx.origin", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Address(addr)) = result {
assert_eq!(addr, Address::from([0x11; 20]));
}
let result = evaluator.eval("block.timestamp + 3600", 0); assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1700003600));
}
}
#[test]
fn test_complex_mapping_and_array_operations() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("userIndex", DynSolValue::Uint(U256::from(5), 256));
let result = evaluator.eval("users[userIndex].balance", 0);
assert!(result.is_ok());
let result = evaluator.eval("balances[msg.sender]", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(1000000)); }
let result = evaluator.eval("tokenData[token].holders[owner].amount", 0);
assert!(result.is_ok());
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("get_mapping_or_array_value")));
assert!(log.iter().any(|entry| entry.contains("access_member")));
}
#[test]
fn test_advanced_function_call_scenarios() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_function(
"getReserves",
DynSolValue::Tuple(vec![
DynSolValue::Uint(U256::from(1000000), 256),
DynSolValue::Uint(U256::from(500000), 256),
DynSolValue::Uint(U256::from(1700000000), 256),
]),
);
let result = evaluator.eval("getReserves()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Tuple(values)) = result {
assert_eq!(values.len(), 3);
}
let result = evaluator.eval("name()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(name)) = result {
assert_eq!(name, "MockToken");
}
let result = evaluator.eval("symbol()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(symbol)) = result {
assert_eq!(symbol, "MTK");
}
let result = evaluator.eval("decimals()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(decimals, _)) = result {
assert_eq!(decimals, U256::from(18));
}
let result = evaluator.eval("approve()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(success)) = result {
assert!(success);
}
}
#[test]
fn test_cross_handler_interactions() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("(msg.value * balanceOf()) / totalSupply()", 0);
assert!(result.is_ok());
let result = evaluator.eval("msg.value > 0 && balanceOf() > 1000", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_valid)) = result {
assert!(is_valid);
}
let result = evaluator.eval("msg.sender == tx.origin", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_same)) = result {
assert!(!is_same); }
let result = evaluator.eval("block.number % 100", 0);
assert!(result.is_ok());
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("get_msg_")));
assert!(log.iter().any(|entry| entry.contains("get_tx_")));
assert!(log.iter().any(|entry| entry.contains("get_block_")));
assert!(log.iter().any(|entry| entry.contains("call_function")));
}
#[test]
fn test_error_handling_and_recovery() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let result = evaluator.eval("1 / 0", 0);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string().to_lowercase();
assert!(error_msg.contains("division") && error_msg.contains("zero"));
let result = evaluator.eval("true + 5", 0);
assert!(result.is_err());
let result = evaluator.eval("msg.sender", 0);
assert!(result.is_ok());
let log = debug_handler.get_log();
assert!(log.iter().any(|entry| entry.contains("get_msg_sender")));
}
#[test]
fn test_performance_and_logging_metrics() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.clear_log();
let result = evaluator
.eval("((balanceOf() * msg.value) / totalSupply()) > (block.timestamp % 1000)", 0);
assert!(result.is_ok());
let log = debug_handler.get_log();
assert!(!log.is_empty());
let function_calls = log.iter().filter(|entry| entry.contains("call_function")).count();
let msg_accesses = log.iter().filter(|entry| entry.contains("get_msg_")).count();
let block_accesses = log.iter().filter(|entry| entry.contains("get_block_")).count();
assert!(function_calls >= 2); assert!(msg_accesses >= 1); assert!(block_accesses >= 1);
debug_handler.clear_log();
let cleared_log = debug_handler.get_log();
assert!(cleared_log.is_empty());
}
#[test]
fn test_dynamic_value_simulation() {
let (handlers, _debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
let balance_result = evaluator.eval("balance", 0);
if let Ok(DynSolValue::Uint(val, _)) = balance_result {
assert_eq!(val, U256::from(1000000)); } else {
panic!("Expected balance to return Uint(1000000, 256)");
}
let amount_result = evaluator.eval("amount", 0);
if let Ok(DynSolValue::Uint(val, _)) = amount_result {
assert_eq!(val, U256::from(1000000)); } else {
panic!("Expected amount to return Uint(1000000, 256)");
}
let value_result = evaluator.eval("value", 0);
if let Ok(DynSolValue::Uint(val, _)) = value_result {
assert_eq!(val, U256::from(1000000)); } else {
panic!("Expected value to return Uint(1000000, 256)");
}
let address_result = evaluator.eval("myaddress", 0);
assert!(matches!(address_result.unwrap(), DynSolValue::Address(_)));
let owner_result = evaluator.eval("owner", 0);
assert!(matches!(owner_result.unwrap(), DynSolValue::Address(_)));
let sender_result = evaluator.eval("sender", 0);
assert!(matches!(sender_result.unwrap(), DynSolValue::Address(_)));
let count_result = evaluator.eval("count", 0);
if let Ok(DynSolValue::Uint(val, _)) = count_result {
assert_eq!(val, U256::from(5)); } else {
panic!("Expected count to return Uint(5, 256)");
}
let length_result = evaluator.eval("length", 0);
if let Ok(DynSolValue::Uint(val, _)) = length_result {
assert_eq!(val, U256::from(5)); } else {
panic!("Expected length to return Uint(5, 256)");
}
let index_result = evaluator.eval("index", 0);
if let Ok(DynSolValue::Uint(val, _)) = index_result {
assert_eq!(val, U256::from(5)); } else {
panic!("Expected index to return Uint(5, 256)");
}
let enabled_result = evaluator.eval("enabled", 0);
if let Ok(DynSolValue::Bool(val)) = enabled_result {
assert!(val); } else {
panic!("Expected enabled to return Bool(true)");
}
let active_result = evaluator.eval("active", 0);
if let Ok(DynSolValue::Bool(val)) = active_result {
assert!(val); } else {
panic!("Expected active to return Bool(true)");
}
let flag_result = evaluator.eval("flag", 0);
if let Ok(DynSolValue::Bool(val)) = flag_result {
assert!(val); } else {
panic!("Expected flag to return Bool(true)");
}
let name_result = evaluator.eval("name", 0);
if let Ok(DynSolValue::String(val)) = name_result {
assert_eq!(val, "Mock_name"); } else {
panic!("Expected name to return String");
}
let symbol_result = evaluator.eval("symbol", 0);
if let Ok(DynSolValue::String(val)) = symbol_result {
assert_eq!(val, "Mock_symbol"); } else {
panic!("Expected symbol to return String");
}
let uri_result = evaluator.eval("uri", 0);
if let Ok(DynSolValue::String(val)) = uri_result {
assert_eq!(val, "Mock_uri"); } else {
panic!("Expected uri to return String");
}
let fallback_result = evaluator.eval("randomVariable", 0);
if let Ok(DynSolValue::Uint(val, _)) = fallback_result {
assert_eq!(val, U256::from(42));
} else {
panic!("Expected fallback to return Uint(42, 256)");
}
}
#[test]
fn test_enhanced_builtin_properties() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("testString", DynSolValue::String("Hello".to_string()));
debug_handler.set_variable("testBytes", DynSolValue::Bytes(vec![1, 2, 3]));
debug_handler.set_variable("testAddress", DynSolValue::Address(Address::ZERO));
debug_handler.set_variable(
"testInt",
DynSolValue::Int(I256::from_raw(U256::from(42).wrapping_neg()), 256),
);
debug_handler.set_variable("zeroInt", DynSolValue::Int(I256::ZERO, 256));
debug_handler.set_variable("zeroUint", DynSolValue::Uint(U256::ZERO, 256));
debug_handler.set_variable("nonZeroUint", DynSolValue::Uint(U256::from(123), 256));
let result = evaluator.eval("testString.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(len, _)) = result {
assert_eq!(len, U256::from(5)); }
let result = evaluator.eval("testBytes.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(len, _)) = result {
assert_eq!(len, U256::from(3));
}
let result = evaluator.eval("testInt.abs", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(val, _)) = result {
assert_eq!(val, U256::from(42));
}
let result = evaluator.eval("zeroInt.isZero", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_zero)) = result {
assert!(is_zero);
}
let result = evaluator.eval("zeroUint.isZero", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_zero)) = result {
assert!(is_zero);
}
let result = evaluator.eval("testAddress.isZero", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_zero)) = result {
assert!(is_zero); }
let result = evaluator.eval("nonZeroUint.isZero", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_zero)) = result {
assert!(!is_zero);
}
}
#[test]
fn test_builtin_member_functions() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_variable("testString", DynSolValue::String("Hello World".to_string()));
debug_handler.set_variable("emptyString", DynSolValue::String("".to_string()));
debug_handler.set_variable("myBytes", DynSolValue::Bytes(vec![1, 2, 3, 4, 5]));
debug_handler.set_variable("emptyBytes", DynSolValue::Bytes(vec![]));
debug_handler.set_variable(
"myArray",
DynSolValue::Tuple(vec![
DynSolValue::Uint(U256::from(10), 256),
DynSolValue::Uint(U256::from(20), 256),
DynSolValue::Uint(U256::from(30), 256),
]),
);
debug_handler.set_variable("emptyArray", DynSolValue::Tuple(vec![]));
debug_handler.set_variable("testAddress", DynSolValue::Address(Address::from([0x42; 20])));
debug_handler.set_variable("numberA", DynSolValue::Uint(U256::from(100), 256));
debug_handler.set_variable("numberB", DynSolValue::Uint(U256::from(200), 256));
let result = evaluator.eval("testString.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(len, _)) = result {
assert_eq!(len, U256::from(11)); }
let result = evaluator.eval("myBytes.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(len, _)) = result {
assert_eq!(len, U256::from(5));
}
let result = evaluator.eval("myArray.length", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(len, _)) = result {
assert_eq!(len, U256::from(3));
}
let result = evaluator.eval("myArray.push()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(new_len, _)) = result {
assert_eq!(new_len, U256::from(4)); }
debug_handler.set_variable("newElement", DynSolValue::Uint(U256::from(40), 256));
let result = evaluator.eval("myArray.push(newElement)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(new_len, _)) = result {
assert_eq!(new_len, U256::from(4)); }
let result = evaluator.eval("myArray.pop()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(popped_val, _)) = result {
assert_eq!(popped_val, U256::from(30)); }
let result = evaluator.eval("emptyArray.pop()", 0);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Cannot pop from empty array"));
debug_handler.set_variable("suffix", DynSolValue::String(" Test".to_string()));
let result = evaluator.eval("testString.concat(suffix)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(concatenated)) = result {
assert_eq!(concatenated, "Hello World Test");
}
debug_handler.set_variable("startIndex", DynSolValue::Uint(U256::from(6), 256));
let result = evaluator.eval("testString.slice(startIndex)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(sliced)) = result {
assert_eq!(sliced, "World");
}
debug_handler.set_variable("endIndex", DynSolValue::Uint(U256::from(5), 256));
let result = evaluator.eval("testString.slice(0, endIndex)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::String(sliced)) = result {
assert_eq!(sliced, "Hello");
}
debug_handler.set_variable("moreBytes", DynSolValue::Bytes(vec![6, 7, 8]));
let result = evaluator.eval("myBytes.concat(moreBytes)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bytes(concatenated)) = result {
assert_eq!(concatenated, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
let result = evaluator.eval("myBytes.slice(2)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bytes(sliced)) = result {
assert_eq!(sliced, vec![3, 4, 5]);
}
let result = evaluator.eval("myBytes.slice(1, 4)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bytes(sliced)) = result {
assert_eq!(sliced, vec![2, 3, 4]);
}
let result = evaluator.eval("numberA.min(numberB)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(min_val, _)) = result {
assert_eq!(min_val, U256::from(100));
}
let result = evaluator.eval("numberA.max(numberB)", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Uint(max_val, _)) = result {
assert_eq!(max_val, U256::from(200));
}
let result = evaluator.eval("testString.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(!is_empty);
}
let result = evaluator.eval("emptyString.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(is_empty);
}
let result = evaluator.eval("myBytes.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(!is_empty);
}
let result = evaluator.eval("emptyBytes.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(is_empty);
}
let result = evaluator.eval("myArray.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(!is_empty);
}
let result = evaluator.eval("emptyArray.isEmpty()", 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_empty)) = result {
assert!(is_empty);
}
}
#[test]
fn test_extremely_complex_expressions() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_function(
"getPoolData",
DynSolValue::Tuple(vec![
DynSolValue::Uint(U256::from(1000000), 256), DynSolValue::Uint(U256::from(2000000), 256), DynSolValue::Uint(U256::from(1700000000), 256), ]),
);
debug_handler.set_function("calculateFee", DynSolValue::Uint(U256::from(3000), 256)); debug_handler.set_function("getPrice", DynSolValue::Uint(U256::from(2000), 256)); debug_handler.set_function("getUserBalance", DynSolValue::Uint(U256::from(50000), 256));
debug_handler.set_function("getSlippageTolerance", DynSolValue::Uint(U256::from(100), 256));
let complex_expr1 = "((getUserBalance() * getPrice()) / 1000000) > ((calculateFee() * msg.value * getSlippageTolerance()) / (10000 * 100)) && (block.timestamp - 1700000000) < 3600 && msg.sender != tx.origin";
let result = evaluator.eval(complex_expr1, 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(is_valid)) = result {
println!("Complex DeFi condition result: {}", is_valid);
}
let complex_expr2 = "(((balanceOf() + getUserBalance()) * (getPrice() - calculateFee())) / totalSupply()) > (msg.value * ((block.number % 100) + 1)) ? ((getSlippageTolerance() * 2) + (block.timestamp % 1000)) : ((calculateFee() * 3) - (msg.value / 1000))";
let result = evaluator.eval(complex_expr2, 0);
assert!(result.is_ok());
println!("Complex ternary expression result: {:?}", result);
let complex_expr3 = "((balanceOf() * (block.number - 18500000)) + (getUserBalance() * getSlippageTolerance())) >= ((totalSupply() / 100) * ((msg.value > 1000000000000000000) ? (calculateFee() + 500) : (calculateFee() - 200))) && (block.timestamp > 1700000000) && ((tx.origin == msg.sender) || (getPrice() > 1800))";
let result = evaluator.eval(complex_expr3, 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(governance_valid)) = result {
println!("Complex governance voting condition: {}", governance_valid);
}
let log = debug_handler.get_log();
assert!(!log.is_empty());
let function_calls = log.iter().filter(|entry| entry.contains("call_function")).count();
assert!(function_calls >= 10, "Should have many function calls in complex expressions");
}
#[test]
fn test_ultra_complex_nested_expressions() {
let (handlers, debug_handler) = create_simulation_debug_handlers();
let evaluator = ExpressionEvaluator::new(handlers);
debug_handler.set_function(
"getLiquidityData",
DynSolValue::Tuple(vec![
DynSolValue::Uint(U256::from(5000000), 256),
DynSolValue::Uint(U256::from(3000000), 256),
DynSolValue::Uint(U256::from(8000000), 256),
]),
);
debug_handler.set_function("calculateRewards", DynSolValue::Uint(U256::from(12500), 256));
debug_handler.set_function("getMultiplier", DynSolValue::Uint(U256::from(150), 256)); debug_handler.set_function("getRiskFactor", DynSolValue::Uint(U256::from(80), 256)); debug_handler.set_function("getTimeDecay", DynSolValue::Uint(U256::from(95), 256));
let ultra_complex = r#"
(
(
(
(balanceOf() * getMultiplier() * getRiskFactor()) / (100 * 100)
) +
(
(calculateRewards() * getTimeDecay() * ((block.timestamp - 1700000000) / 86400)) / (100 * 365)
)
) *
(
(msg.value > (totalSupply() / 1000)) ?
(
((getPrice() + calculateFee()) * (block.number % 1000)) / 500
) :
(
((getPrice() - calculateFee()) * (block.timestamp % 10000)) / 2000
)
)
) >=
(
(
(getUserBalance() * (100 + getSlippageTolerance())) / 100
) +
(
((msg.sender == tx.origin) ? (calculateRewards() * 2) : (calculateRewards() / 2)) *
((block.number > 18500000) ? getMultiplier() : (getMultiplier() / 2))
) / 100
)
"#.replace('\n', "").replace(' ', "");
let result = evaluator.eval(&ultra_complex, 0);
assert!(result.is_ok());
if let Ok(DynSolValue::Bool(result_bool)) = result {
println!("Ultra complex yield farming condition: {}", result_bool);
}
let log = debug_handler.get_log();
let total_operations = log.len();
assert!(
total_operations >= 20,
"Ultra complex expression should generate many log entries, got: {}",
total_operations
);
println!("Total operations logged: {}", total_operations);
println!("Sample log entries: {:?}", log.iter().take(5).collect::<Vec<_>>());
}
}