use crate::types::NormalizedValue;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecodedCall {
pub function_name: String,
pub selector: Option<[u8; 4]>,
pub inputs: Vec<(String, NormalizedValue)>,
pub raw_data: Vec<u8>,
pub decode_errors: HashMap<String, String>,
}
impl DecodedCall {
pub fn selector_hex(&self) -> Option<String> {
self.selector
.map(|s| format!("0x{}", hex::encode(s)))
}
pub fn input(&self, name: &str) -> Option<&NormalizedValue> {
self.inputs
.iter()
.find(|(n, _)| n == name)
.map(|(_, v)| v)
}
pub fn is_clean(&self) -> bool {
self.decode_errors.is_empty()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecodedConstructor {
pub args: Vec<(String, NormalizedValue)>,
pub raw_data: Vec<u8>,
pub decode_errors: HashMap<String, String>,
}
impl DecodedConstructor {
pub fn arg(&self, name: &str) -> Option<&NormalizedValue> {
self.args
.iter()
.find(|(n, _)| n == name)
.map(|(_, v)| v)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HumanReadable {
pub summary: String,
pub description: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn selector_hex_format() {
let call = DecodedCall {
function_name: "transfer".into(),
selector: Some([0xa9, 0x05, 0x9c, 0xbb]),
inputs: vec![],
raw_data: vec![],
decode_errors: HashMap::new(),
};
assert_eq!(call.selector_hex(), Some("0xa9059cbb".to_string()));
}
#[test]
fn input_lookup() {
let call = DecodedCall {
function_name: "transfer".into(),
selector: None,
inputs: vec![
("to".into(), NormalizedValue::Address("0xabc".into())),
("amount".into(), NormalizedValue::Uint(1000)),
],
raw_data: vec![],
decode_errors: HashMap::new(),
};
assert!(call.input("to").is_some());
assert!(call.input("nonexistent").is_none());
}
}