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