sal-vault 0.1.0

SAL Vault - Cryptographic functionality including key management, digital signatures, symmetric encryption, Ethereum wallets, and encrypted key-value store
Documentation
//! Utility functions for smart contract interactions.

use ethers::abi::{Abi, ParamType, Token};
use ethers::types::{Address, U256};
use rhai::{Array, Dynamic};
use std::str::FromStr;

/// 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
        }
    }
}