alloy_encode_packed/
lib.rs

1use alloy::primitives::{Address, U256};
2
3pub struct TakeLastXBytes(pub usize);
4
5pub enum SolidityDataType<'a> {
6    String(&'a str),
7    Address(Address),
8    Bytes(&'a [u8]),
9    Bool(bool),
10    Number(U256),
11    NumberWithShift(U256, TakeLastXBytes),
12}
13
14pub mod abi {
15    use super::SolidityDataType;
16
17    /// Pack a single `SolidityDataType` into bytes
18    fn pack<'a>(data_type: &'a SolidityDataType) -> Vec<u8> {
19        let mut res = Vec::new();
20        match data_type {
21            SolidityDataType::String(s) => {
22                res.extend(s.as_bytes());
23            }
24            SolidityDataType::Address(a) => {
25                res.extend(a.0);
26            }
27            SolidityDataType::Number(n) => {
28                res.extend(n.to_be_bytes::<32>());
29            }
30            SolidityDataType::Bytes(b) => {
31                res.extend(*b);
32            }
33            SolidityDataType::Bool(b) => {
34                if *b {
35                    res.push(1);
36                } else {
37                    res.push(0);
38                }
39            }
40            SolidityDataType::NumberWithShift(n, to_take) => {
41                let local_res = n.to_be_bytes::<32>().to_vec();
42
43                let to_skip = local_res.len() - (to_take.0 / 8);
44                let local_res = local_res.into_iter().skip(to_skip).collect::<Vec<u8>>();
45                res.extend(local_res);
46            }
47        };
48        return res;
49    }
50
51    pub fn encode_packed(items: &[SolidityDataType]) -> (Vec<u8>, String) {
52        let res = items.iter().fold(Vec::new(), |mut acc, i| {
53            let pack = pack(i);
54            acc.push(pack);
55            acc
56        });
57        let res = res.join(&[][..]);
58        let hexed = hex::encode(&res);
59        (res, hexed)
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use std::convert::TryInto;
66
67    use super::*;
68
69    #[test]
70    fn test_normal_use_case() {
71        let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
72        let address: [u8; 20] = address.try_into().unwrap();
73        let input = vec![
74            SolidityDataType::NumberWithShift(U256::from(3838), TakeLastXBytes(24)),
75            SolidityDataType::Number(U256::from(4001)),
76            SolidityDataType::String("this-is-a-sample-string"),
77            SolidityDataType::Address(Address::from(address)),
78            SolidityDataType::Number(U256::from(1)),
79        ];
80        let (_bytes, hash) = abi::encode_packed(&input);
81        let hash = format!("0x{:}", hash);
82        let expected = "0x000efe0000000000000000000000000000000000000000000000000000000000000fa1746869732d69732d612d73616d706c652d737472696e67d8b934580fce35a11b58c6d73adee468a2833fa80000000000000000000000000000000000000000000000000000000000000001";
83        assert_eq!(hash, expected);
84    }
85
86    #[test]
87    fn test_uint24() {
88        let input = vec![SolidityDataType::NumberWithShift(
89            U256::from(4001),
90            TakeLastXBytes(24),
91        )];
92        let (_bytes, hash) = abi::encode_packed(&input);
93        let hash = format!("0x{:}", hash);
94        let expected = "0x000fa1";
95        assert_eq!(hash, expected);
96    }
97
98    #[test]
99    fn test_uint256() {
100        let input = vec![SolidityDataType::Number(U256::from(3838110))];
101        let (_bytes, hash) = abi::encode_packed(&input);
102        let hash = format!("0x{:}", hash);
103        let expected = "0x00000000000000000000000000000000000000000000000000000000003a909e";
104        assert_eq!(hash, expected);
105    }
106
107    #[test]
108    fn test_string() {
109        let input = vec![SolidityDataType::String("this-is-a-sample-string")];
110        let (_bytes, hash) = abi::encode_packed(&input);
111        let hash = format!("0x{:}", hash);
112        let expected = "0x746869732d69732d612d73616d706c652d737472696e67";
113        assert_eq!(hash, expected);
114    }
115
116    #[test]
117    fn test_address() {
118        let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
119        let address: [u8; 20] = address.try_into().unwrap();
120        let input = vec![SolidityDataType::Address(Address::from(address))];
121        let (_bytes, hash) = abi::encode_packed(&input);
122        let hash = format!("0x{:}", hash);
123        let expected = "0xd8b934580fce35a11b58c6d73adee468a2833fa8";
124        assert_eq!(hash, expected);
125    }
126
127    #[test]
128    fn test_bool() {
129        let input = vec![SolidityDataType::Bool(false)];
130        let (_bytes, hash) = abi::encode_packed(&input);
131        let hash = format!("0x{:}", hash);
132        let expected = "0x00";
133        assert_eq!(hash, expected);
134    }
135    #[test]
136    fn test_normal_bytes() {
137        let bytes = "abababababababababababababababababababababababababababababab";
138        let bytes = hex::decode(bytes).unwrap();
139        let bytes: [u8; 30] = bytes.try_into().unwrap();
140
141        let input = vec![SolidityDataType::Bytes(&bytes)];
142        let (_bytes, hash) = abi::encode_packed(&input);
143        let hash = format!("0x{:}", hash);
144        let expected = "0xabababababababababababababababababababababababababababababab";
145        assert_eq!(hash, expected);
146    }
147}