Skip to main content

df_hyperliquid_core/client/evm/abi/
encode.rs

1//! `abi.encode`: each value checked against its type, then heads, then the
2//! tails they point into.
3
4use crate::client::evm::abi::{AbiError, Kind, Param, Token, WORD};
5use crate::client::evm::u256::{B256, U256};
6
7/// `abi.encode(values...)`: heads, then the tails they point into. Each value
8/// is checked against its type first.
9pub fn encode(params: &[Param], tokens: &[Token]) -> Result<Vec<u8>, AbiError> {
10    if params.len() != tokens.len() {
11        return Err(AbiError::Count { wants: params.len(), got: tokens.len() });
12    }
13    for (param, token) in params.iter().zip(tokens) {
14        if !fits(&param.kind, token) {
15            return Err(AbiError::Mismatch { kind: param.kind.canonical(), token: token.describe() });
16        }
17    }
18    let kinds: Vec<&Kind> = params.iter().map(|param| &param.kind).collect();
19    Ok(sequence(&kinds, tokens))
20}
21
22/// Whether `token` is a value of `kind`, its integers within their width.
23fn fits(kind: &Kind, token: &Token) -> bool {
24    match (kind, token) {
25        (Kind::Uint(bits), Token::Uint(value)) => value.bits() <= u32::from(*bits),
26        (Kind::Int(bits), Token::Int(value)) => sign_extended(&value.to_be_bytes(), *bits),
27        (Kind::Address, Token::Address(_))
28        | (Kind::Bool, Token::Bool(_))
29        | (Kind::String, Token::String(_))
30        | (Kind::Bytes, Token::Bytes(_)) => true,
31        (Kind::Fixed(size), Token::Fixed(bytes)) => bytes.len() == *size,
32        (Kind::Array(inner), Token::Array(items)) => items.iter().all(|item| fits(inner, item)),
33        (Kind::Sized(inner, count), Token::Array(items)) => {
34            items.len() == *count && items.iter().all(|item| fits(inner, item))
35        }
36        (Kind::Tuple(params), Token::Tuple(items)) => {
37            params.len() == items.len() && params.iter().zip(items).all(|(param, item)| fits(&param.kind, item))
38        }
39        _ => false,
40    }
41}
42
43/// Whether a two's complement word is the sign extension of its low `bits`.
44pub(super) fn sign_extended(word: &[u8; WORD], bits: u16) -> bool {
45    let spare = WORD - usize::from(bits) / 8;
46    if spare == 0 {
47        return true;
48    }
49    let fill = if word[spare] & 0x80 == 0 { 0x00 } else { 0xff };
50    word[..spare].iter().all(|byte| *byte == fill)
51}
52
53fn sequence(kinds: &[&Kind], tokens: &[Token]) -> Vec<u8> {
54    let heads: usize = kinds.iter().map(|kind| kind.head()).sum();
55    let mut head = Vec::with_capacity(heads);
56    let mut tail = Vec::new();
57    for (kind, token) in kinds.iter().zip(tokens) {
58        let encoded = one(kind, token);
59        match kind.is_dynamic() {
60            true => {
61                head.extend_from_slice(&word_of(heads + tail.len()));
62                tail.extend(encoded);
63            }
64            false => head.extend(encoded),
65        }
66    }
67    head.extend(tail);
68    head
69}
70
71/// One value checked by [`fits`]: its kind and token agree.
72fn one(kind: &Kind, token: &Token) -> Vec<u8> {
73    match (kind, token) {
74        (_, Token::Uint(value) | Token::Int(value)) => value.to_be_bytes().to_vec(),
75        (_, Token::Address(address)) => B256::from_address(*address).0.to_vec(),
76        (_, Token::Bool(value)) => word_of(usize::from(*value)).to_vec(),
77        (_, Token::String(text)) => packed(text.as_bytes()),
78        (_, Token::Bytes(bytes)) => packed(bytes),
79        (_, Token::Fixed(bytes)) => padded(bytes),
80        (Kind::Array(inner), Token::Array(items)) => {
81            let mut out = word_of(items.len()).to_vec();
82            out.extend(sequence(&vec![inner.as_ref(); items.len()], items));
83            out
84        }
85        (Kind::Sized(inner, _), Token::Array(items)) => sequence(&vec![inner.as_ref(); items.len()], items),
86        (Kind::Tuple(params), Token::Tuple(items)) => {
87            let kinds: Vec<&Kind> = params.iter().map(|param| &param.kind).collect();
88            sequence(&kinds, items)
89        }
90        (_, Token::Array(items) | Token::Tuple(items)) => sequence(&vec![kind; items.len()], items),
91    }
92}
93
94fn packed(bytes: &[u8]) -> Vec<u8> {
95    let mut out = word_of(bytes.len()).to_vec();
96    out.extend(padded(bytes));
97    out
98}
99
100fn padded(bytes: &[u8]) -> Vec<u8> {
101    let mut out = bytes.to_vec();
102    out.resize(bytes.len().div_ceil(WORD) * WORD, 0);
103    out
104}
105
106fn word_of(value: usize) -> [u8; WORD] {
107    U256::from_u64(value as u64).to_be_bytes()
108}
109
110#[cfg(test)]
111#[path = "tests/encode.rs"]
112mod tests;