Skip to main content

df_elysium_core/client/
method.rs

1//! A contract's function by its signature: the selector it is called by, the calldata of a call
2//! and how its answer reads. Every contract this crate calls (the precompiles, the bridge, the
3//! tokens) is a list of [`Method`]s, each checked against the reference implementations.
4
5use df_hyperliquid_core::client::evm::abi::{self, AbiError, Token};
6
7use crate::utils::{Address, keccak256};
8use crate::{B256, U256};
9
10const WORD: usize = 32;
11
12/// A function: its canonical `name(types)` and what it answers.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct Method {
15    /// `name(t1,t2)`, as the selector hashes it.
16    pub signature: &'static str,
17    /// What it answers, as a parameter list: `uint64 gasEstimate, uint64 gasEstimateForL1`.
18    pub returns: &'static str,
19}
20
21impl Method {
22    #[must_use]
23    pub const fn new(signature: &'static str, returns: &'static str) -> Method {
24        Method { signature, returns }
25    }
26
27    /// The four bytes that name it: the start of `keccak256(signature)`.
28    #[must_use]
29    pub fn selector(&self) -> [u8; 4] {
30        let hash = keccak256(self.signature.as_bytes());
31        [hash[0], hash[1], hash[2], hash[3]]
32    }
33
34    /// The calldata of a call: the selector, then the arguments, encoded as `abi.encode` lays them
35    /// out. The arguments are the function's, in order.
36    #[must_use]
37    pub fn calldata(&self, args: &[Arg<'_>]) -> Vec<u8> {
38        let heads = args.len() * WORD;
39        let mut head = Vec::with_capacity(4 + heads);
40        head.extend_from_slice(&self.selector());
41        let mut tail = Vec::new();
42        for arg in args {
43            match arg {
44                Arg::Address(address) => head.extend_from_slice(&B256::from_address(*address).0),
45                Arg::Uint(value) => head.extend_from_slice(&value.to_be_bytes()),
46                Arg::Word(word) => head.extend_from_slice(&word.0),
47                Arg::Bool(value) => head.extend_from_slice(&count(usize::from(*value))),
48                Arg::Bytes(bytes) => {
49                    head.extend_from_slice(&count(heads + tail.len()));
50                    tail.extend_from_slice(&count(bytes.len()));
51                    tail.extend_from_slice(bytes);
52                    tail.resize(tail.len() + bytes.len().next_multiple_of(WORD) - bytes.len(), 0);
53                }
54                Arg::Words(words) => {
55                    head.extend_from_slice(&count(heads + tail.len()));
56                    tail.extend_from_slice(&count(words.len()));
57                    for word in *words {
58                        tail.extend_from_slice(&word.0);
59                    }
60                }
61            }
62        }
63        head.extend(tail);
64        head
65    }
66
67    /// The answer read against [`Method::returns`].
68    pub fn answer(&self, data: &[u8]) -> Result<Vec<Token>, AbiError> {
69        abi::decode(&abi::params(self.returns)?, data)
70    }
71}
72
73/// One argument: a static word, or `bytes` and `bytes32[]`, the two dynamic kinds the calls here
74/// take.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum Arg<'a> {
77    Address(Address),
78    Uint(U256),
79    Word(B256),
80    Bool(bool),
81    Bytes(&'a [u8]),
82    Words(&'a [B256]),
83}
84
85impl Arg<'_> {
86    /// A `uint` of up to 64 bits.
87    #[must_use]
88    pub const fn u64(value: u64) -> Arg<'static> {
89        Arg::Uint(U256::from_u64(value))
90    }
91}
92
93fn count(value: usize) -> [u8; WORD] {
94    U256::from_u64(value as u64).to_be_bytes()
95}
96
97#[cfg(test)]
98#[path = "tests/method.rs"]
99mod tests;