df_hyperliquid_core/client/evm/abi/
function.rs1use crate::client::evm::abi::kind::is_name;
5use crate::client::evm::abi::{AbiError, Param, Token, decode, encode, params, tuple_type};
6use crate::client::evm::u256::B256;
7use crate::utils::hex;
8use crate::utils::sign::keccak256;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Function {
14 pub name: String,
15 pub inputs: Vec<Param>,
16 pub outputs: Vec<Param>,
17}
18
19impl Function {
20 pub fn parse(text: &str) -> Result<Function, AbiError> {
21 let bad = || AbiError::Signature(text.trim().to_owned());
22 let (name, inside, rest) = signature_parts(text, "function").ok_or_else(bad)?;
23 let inputs = params(inside)?;
24 let outputs = match rest.find("returns") {
25 Some(at) => {
26 let after = rest[at + "returns".len()..].trim();
27 let inner = after.strip_prefix('(').and_then(|after| after.strip_suffix(')')).ok_or_else(bad)?;
28 params(inner)?
29 }
30 None => Vec::new(),
31 };
32 Ok(Function { name: name.to_owned(), inputs, outputs })
33 }
34
35 #[must_use]
37 pub fn signature(&self) -> String {
38 format!("{}{}", self.name, tuple_type(&self.inputs))
39 }
40
41 #[must_use]
43 pub fn selector(&self) -> [u8; 4] {
44 let hash = keccak256(self.signature().as_bytes());
45 [hash[0], hash[1], hash[2], hash[3]]
46 }
47
48 pub fn encode(&self, args: &[Token]) -> Result<Vec<u8>, AbiError> {
50 let mut out = self.selector().to_vec();
51 out.extend(encode(&self.inputs, args)?);
52 Ok(out)
53 }
54
55 pub fn decode_input(&self, calldata: &[u8]) -> Result<Vec<Token>, AbiError> {
57 let found = calldata.get(..4).ok_or(AbiError::Short(calldata.len()))?;
58 if found != self.selector() {
59 return Err(AbiError::Selector {
60 wants: hex::encode_prefixed(&self.selector()),
61 found: hex::encode_prefixed(found),
62 });
63 }
64 decode(&self.inputs, &calldata[4..])
65 }
66
67 pub fn decode_output(&self, answer: &[u8]) -> Result<Vec<Token>, AbiError> {
69 decode(&self.outputs, answer)
70 }
71}
72
73fn signature_parts<'a>(text: &'a str, keyword: &str) -> Option<(&'a str, &'a str, &'a str)> {
75 let text = text.trim();
76 let text = text.strip_prefix(keyword).filter(|rest| rest.starts_with(char::is_whitespace)).unwrap_or(text).trim();
77 let open = text.find('(')?;
78 let name = text[..open].trim();
79 if name.is_empty() || !is_name(name) {
80 return None;
81 }
82 let mut depth = 0i32;
83 for (at, letter) in text[open..].char_indices() {
84 match letter {
85 '(' => depth += 1,
86 ')' => {
87 depth -= 1;
88 if depth == 0 {
89 let close = open + at;
90 return Some((name, &text[open + 1..close], &text[close + 1..]));
91 }
92 }
93 _ => {}
94 }
95 }
96 None
97}
98
99pub fn selector(signature: &str) -> Result<[u8; 4], AbiError> {
101 Function::parse(signature).map(|function| function.selector())
102}
103
104pub fn event_topic(signature: &str) -> Result<B256, AbiError> {
106 let (name, inside, _) =
107 signature_parts(signature, "event").ok_or_else(|| AbiError::Signature(signature.trim().to_owned()))?;
108 let canonical = format!("{name}{}", tuple_type(¶ms(inside)?));
109 Ok(B256(keccak256(canonical.as_bytes())))
110}
111
112#[cfg(test)]
113#[path = "tests/function.rs"]
114mod tests;