use df_hyperliquid_core::client::evm::abi::{self, AbiError, Token};
use crate::utils::{Address, keccak256};
use crate::{B256, U256};
const WORD: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Method {
pub signature: &'static str,
pub returns: &'static str,
}
impl Method {
#[must_use]
pub const fn new(signature: &'static str, returns: &'static str) -> Method {
Method { signature, returns }
}
#[must_use]
pub fn selector(&self) -> [u8; 4] {
let hash = keccak256(self.signature.as_bytes());
[hash[0], hash[1], hash[2], hash[3]]
}
#[must_use]
pub fn calldata(&self, args: &[Arg<'_>]) -> Vec<u8> {
let heads = args.len() * WORD;
let mut head = Vec::with_capacity(4 + heads);
head.extend_from_slice(&self.selector());
let mut tail = Vec::new();
for arg in args {
match arg {
Arg::Address(address) => head.extend_from_slice(&B256::from_address(*address).0),
Arg::Uint(value) => head.extend_from_slice(&value.to_be_bytes()),
Arg::Word(word) => head.extend_from_slice(&word.0),
Arg::Bool(value) => head.extend_from_slice(&count(usize::from(*value))),
Arg::Bytes(bytes) => {
head.extend_from_slice(&count(heads + tail.len()));
tail.extend_from_slice(&count(bytes.len()));
tail.extend_from_slice(bytes);
tail.resize(tail.len() + bytes.len().next_multiple_of(WORD) - bytes.len(), 0);
}
Arg::Words(words) => {
head.extend_from_slice(&count(heads + tail.len()));
tail.extend_from_slice(&count(words.len()));
for word in *words {
tail.extend_from_slice(&word.0);
}
}
}
}
head.extend(tail);
head
}
pub fn answer(&self, data: &[u8]) -> Result<Vec<Token>, AbiError> {
abi::decode(&abi::params(self.returns)?, data)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arg<'a> {
Address(Address),
Uint(U256),
Word(B256),
Bool(bool),
Bytes(&'a [u8]),
Words(&'a [B256]),
}
impl Arg<'_> {
#[must_use]
pub const fn u64(value: u64) -> Arg<'static> {
Arg::Uint(U256::from_u64(value))
}
}
fn count(value: usize) -> [u8; WORD] {
U256::from_u64(value as u64).to_be_bytes()
}
#[cfg(test)]
#[path = "tests/method.rs"]
mod tests;