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() > 64 && !(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    let map = Option::<BTreeMap<Bytes, Bytes>>::deserialize(deserializer)?;
129    match map {
130        Some(map) => {
131            let mut res_map = BTreeMap::new();
132            for (k, v) in map {
133                let k_deserialized = from_bytes_to_b256::<'de, D>(k)?;
134                let v_deserialized = from_bytes_to_b256::<'de, D>(v)?;
135                res_map.insert(k_deserialized, v_deserialized);
136            }
137            Ok(Some(res_map))
138        }
139        None => Ok(None),
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use alloc::string::{String, ToString};
147    use serde_json::json;
148
149    #[test]
150    fn default_number_storage_key() {
151        let key = JsonStorageKey::Number(Default::default());
152        assert_eq!(key.to_string(), String::from("0x0"));
153    }
154
155    #[test]
156    fn default_hash_storage_key() {
157        let key = JsonStorageKey::default();
158        assert_eq!(
159            key.to_string(),
160            String::from("0x0000000000000000000000000000000000000000000000000000000000000000")
161        );
162    }
163
164    #[test]
165    fn test_storage_key() {
166        let cases = [
167            "0x0000000000000000000000000000000000000000000000000000000000000001", // Hash
168            "0000000000000000000000000000000000000000000000000000000000000001",   // Hash
169        ];
170
171        let key: JsonStorageKey = serde_json::from_str(&json!(cases[0]).to_string()).unwrap();
172        let key2: JsonStorageKey = serde_json::from_str(&json!(cases[1]).to_string()).unwrap();
173
174        assert_eq!(key.as_b256(), key2.as_b256());
175    }
176
177    #[test]
178    fn test_storage_key_serde_roundtrips() {
179        let test_cases = [
180            "0x0000000000000000000000000000000000000000000000000000000000000001", // Hash
181            "0x0000000000000000000000000000000000000000000000000000000000000abc", // Hash
182            "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",   // Number
183            "0xabc",                                                              // Number
184            "0xabcd",                                                             // Number
185        ];
186
187        for input in test_cases {
188            let key: JsonStorageKey = serde_json::from_str(&json!(input).to_string()).unwrap();
189            let output = key.to_string();
190
191            assert_eq!(
192                input, output,
193                "Storage key roundtrip failed to preserve the exact hex representation for {input}"
194            );
195        }
196    }
197
198    #[test]
199    fn test_as_b256() {
200        let cases = [
201            "0x0abc",                                                             // Number
202            "0x0000000000000000000000000000000000000000000000000000000000000abc", // Hash
203        ];
204
205        let num_key: JsonStorageKey = serde_json::from_str(&json!(cases[0]).to_string()).unwrap();
206        let hash_key: JsonStorageKey = serde_json::from_str(&json!(cases[1]).to_string()).unwrap();
207
208        assert_eq!(num_key, JsonStorageKey::Number(U256::from_str(cases[0]).unwrap()));
209        assert_eq!(hash_key, JsonStorageKey::Hash(B256::from_str(cases[1]).unwrap()));
210
211        assert_eq!(num_key.as_b256(), hash_key.as_b256());
212    }
213
214    #[test]
215    fn test_json_storage_key_from_b256() {
216        let b256_value = B256::from([1u8; 32]);
217        let key = JsonStorageKey::from(b256_value);
218        assert_eq!(key, JsonStorageKey::Hash(b256_value));
219        assert_eq!(
220            key.to_string(),
221            "0x0101010101010101010101010101010101010101010101010101010101010101"
222        );
223    }
224
225    #[test]
226    fn test_json_storage_key_from_u256() {
227        let u256_value = U256::from(42);
228        let key = JsonStorageKey::from(u256_value);
229        assert_eq!(key, JsonStorageKey::Number(u256_value));
230        assert_eq!(key.to_string(), "0x2a");
231    }
232
233    #[test]
234    fn test_json_storage_key_from_u8_array() {
235        let bytes = [0u8; 32];
236        let key = JsonStorageKey::from(bytes);
237        assert_eq!(key, JsonStorageKey::Hash(B256::from(bytes)));
238    }
239
240    #[test]
241    fn test_from_str_parsing() {
242        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";
243        let key = JsonStorageKey::from_str(hex_str).unwrap();
244        assert_eq!(key, JsonStorageKey::Hash(B256::from_str(hex_str).unwrap()));
245    }
246
247    #[test]
248    fn test_from_str_with_too_long_hex_string() {
249        let long_hex_str = "0x".to_string() + &"1".repeat(65);
250        let result = JsonStorageKey::from_str(&long_hex_str);
251
252        assert!(matches!(result, Err(ParseError::BaseConvertError(BaseConvertError::Overflow))));
253    }
254
255    #[test]
256    fn test_deserialize_storage_map_with_valid_data() {
257        let json_data = json!({
258            "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22",
259            "0x0000000000000000000000000000000000000000000000000000000000000002": "0x33"
260        });
261
262        // Specify the deserialization type explicitly
263        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
264            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
265        )
266        .unwrap();
267
268        assert_eq!(
269            deserialized.unwrap(),
270            BTreeMap::from([
271                (B256::from(U256::from(1u128)), B256::from(U256::from(0x22u128))),
272                (B256::from(U256::from(2u128)), B256::from(U256::from(0x33u128)))
273            ])
274        );
275    }
276
277    #[test]
278    fn test_deserialize_storage_map_with_empty_data() {
279        let json_data = json!({});
280        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
281            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
282        )
283        .unwrap();
284        assert!(deserialized.unwrap().is_empty());
285    }
286
287    #[test]
288    fn test_deserialize_storage_map_with_none() {
289        let json_data = json!(null);
290        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
291            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
292        )
293        .unwrap();
294        assert!(deserialized.is_none());
295    }
296
297    #[test]
298    fn test_from_bytes_to_b256_with_valid_input() {
299        // Test case with input less than 32 bytes, should be left-padded with zeros
300        let bytes = Bytes::from(vec![0x1, 0x2, 0x3, 0x4]);
301        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
302        let expected = B256::from_slice(&[
303            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,
304            2, 3, 4,
305        ]);
306        assert_eq!(result, expected);
307    }
308
309    #[test]
310    fn test_from_bytes_to_b256_with_exact_32_bytes() {
311        // Test case with input exactly 32 bytes long
312        let bytes = Bytes::from(vec![
313            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
314            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
315            0x20,
316        ]);
317        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
318        let expected = B256::from_slice(&[
319            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
320            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
321            0x20,
322        ]);
323        assert_eq!(result, expected);
324    }
325
326    #[test]
327    fn test_from_bytes_to_b256_with_input_too_long() {
328        // Test case with input longer than 32 bytes, should return an error
329        let bytes = Bytes::from(vec![0x1; 33]); // 33 bytes long
330        let result = from_bytes_to_b256::<serde_json::Value>(bytes);
331        assert!(result.is_err());
332        assert_eq!(result.unwrap_err().to_string(), "input too long to be a B256");
333    }
334
335    #[test]
336    fn test_from_bytes_to_b256_with_empty_input() {
337        // Test case with empty input, should be all zeros
338        let bytes = Bytes::from(vec![]);
339        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
340        let expected = B256::from_slice(&[0; 32]); // All zeros
341        assert_eq!(result, expected);
342    }
343}