Skip to main content

alloy_serde/
storage.rs

1use alloc::collections::BTreeMap;
2use alloy_primitives::{
3    ruint::{BaseConvertError, ParseError},
4    Bytes, B256, U256,
5};
6use core::{fmt, str::FromStr};
7use serde::{Deserialize, Deserializer, Serialize};
8
9/// A storage key that accepts hex strings up to 32 bytes for `eth_getStorageAt` and `eth_getProof`.
10///
11/// A full-width, 32-byte input is stored as [`Hash`](Self::Hash) and serializes at full width.
12/// Shorter inputs are stored as [`Number`](Self::Number) and serialize as canonical quantities, so
13/// their original prefix, letter case, and leading zeroes are not preserved. [`Self::as_b256`]
14/// returns either representation left-padded to 32 bytes.
15///
16/// This distinction mirrors geth's treatment of `eth_getProof` storage keys. See how input
17/// `storageKey`s populate the returned `StorageResult`:
18/// <https://github.com/ethereum/go-ethereum/blob/00a73fbcce3250b87fc4160f3deddc44390848f4/internal/ethapi/api.go#L658-L690>
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
20#[serde(untagged)]
21pub enum JsonStorageKey {
22    /// A full 32-byte key (tried first during deserialization)
23    Hash(B256),
24    /// A number (fallback if B256 deserialization fails)
25    Number(U256),
26}
27
28impl JsonStorageKey {
29    /// Returns the key as a [`B256`] value.
30    pub fn as_b256(&self) -> B256 {
31        match self {
32            Self::Hash(hash) => *hash,
33            Self::Number(num) => B256::from(*num),
34        }
35    }
36}
37
38impl Default for JsonStorageKey {
39    fn default() -> Self {
40        Self::Hash(Default::default())
41    }
42}
43
44impl From<B256> for JsonStorageKey {
45    fn from(value: B256) -> Self {
46        Self::Hash(value)
47    }
48}
49
50impl From<[u8; 32]> for JsonStorageKey {
51    fn from(value: [u8; 32]) -> Self {
52        B256::from(value).into()
53    }
54}
55
56impl From<U256> for JsonStorageKey {
57    fn from(value: U256) -> Self {
58        Self::Number(value)
59    }
60}
61
62impl FromStr for JsonStorageKey {
63    type Err = ParseError;
64
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        if s.len() > 65 && !(s.len() == 66 && s.starts_with("0x")) {
67            return Err(ParseError::BaseConvertError(BaseConvertError::Overflow));
68        }
69
70        if let Ok(hash) = B256::from_str(s) {
71            return Ok(Self::Hash(hash));
72        }
73        s.parse().map(Self::Number)
74    }
75}
76
77impl fmt::Display for JsonStorageKey {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Self::Hash(hash) => hash.fmt(f),
81            Self::Number(num) => write!(f, "{num:#x}"),
82        }
83    }
84}
85
86/// Converts a [`Bytes`] value of at most 32 bytes into a [`B256`], left-padding it with zeroes.
87pub fn from_bytes_to_b256<'de, D>(bytes: Bytes) -> Result<B256, D::Error>
88where
89    D: Deserializer<'de>,
90{
91    if bytes.0.len() > 32 {
92        return Err(serde::de::Error::custom("input too long to be a B256"));
93    }
94
95    // left pad with zeros to 32 bytes
96    let mut padded = [0u8; 32];
97    padded[32 - bytes.0.len()..].copy_from_slice(&bytes.0);
98
99    // then convert to B256 without a panic
100    Ok(B256::from_slice(&padded))
101}
102
103/// Deserializes an optional storage map, accepting keys and values shorter than 32 bytes and
104/// left-padding them with zeroes:
105///
106/// ```json
107/// {
108///     "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
109/// }
110/// ```
111pub fn deserialize_storage_map<'de, D>(
112    deserializer: D,
113) -> Result<Option<BTreeMap<B256, B256>>, D::Error>
114where
115    D: Deserializer<'de>,
116{
117    if deserializer.is_human_readable() {
118        let map = Option::<BTreeMap<Bytes, Bytes>>::deserialize(deserializer)?;
119        match map {
120            Some(map) => {
121                let mut res_map = BTreeMap::new();
122                for (k, v) in map {
123                    let k_deserialized = from_bytes_to_b256::<'de, D>(k)?;
124                    let v_deserialized = from_bytes_to_b256::<'de, D>(v)?;
125                    res_map.insert(k_deserialized, v_deserialized);
126                }
127                Ok(Some(res_map))
128            }
129            None => Ok(None),
130        }
131    } else {
132        Option::<BTreeMap<B256, B256>>::deserialize(deserializer)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use alloc::string::{String, ToString};
140    use serde_json::json;
141
142    #[test]
143    fn default_number_storage_key() {
144        let key = JsonStorageKey::Number(Default::default());
145        assert_eq!(key.to_string(), String::from("0x0"));
146    }
147
148    #[test]
149    fn default_hash_storage_key() {
150        let key = JsonStorageKey::default();
151        assert_eq!(
152            key.to_string(),
153            String::from("0x0000000000000000000000000000000000000000000000000000000000000000")
154        );
155    }
156
157    #[test]
158    fn test_storage_key() {
159        let cases = [
160            "0x0000000000000000000000000000000000000000000000000000000000000001", // Hash
161            "0000000000000000000000000000000000000000000000000000000000000001",   // Hash
162        ];
163
164        let key: JsonStorageKey = serde_json::from_str(&json!(cases[0]).to_string()).unwrap();
165        let key2: JsonStorageKey = serde_json::from_str(&json!(cases[1]).to_string()).unwrap();
166
167        assert_eq!(key.as_b256(), key2.as_b256());
168    }
169
170    #[test]
171    fn test_storage_key_serde_roundtrips() {
172        let test_cases = [
173            "0x0000000000000000000000000000000000000000000000000000000000000001", // Hash
174            "0x0000000000000000000000000000000000000000000000000000000000000abc", // Hash
175            "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",   // Number
176            "0xabc",                                                              // Number
177            "0xabcd",                                                             // Number
178            // 0x + 63 hex chars (U256 value, total len = 65)
179            "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // Number
180            // 0x + 64 hex chars (max U256, total len = 66)
181            "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // Hash
182        ];
183
184        for input in test_cases {
185            let key: JsonStorageKey = serde_json::from_str(&json!(input).to_string()).unwrap();
186            let output = key.to_string();
187
188            assert_eq!(
189                input, output,
190                "Storage key roundtrip failed to preserve the exact hex representation for {input}"
191            );
192        }
193    }
194
195    #[test]
196    fn test_as_b256() {
197        let cases = [
198            "0x0abc",                                                             // Number
199            "0x0000000000000000000000000000000000000000000000000000000000000abc", // Hash
200        ];
201
202        let num_key: JsonStorageKey = serde_json::from_str(&json!(cases[0]).to_string()).unwrap();
203        let hash_key: JsonStorageKey = serde_json::from_str(&json!(cases[1]).to_string()).unwrap();
204
205        assert_eq!(num_key, JsonStorageKey::Number(U256::from_str(cases[0]).unwrap()));
206        assert_eq!(hash_key, JsonStorageKey::Hash(B256::from_str(cases[1]).unwrap()));
207
208        assert_eq!(num_key.as_b256(), hash_key.as_b256());
209    }
210
211    #[test]
212    fn test_json_storage_key_from_b256() {
213        let b256_value = B256::from([1u8; 32]);
214        let key = JsonStorageKey::from(b256_value);
215        assert_eq!(key, JsonStorageKey::Hash(b256_value));
216        assert_eq!(
217            key.to_string(),
218            "0x0101010101010101010101010101010101010101010101010101010101010101"
219        );
220    }
221
222    #[test]
223    fn test_json_storage_key_from_u256() {
224        let u256_value = U256::from(42);
225        let key = JsonStorageKey::from(u256_value);
226        assert_eq!(key, JsonStorageKey::Number(u256_value));
227        assert_eq!(key.to_string(), "0x2a");
228    }
229
230    #[test]
231    fn test_json_storage_key_from_u8_array() {
232        let bytes = [0u8; 32];
233        let key = JsonStorageKey::from(bytes);
234        assert_eq!(key, JsonStorageKey::Hash(B256::from(bytes)));
235    }
236
237    #[test]
238    fn test_from_str_parsing() {
239        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";
240        let key = JsonStorageKey::from_str(hex_str).unwrap();
241        assert_eq!(key, JsonStorageKey::Hash(B256::from_str(hex_str).unwrap()));
242    }
243
244    #[test]
245    fn test_from_str_with_too_long_hex_string() {
246        let long_hex_str = "0x".to_string() + &"1".repeat(65);
247        let result = JsonStorageKey::from_str(&long_hex_str);
248
249        assert!(matches!(result, Err(ParseError::BaseConvertError(BaseConvertError::Overflow))));
250    }
251
252    #[test]
253    fn test_deserialize_too_long_storage_key() {
254        let key = "0x".to_string() + &"f".repeat(68);
255        let result: Result<JsonStorageKey, _> = serde_json::from_str(&json!(key).to_string());
256        assert!(result.is_err(), "storage key with 68 hex chars should fail deserialization");
257    }
258
259    #[test]
260    fn test_from_str_length_boundaries() {
261        // 0x + 63 hex chars = 65 total — valid U256
262        let key_63 = "0x".to_string() + &"f".repeat(63);
263        let result = JsonStorageKey::from_str(&key_63);
264        assert!(result.is_ok(), "0x + 63 hex chars should be a valid U256 storage key");
265        assert!(matches!(result.unwrap(), JsonStorageKey::Number(_)));
266
267        // 0x + 64 hex chars = 66 total — valid B256
268        let key_64 = "0x".to_string() + &"f".repeat(64);
269        let result = JsonStorageKey::from_str(&key_64);
270        assert!(result.is_ok(), "0x + 64 hex chars should be a valid B256 storage key");
271        assert!(matches!(result.unwrap(), JsonStorageKey::Hash(_)));
272
273        // 0x + 65 hex chars = 67 total — overflow
274        let key_65 = "0x".to_string() + &"f".repeat(65);
275        assert!(JsonStorageKey::from_str(&key_65).is_err());
276
277        // 64 bare hex chars — valid B256
278        let bare_64 = "f".repeat(64);
279        let result = JsonStorageKey::from_str(&bare_64);
280        assert!(result.is_ok(), "64 bare hex chars should be a valid B256 storage key");
281        assert!(matches!(result.unwrap(), JsonStorageKey::Hash(_)));
282
283        // 65 bare hex chars — overflow
284        let bare_65 = "f".repeat(65);
285        assert!(JsonStorageKey::from_str(&bare_65).is_err());
286    }
287
288    #[test]
289    fn test_deserialize_storage_map_with_valid_data() {
290        let json_data = json!({
291            "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22",
292            "0x0000000000000000000000000000000000000000000000000000000000000002": "0x33"
293        });
294
295        // Specify the deserialization type explicitly
296        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
297            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
298        )
299        .unwrap();
300
301        assert_eq!(
302            deserialized.unwrap(),
303            BTreeMap::from([
304                (B256::from(U256::from(1u128)), B256::from(U256::from(0x22u128))),
305                (B256::from(U256::from(2u128)), B256::from(U256::from(0x33u128)))
306            ])
307        );
308    }
309
310    #[test]
311    fn test_deserialize_storage_map_with_empty_data() {
312        let json_data = json!({});
313        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
314            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
315        )
316        .unwrap();
317        assert!(deserialized.unwrap().is_empty());
318    }
319
320    #[test]
321    fn test_deserialize_storage_map_with_none() {
322        let json_data = json!(null);
323        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
324            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
325        )
326        .unwrap();
327        assert!(deserialized.is_none());
328    }
329
330    #[test]
331    fn test_from_bytes_to_b256_with_valid_input() {
332        // Test case with input less than 32 bytes, should be left-padded with zeros
333        let bytes = Bytes::from(vec![0x1, 0x2, 0x3, 0x4]);
334        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
335        let expected = B256::from_slice(&[
336            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
337            2, 3, 4,
338        ]);
339        assert_eq!(result, expected);
340    }
341
342    #[test]
343    fn test_from_bytes_to_b256_with_exact_32_bytes() {
344        // Test case with input exactly 32 bytes long
345        let bytes = Bytes::from(vec![
346            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
347            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
348            0x20,
349        ]);
350        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
351        let expected = B256::from_slice(&[
352            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
353            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
354            0x20,
355        ]);
356        assert_eq!(result, expected);
357    }
358
359    #[test]
360    fn test_from_bytes_to_b256_with_input_too_long() {
361        // Test case with input longer than 32 bytes, should return an error
362        let bytes = Bytes::from(vec![0x1; 33]); // 33 bytes long
363        let result = from_bytes_to_b256::<serde_json::Value>(bytes);
364        assert!(result.is_err());
365        assert_eq!(result.unwrap_err().to_string(), "input too long to be a B256");
366    }
367
368    #[test]
369    fn test_from_bytes_to_b256_with_empty_input() {
370        // Test case with empty input, should be all zeros
371        let bytes = Bytes::from(vec![]);
372        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
373        let expected = B256::from_slice(&[0; 32]); // All zeros
374        assert_eq!(result, expected);
375    }
376}