Skip to main content

df_hyperliquid_core/client/evm/
rlp.rs

1//! Recursive length prefix, the encoding a transaction is hashed and sent in,
2//! and the one a CREATE address is derived from. Only encoding: nothing here
3//! reads one back.
4
5use crate::client::evm::u256::U256;
6
7/// A string of bytes: a single byte under `0x80` is itself, anything else is
8/// prefixed with its length.
9#[must_use]
10pub fn bytes(payload: &[u8]) -> Vec<u8> {
11    if let [only] = payload
12        && *only < 0x80
13    {
14        return vec![*only];
15    }
16    let mut out = prefix(payload.len(), 0x80);
17    out.extend_from_slice(payload);
18    out
19}
20
21/// An integer, big-endian with no leading zeros, so zero is the empty string.
22#[must_use]
23pub fn uint(value: u128) -> Vec<u8> {
24    bytes(trimmed(&value.to_be_bytes()))
25}
26
27/// A 256-bit integer, as [`uint`] writes a narrower one.
28#[must_use]
29pub fn u256(value: U256) -> Vec<u8> {
30    bytes(&value.to_be_trimmed())
31}
32
33/// A 32-byte word read as an integer, as a signature's `r` and `s` are.
34#[must_use]
35pub fn word(value: &[u8; 32]) -> Vec<u8> {
36    bytes(trimmed(value))
37}
38
39/// A list of items that are each already encoded.
40#[must_use]
41pub fn list(items: &[Vec<u8>]) -> Vec<u8> {
42    let length = items.iter().map(Vec::len).sum();
43    let mut out = prefix(length, 0xc0);
44    for item in items {
45        out.extend_from_slice(item);
46    }
47    out
48}
49
50fn trimmed(big_endian: &[u8]) -> &[u8] {
51    let first = big_endian.iter().position(|byte| *byte != 0).unwrap_or(big_endian.len());
52    &big_endian[first..]
53}
54
55/// Short payloads carry their length in the tag; from 56 bytes on, the tag
56/// says how many bytes the length itself takes.
57fn prefix(length: usize, base: u8) -> Vec<u8> {
58    if let Ok(short) = u8::try_from(length)
59        && short < 56
60    {
61        return vec![base + short];
62    }
63    let written = trimmed(&(length as u64).to_be_bytes()).to_vec();
64    // At most eight bytes of length, so the tag stays under 0xc0 and 0x100.
65    let mut out = vec![base + 55 + written.len() as u8];
66    out.extend_from_slice(&written);
67    out
68}
69
70#[cfg(test)]
71#[path = "tests/rlp.rs"]
72mod tests;