wallet_utils/
lib.rs

1use alloy_sol_types::{sol, SolType};
2use hex::decode;
3use serde::Deserialize;
4use std::collections::HashMap;
5use std::error::Error;
6use std::fs;
7
8#[derive(Deserialize)]
9struct FunctionInfo {
10    abi: Vec<String>,
11    r#type: String,
12}
13
14type FunctionSelectorMap = HashMap<String, FunctionInfo>;
15
16pub fn decode_calldata(data: &str) -> Result<(String, String, String), Box<dyn Error>> {
17    // Load the JSON file
18    let file_content = fs::read_to_string("./src/wallet-sig-map.json")?;
19    let selectors: FunctionSelectorMap = serde_json::from_str(&file_content)?;
20
21    // Convert the hex string to bytes
22    let data_bytes = decode(data)?;
23
24    // Extract the method selector (first 4 bytes)
25    let method_selector = &data_bytes[..4];
26    let selector_str = format!("0x{}", hex::encode(method_selector));
27
28    // Check if the method selector matches one of the known selectors
29    if let Some(function_info) = selectors.get(&selector_str) {
30        // Handle the "simple" type by default
31        if function_info.r#type == "simple"
32            && function_info.abi == vec!["address", "uint256", "bytes"]
33        {
34            // Define the ABI type structure
35            type MyTuple = sol!((address, uint256, bytes));
36
37            // Decode the ABI data
38            let param_bytes = &data_bytes[4..];
39            let decoded = MyTuple::abi_decode_params(param_bytes, false)?;
40
41            // Extract the decoded values
42            let target = decoded.0.to_string();
43            let value = decoded.1.to_string();
44            let calldata = decoded.2.to_string();
45
46            // Return the target (method selector), value, and calldata as strings
47            return Ok((target, value, calldata));
48        }
49        // Handle other types or more complex structures here if needed...
50    }
51
52    // If no match is found, return an error
53    Err("Unsupported method selector or ABI structure".into())
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_decode_abi() {
62        let calldata = "b61d27f6000000000000000000000000b382daf7230b73f71a766c96707cf9ec6316b360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000144e555c489000000000000000000000000efb80041d435d26d397bba4d4138b8232ea82d5400000000000000000000000081cc3c9c23ba6ce4dcf10b116079710f15336fd30000000000000000000000000000000000000000000035c900000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009581cc3c9c23ba6ce4dcf10b116079710f15336fd30000000000000000000000000000000000000000000000000000000066c97cdd0000000000000000000000000000000000000000000000000000000066c975d5d78c691a386ed33f24c0611b5021cd9a5231e763bc1127a66840231d97f599a7792bc42640ca19bf7c6f1d04a27095decf66a8dd13ea52f0c221e821965004981c000000000000000000000000000000000000000000000000000000000000000000000000000000";
63
64        let (target, address, bytes_data) = decode_calldata(calldata).unwrap();
65
66        assert_eq!(
67            target.to_ascii_lowercase(),
68            "0xb382daf7230b73f71a766c96707cf9ec6316b360"
69        );
70        assert_eq!(address, "0");
71        assert!(bytes_data.to_ascii_lowercase().contains("0xe555c489"));
72    }
73}