eth_encode_packed/
lib.rs

1pub use ethabi; // Re-export
2pub use hex; // Re-export
3
4use ethabi::ethereum_types::{Address, U256};
5
6pub struct TakeLastXBytes(pub usize);
7
8/// Represents a data type in solidity
9/// ```rust
10/// use eth_encode_packed::SolidityDataType;
11/// use eth_encode_packed::TakeLastXBytes;
12/// use eth_encode_packed::ethabi::ethereum_types::{U256, Address};
13/// // Uint24
14/// SolidityDataType::NumberWithShift(U256::from(3838), TakeLastXBytes(24));
15/// // String
16/// SolidityDataType::String("ipfs-cid-url-very-long");
17/// // Bool
18/// SolidityDataType::Bool(true);
19/// // Address
20/// use std::convert::TryInto;
21///
22/// let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
23/// let address: [u8; 20] = address.try_into().unwrap();
24/// SolidityDataType::Address(Address::from(address));
25/// ```
26pub enum SolidityDataType<'a> {
27    String(&'a str),
28    Address(Address),
29    Bytes(&'a [u8]),
30    Bool(bool),
31    Number(U256),
32    NumberWithShift(U256, TakeLastXBytes),
33}
34
35pub mod abi {
36
37    use crate::SolidityDataType;
38
39    /// Pack a single `SolidityDataType` into bytes
40    fn pack<'a>(data_type: &'a SolidityDataType) -> Vec<u8> {
41        let mut res = Vec::new();
42        match data_type {
43            SolidityDataType::String(s) => {
44                res.extend(s.as_bytes());
45            }
46            SolidityDataType::Address(a) => {
47                res.extend(a.0);
48            }
49            SolidityDataType::Number(n) => {
50                for b in n.0.iter().rev() {
51                    let bytes = b.to_be_bytes();
52                    res.extend(bytes);
53                }
54            }
55            SolidityDataType::Bytes(b) => {
56                res.extend(*b);
57            }
58            SolidityDataType::Bool(b) => {
59                if *b {
60                    res.push(1);
61                } else {
62                    res.push(0);
63                }
64            }
65            SolidityDataType::NumberWithShift(n, to_take) => {
66                let local_res = n.0.iter().rev().fold(vec![], |mut acc, i| {
67                    let bytes = i.to_be_bytes();
68                    acc.extend(bytes);
69                    acc
70                });
71
72                let to_skip = local_res.len() - (to_take.0 / 8);
73                let local_res = local_res.into_iter().skip(to_skip).collect::<Vec<u8>>();
74                res.extend(local_res);
75            }
76        };
77        return res;
78    }
79
80
81    /// ```rust
82    /// use eth_encode_packed::hex;
83    /// use eth_encode_packed::SolidityDataType;
84    /// use eth_encode_packed::TakeLastXBytes;
85    /// use eth_encode_packed::abi;
86    /// use eth_encode_packed::ethabi::ethereum_types::{Address, U256};
87    /// use std::convert::TryInto;
88    ///
89    /// let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
90    /// let address: [u8; 20] = address.try_into().unwrap();
91    /// let input = vec![
92    ///     SolidityDataType::NumberWithShift(U256::from(3838), TakeLastXBytes(24)),
93    ///     SolidityDataType::Number(U256::from(4001)),
94    ///     SolidityDataType::String("this-is-a-sample-string"),
95    ///     SolidityDataType::Address(Address::from(address)),
96    ///     SolidityDataType::Number(U256::from(1)),
97    /// ];
98    /// let (_bytes, hash) = abi::encode_packed(&input);
99    /// let hash = format!("0x{:}", hash);
100    /// let expected = "0x000efe0000000000000000000000000000000000000000000000000000000000000fa1746869732d69732d612d73616d706c652d737472696e67d8b934580fce35a11b58c6d73adee468a2833fa80000000000000000000000000000000000000000000000000000000000000001";
101    /// assert_eq!(hash, expected);
102    /// ```
103    pub fn encode_packed(items: &[SolidityDataType]) -> (Vec<u8>, String) {
104        let res = items.iter().fold(Vec::new(), |mut acc, i| {
105            let pack = pack(i);
106            acc.push(pack);
107            acc
108        });
109        let res = res.join(&[][..]);
110        let hexed = hex::encode(&res);
111        (res, hexed)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use std::convert::TryInto;
118
119    use super::*;
120
121    #[test]
122    fn test_normal_use_case() {
123        let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
124        let address: [u8; 20] = address.try_into().unwrap();
125        let input = vec![
126            SolidityDataType::NumberWithShift(U256::from(3838), TakeLastXBytes(24)),
127            SolidityDataType::Number(U256::from(4001)),
128            SolidityDataType::String("this-is-a-sample-string"),
129            SolidityDataType::Address(Address::from(address)),
130            SolidityDataType::Number(U256::from(1)),
131        ];
132        let (_bytes, hash) = abi::encode_packed(&input);
133        let hash = format!("0x{:}", hash);
134        let expected = "0x000efe0000000000000000000000000000000000000000000000000000000000000fa1746869732d69732d612d73616d706c652d737472696e67d8b934580fce35a11b58c6d73adee468a2833fa80000000000000000000000000000000000000000000000000000000000000001";
135        assert_eq!(hash, expected);
136    }
137
138    #[test]
139    fn test_uint24() {
140        let input = vec![SolidityDataType::NumberWithShift(
141            U256::from(4001),
142            TakeLastXBytes(24),
143        )];
144        let (_bytes, hash) = abi::encode_packed(&input);
145        let hash = format!("0x{:}", hash);
146        let expected = "0x000fa1";
147        assert_eq!(hash, expected);
148    }
149
150    #[test]
151    fn test_uint256() {
152        let input = vec![SolidityDataType::Number(U256::from(3838110))];
153        let (_bytes, hash) = abi::encode_packed(&input);
154        let hash = format!("0x{:}", hash);
155        let expected = "0x00000000000000000000000000000000000000000000000000000000003a909e";
156        assert_eq!(hash, expected);
157    }
158
159    #[test]
160    fn test_string() {
161        let input = vec![SolidityDataType::String("this-is-a-sample-string")];
162        let (_bytes, hash) = abi::encode_packed(&input);
163        let hash = format!("0x{:}", hash);
164        let expected = "0x746869732d69732d612d73616d706c652d737472696e67";
165        assert_eq!(hash, expected);
166    }
167
168    #[test]
169    fn test_address() {
170        let address = hex::decode("d8b934580fcE35a11B58C6D73aDeE468a2833fa8").unwrap();
171        let address: [u8; 20] = address.try_into().unwrap();
172        let input = vec![SolidityDataType::Address(Address::from(address))];
173        let (_bytes, hash) = abi::encode_packed(&input);
174        let hash = format!("0x{:}", hash);
175        let expected = "0xd8b934580fce35a11b58c6d73adee468a2833fa8";
176        assert_eq!(hash, expected);
177    }
178
179    #[test]
180    fn test_bool() {
181        let input = vec![SolidityDataType::Bool(false)];
182        let (_bytes, hash) = abi::encode_packed(&input);
183        let hash = format!("0x{:}", hash);
184        let expected = "0x00";
185        assert_eq!(hash, expected);
186    }
187    #[test]
188    fn test_normal_bytes() {
189        let bytes = "abababababababababababababababababababababababababababababab";
190        let bytes = hex::decode(bytes).unwrap();
191        let bytes: [u8; 30] = bytes.try_into().unwrap();
192
193        let input = vec![SolidityDataType::Bytes(&bytes)];
194        let (_bytes, hash) = abi::encode_packed(&input);
195        let hash = format!("0x{:}", hash);
196        let expected = "0xabababababababababababababababababababababababababababababab";
197        assert_eq!(hash, expected);
198    }
199}