use serde_json::Value;
use ethers::{
abi::{Token},
types::{Address, Bytes}
};
use std::str::FromStr;
use crate::error::{Result, InvalidInputsError};
use std::collections::HashMap;
use ethers_core::abi::HumanReadableParser;
pub fn encode_with_signature(signature: &str, params: &Vec<Token>) -> Result<Bytes> {
// Ensure the function signature starts with "function ".
let full_signature = if signature.trim_start().starts_with("function ") {
signature.trim().to_owned()
} else {
format!("function {}", signature.trim())
};
// Parse the human-readable function signature into a Function object.
let fun = HumanReadableParser::parse_function(&full_signature)
.map_err(|e| InvalidInputsError(e.to_string()))?;
// Encode the function call using the provided tokens.
let encoded = fun
.encode_input(params)
.map_err(|e| InvalidInputsError(e.to_string()))?;
Ok(encoded.into())
}
pub fn checksum_addresses_in_json(data: HashMap<String, Value>) -> Result<HashMap<String, Value>> {
let mut new_map = HashMap::new();
for (key, value) in data {
new_map.insert(key, process_value(value)?);
}
Ok(new_map)
}
// Helper function to process individual Values
fn process_value(data: Value) -> Result<Value> {
match data {
Value::Object(map) => {
// Handle dictionary/object
let mut new_map = serde_json::Map::new();
for (key, value) in map {
new_map.insert(key, process_value(value)?);
}
Ok(Value::Object(new_map))
}
Value::Array(arr) => {
// Handle array/list
let new_arr: Result<Vec<Value>> = arr
.into_iter()
.map(process_value)
.collect();
Ok(Value::Array(new_arr?))
}
Value::String(s) => {
// Handle potential Ethereum address
if let Ok(addr) = s.parse::<Address>() {
// Convert to checksum address (without chain_id)
Ok(Value::String(ethers::utils::to_checksum(&addr, None)))
} else {
// If not an address, return original string
Ok(Value::String(s))
}
}
// Return all other types unchanged
_ => Ok(data),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_encode_function_signature(){
// Prepare the parameters
let spender = Token::Address(Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap());
let amount = Token::Uint(ethers::types::U256::from(32));
let array = Token::Array(vec![Token::Address(Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap()), Token::Address(Address::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap())]);
let struct_token = Token::Tuple(vec![
Token::Uint(ethers::types::U256::from(32)),
array.clone()
]);
// Encode the function call data
let tokens = vec![spender, amount, array, struct_token];
let calldata = encode_with_signature("approve(address,uint256,address[],(uint256,address[]))", &tokens).unwrap();
println!("Encoded: 0x{}", hex::encode(&calldata));
assert!(hex::encode(&calldata) == "f46645f40000000000000000000000001234567890abcdef1234567890abcdef123456780000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001234567890abcdef1234567890abcdef123456780000000000000000000000001234567890abcdef1234567890abcdef123456780000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000001234567890abcdef1234567890abcdef123456780000000000000000000000001234567890abcdef1234567890abcdef12345678");
}
#[test]
fn test_checksum_addresses() {
let mut input = HashMap::new();
let mut inner_map = HashMap::new();
// Create nested structure using HashMap
inner_map.insert("address".to_string(), json!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"));
inner_map.insert("other".to_string(), json!("not an address"));
let mut nested_map = HashMap::new();
nested_map.insert("address".to_string(), json!("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"));
inner_map.insert("nested".to_string(), json!(nested_map));
inner_map.insert("array".to_string(),
json!(["0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "not an address"]));
input.insert("test_key".to_string(), Value::Object(inner_map.into_iter().collect()));
let result = checksum_addresses_in_json(input).unwrap();
// Verify the checksummed addresses
let result_value = &result["test_key"];
if let Value::Object(map) = result_value {
let addr = map["address"].as_str().unwrap();
assert!(addr.contains(char::is_uppercase), "Address should be checksummed");
assert_eq!(addr, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
// Verify nested address is also checksummed
if let Value::Object(nested) = &map["nested"] {
let nested_addr = nested["address"].as_str().unwrap();
assert!(nested_addr.contains(char::is_uppercase), "Nested address should be checksummed");
assert_eq!(nested_addr, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
}
// Verify array address is checksummed
if let Value::Array(arr) = &map["array"] {
let arr_addr = arr[0].as_str().unwrap();
assert!(arr_addr.contains(char::is_uppercase), "Array address should be checksummed");
assert_eq!(arr_addr, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
}
} else {
panic!("Expected object");
}
}
}