Skip to main content

df_hyperliquid_core/client/evm/abi/
function.rs

1//! Functions and events by their signature: a [`Function`]'s selector, calldata
2//! and answer, and the topic an event is logged under.
3
4use 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/// A function as Solidity or viem writes it: `transfer(address to, uint256
11/// amount) returns (bool)`, with or without `function` and its modifiers.
12#[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    /// `name(t1,t2)`, what the selector hashes.
36    #[must_use]
37    pub fn signature(&self) -> String {
38        format!("{}{}", self.name, tuple_type(&self.inputs))
39    }
40
41    /// The four bytes that name the function: the start of `keccak256(signature)`.
42    #[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    /// The calldata of a call: the selector, then the arguments.
49    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    /// A call's arguments back out of its calldata.
56    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    /// What the call answered, read against its declared outputs.
68    pub fn decode_output(&self, answer: &[u8]) -> Result<Vec<Token>, AbiError> {
69        decode(&self.outputs, answer)
70    }
71}
72
73/// `name`, what is inside its parentheses, and what follows them.
74fn 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
99/// The selector of a function signature in any spelling the docs use.
100pub fn selector(signature: &str) -> Result<[u8; 4], AbiError> {
101    Function::parse(signature).map(|function| function.selector())
102}
103
104/// The first topic an event is logged under: `keccak256(name(t1,t2))`.
105pub 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(&params(inside)?));
109    Ok(B256(keccak256(canonical.as_bytes())))
110}
111
112#[cfg(test)]
113#[path = "tests/function.rs"]
114mod tests;