use crate::types::FunctionResponse;
use alloy::{
dyn_abi::{DynSolType, DynSolValue},
hex,
};
pub async fn fetch_function_name(input: &str) -> Option<(String, Vec<DynSolValue>)> {
if input.len() <= 2 {
return Some(("This is likely a native asset transfer".to_string(), vec![]));
}
let selector = &input[0..10];
let calldata = &input[10..];
let url = format!(
"https://api.openchain.xyz/signature-database/v1/lookup?function={}&filter=true",
selector
);
match reqwest::get(&url).await {
Ok(resp) => {
let text = resp.text().await.unwrap_or_default();
if let Ok(parsed) = serde_json::from_str::<FunctionResponse>(&text) {
if let Some(functions) = parsed.result.function.get(selector) {
let func = functions.get(0).map(|f| f.name.clone());
if let Some(sig) = func.clone() {
if !calldata.is_empty() {
if let Some(types) = extract_params(&sig) {
let param_type = DynSolType::Tuple(types);
let decoded = param_type
.abi_decode_params(&hex::decode(calldata).unwrap())
.unwrap();
if let DynSolValue::Tuple(values) = decoded {
return Some((sig, values));
}
}
}
return Some((sig, vec![]));
}
}
}
}
Err(_) => {}
}
if let Ok(bytes) = hex::decode(input.strip_prefix("0x").unwrap_or(input)) {
if let Ok(utf8_str) = std::str::from_utf8(&bytes) {
return Some((
"Likely a raw UTF-8 message (not a function)".to_string(),
vec![DynSolValue::String(utf8_str.to_string())],
));
}
}
None
}
pub fn extract_params(signature: &str) -> Option<Vec<DynSolType>> {
let open = signature.find('(')?;
let close = signature.rfind(')')?;
let params = &signature[open + 1..close];
if params.trim().is_empty() {
return Some(vec![]);
}
let mut types = Vec::new();
let mut current = String::new();
let mut paren_level = 0;
for ch in params.chars() {
match ch {
'(' => {
paren_level += 1;
current.push(ch);
}
')' => {
paren_level -= 1;
current.push(ch);
}
',' if paren_level == 0 => {
if let Ok(parsed) = DynSolType::parse(current.trim()) {
types.push(parsed);
} else {
return None;
}
current.clear();
}
_ => current.push(ch),
}
}
if !current.trim().is_empty() {
if let Ok(parsed) = DynSolType::parse(current.trim()) {
types.push(parsed);
} else {
return None;
}
}
Some(types)
}