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    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        ];
190
191        for input in test_cases {
192            let key: JsonStorageKey = serde_json::from_str(&json!(input).to_string()).unwrap();
193            let output = key.to_string();
194
195            assert_eq!(
196                input, output,
197                "Storage key roundtrip failed to preserve the exact hex representation for {input}"
198            );
199        }
200    }
201
202    #[test]
203    fn test_as_b256() {
204        let cases = [
205            "0x0abc",                                                             // Number
206            "0x0000000000000000000000000000000000000000000000000000000000000abc", // Hash
207        ];
208
209        let num_key: JsonStorageKey = serde_json::from_str(&json!(cases[0]).to_string()).unwrap();
210        let hash_key: JsonStorageKey = serde_json::from_str(&json!(cases[1]).to_string()).unwrap();
211
212        assert_eq!(num_key, JsonStorageKey::Number(U256::from_str(cases[0]).unwrap()));
213        assert_eq!(hash_key, JsonStorageKey::Hash(B256::from_str(cases[1]).unwrap()));
214
215        assert_eq!(num_key.as_b256(), hash_key.as_b256());
216    }
217
218    #[test]
219    fn test_json_storage_key_from_b256() {
220        let b256_value = B256::from([1u8; 32]);
221        let key = JsonStorageKey::from(b256_value);
222        assert_eq!(key, JsonStorageKey::Hash(b256_value));
223        assert_eq!(
224            key.to_string(),
225            "0x0101010101010101010101010101010101010101010101010101010101010101"
226        );
227    }
228
229    #[test]
230    fn test_json_storage_key_from_u256() {
231        let u256_value = U256::from(42);
232        let key = JsonStorageKey::from(u256_value);
233        assert_eq!(key, JsonStorageKey::Number(u256_value));
234        assert_eq!(key.to_string(), "0x2a");
235    }
236
237    #[test]
238    fn test_json_storage_key_from_u8_array() {
239        let bytes = [0u8; 32];
240        let key = JsonStorageKey::from(bytes);
241        assert_eq!(key, JsonStorageKey::Hash(B256::from(bytes)));
242    }
243
244    #[test]
245    fn test_from_str_parsing() {
246        let hex_str = "0x0101010101010101010101010101010101010101010101010101010101010101";
247        let key = JsonStorageKey::from_str(hex_str).unwrap();
248        assert_eq!(key, JsonStorageKey::Hash(B256::from_str(hex_str).unwrap()));
249    }
250
251    #[test]
252    fn test_from_str_with_too_long_hex_string() {
253        let long_hex_str = "0x".to_string() + &"1".repeat(65);
254        let result = JsonStorageKey::from_str(&long_hex_str);
255
256        assert!(matches!(result, Err(ParseError::BaseConvertError(BaseConvertError::Overflow))));
257    }
258
259    #[test]
260    fn test_deserialize_storage_map_with_valid_data() {
261        let json_data = json!({
262            "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22",
263            "0x0000000000000000000000000000000000000000000000000000000000000002": "0x33"
264        });
265
266        // Specify the deserialization type explicitly
267        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
268            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
269        )
270        .unwrap();
271
272        assert_eq!(
273            deserialized.unwrap(),
274            BTreeMap::from([
275                (B256::from(U256::from(1u128)), B256::from(U256::from(0x22u128))),
276                (B256::from(U256::from(2u128)), B256::from(U256::from(0x33u128)))
277            ])
278        );
279    }
280
281    #[test]
282    fn test_deserialize_storage_map_with_empty_data() {
283        let json_data = json!({});
284        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
285            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
286        )
287        .unwrap();
288        assert!(deserialized.unwrap().is_empty());
289    }
290
291    #[test]
292    fn test_deserialize_storage_map_with_none() {
293        let json_data = json!(null);
294        let deserialized: Option<BTreeMap<B256, B256>> = deserialize_storage_map(
295            &serde_json::from_value::<serde_json::Value>(json_data).unwrap(),
296        )
297        .unwrap();
298        assert!(deserialized.is_none());
299    }
300
301    #[test]
302    fn test_from_bytes_to_b256_with_valid_input() {
303        // Test case with input less than 32 bytes, should be left-padded with zeros
304        let bytes = Bytes::from(vec![0x1, 0x2, 0x3, 0x4]);
305        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
306        let expected = B256::from_slice(&[
307            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,
308            2, 3, 4,
309        ]);
310        assert_eq!(result, expected);
311    }
312
313    #[test]
314    fn test_from_bytes_to_b256_with_exact_32_bytes() {
315        // Test case with input exactly 32 bytes long
316        let bytes = Bytes::from(vec![
317            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
318            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
319            0x20,
320        ]);
321        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
322        let expected = B256::from_slice(&[
323            0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11,
324            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
325            0x20,
326        ]);
327        assert_eq!(result, expected);
328    }
329
330    #[test]
331    fn test_from_bytes_to_b256_with_input_too_long() {
332        // Test case with input longer than 32 bytes, should return an error
333        let bytes = Bytes::from(vec![0x1; 33]); // 33 bytes long
334        let result = from_bytes_to_b256::<serde_json::Value>(bytes);
335        assert!(result.is_err());
336        assert_eq!(result.unwrap_err().to_string(), "input too long to be a B256");
337    }
338
339    #[test]
340    fn test_from_bytes_to_b256_with_empty_input() {
341        // Test case with empty input, should be all zeros
342        let bytes = Bytes::from(vec![]);
343        let result = from_bytes_to_b256::<serde_json::Value>(bytes).unwrap();
344        let expected = B256::from_slice(&[0; 32]); // All zeros
345        assert_eq!(result, expected);
346    }
347}