Skip to main content

ethrex_common/
serde_utils.rs

1use std::time::Duration;
2
3use serde::{Deserialize, Deserializer, Serializer, de::Error, ser::SerializeSeq};
4
5pub mod u256 {
6    use super::*;
7    use ethereum_types::U256;
8
9    pub mod dec_str {
10        use super::*;
11        pub fn deserialize<'de, D>(d: D) -> Result<U256, D::Error>
12        where
13            D: Deserializer<'de>,
14        {
15            let value = String::deserialize(d)?;
16            U256::from_dec_str(&value).map_err(|e| D::Error::custom(e.to_string()))
17        }
18
19        pub fn serialize<S>(value: &U256, serializer: S) -> Result<S::Ok, S::Error>
20        where
21            S: Serializer,
22        {
23            serializer.serialize_str(&value.to_string())
24        }
25    }
26
27    pub fn deser_hex_str<'de, D>(d: D) -> Result<U256, D::Error>
28    where
29        D: Deserializer<'de>,
30    {
31        let value = String::deserialize(d)?;
32        U256::from_str_radix(value.trim_start_matches("0x"), 16)
33            .map_err(|_| D::Error::custom("Failed to deserialize u256 value"))
34    }
35
36    pub fn deser_hex_str_opt<'de, D>(d: D) -> Result<Option<U256>, D::Error>
37    where
38        D: Deserializer<'de>,
39    {
40        let s = Option::<String>::deserialize(d)?;
41        match s {
42            Some(s) => U256::from_str_radix(s.trim_start_matches("0x"), 16)
43                .map_err(|_| D::Error::custom("Failed to deserialize u256 value"))
44                .map(Some),
45            None => Ok(None),
46        }
47    }
48
49    pub fn deser_hex_or_dec_str<'de, D>(d: D) -> Result<U256, D::Error>
50    where
51        D: Deserializer<'de>,
52    {
53        let value = String::deserialize(d)?;
54        if value.starts_with("0x") {
55            U256::from_str_radix(value.trim_start_matches("0x"), 16)
56                .map_err(|_| D::Error::custom("Failed to deserialize u256 value"))
57        } else {
58            U256::from_dec_str(&value).map_err(|e| D::Error::custom(e.to_string()))
59        }
60    }
61
62    pub fn serialize_number<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: Serializer,
65    {
66        serializer.serialize_str(&value.to_string())
67    }
68
69    pub mod vec {
70        use super::*;
71        use serde::de::IntoDeserializer;
72        use serde::{Deserialize, Deserializer};
73
74        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<U256>, D::Error>
75        where
76            D: Deserializer<'de>,
77        {
78            let raw_vec = Vec::<String>::deserialize(deserializer)?;
79            raw_vec
80                .into_iter()
81                .map(|s| {
82                    let deser = s.into_deserializer();
83                    super::deser_hex_or_dec_str(deser)
84                })
85                .collect()
86        }
87    }
88
89    pub mod hashmap {
90        use super::*;
91        use serde::de::IntoDeserializer;
92        use serde::{Deserialize, Deserializer};
93        use std::collections::HashMap;
94
95        pub fn deserialize<'de, D>(deserializer: D) -> Result<HashMap<U256, U256>, D::Error>
96        where
97            D: Deserializer<'de>,
98        {
99            let raw_map = HashMap::<String, String>::deserialize(deserializer)?;
100            raw_map
101                .into_iter()
102                .map(|(k, v)| {
103                    let key_deser = k.into_deserializer();
104                    let val_deser = v.into_deserializer();
105
106                    let key = super::deser_hex_or_dec_str(key_deser)?;
107                    let value = super::deser_hex_or_dec_str(val_deser)?;
108                    Ok((key, value))
109                })
110                .collect()
111        }
112    }
113    pub mod hex_str_opt {
114        use serde::Serialize;
115
116        use super::*;
117
118        pub fn serialize<S>(value: &Option<U256>, serializer: S) -> Result<S::Ok, S::Error>
119        where
120            S: Serializer,
121        {
122            Option::<String>::serialize(&value.map(|v| format!("{v:#x}")), serializer)
123        }
124
125        pub fn deserialize<'de, D>(d: D) -> Result<Option<U256>, D::Error>
126        where
127            D: Deserializer<'de>,
128        {
129            let value = Option::<String>::deserialize(d)?;
130            match value {
131                Some(s) if !s.is_empty() => U256::from_str_radix(s.trim_start_matches("0x"), 16)
132                    .map_err(|_| D::Error::custom("Failed to deserialize U256 value"))
133                    .map(Some),
134                _ => Ok(None),
135            }
136        }
137    }
138}
139
140pub mod u32 {
141    use super::*;
142
143    pub mod hex_str {
144        use super::*;
145
146        pub fn deserialize<'de, D>(d: D) -> Result<u32, D::Error>
147        where
148            D: Deserializer<'de>,
149        {
150            let value = String::deserialize(d)?;
151            u32::from_str_radix(value.trim_start_matches("0x"), 16)
152                .map_err(|_| D::Error::custom("Failed to deserialize u32 value"))
153        }
154
155        pub fn serialize<S>(value: &u32, serializer: S) -> Result<S::Ok, S::Error>
156        where
157            S: Serializer,
158        {
159            serializer.serialize_str(&format!("{value:#x}"))
160        }
161    }
162}
163
164pub mod u8 {
165    use super::*;
166
167    pub mod hex_str {
168        use super::*;
169
170        pub fn deserialize<'de, D>(d: D) -> Result<u8, D::Error>
171        where
172            D: Deserializer<'de>,
173        {
174            let value = String::deserialize(d)?;
175            u8::from_str_radix(value.trim_start_matches("0x"), 16)
176                .map_err(|_| D::Error::custom("Failed to deserialize u8 value"))
177        }
178
179        pub fn serialize<S>(value: &u8, serializer: S) -> Result<S::Ok, S::Error>
180        where
181            S: Serializer,
182        {
183            serializer.serialize_str(&format!("{value:#x}"))
184        }
185    }
186}
187
188pub mod u64 {
189    use serde::de::IntoDeserializer;
190
191    use super::*;
192
193    pub mod hex_str {
194        use super::*;
195
196        pub fn deserialize<'de, D>(d: D) -> Result<u64, D::Error>
197        where
198            D: Deserializer<'de>,
199        {
200            let value = String::deserialize(d)?;
201            u64::from_str_radix(value.trim_start_matches("0x"), 16)
202                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
203        }
204
205        pub fn deser_vec<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
206        where
207            D: Deserializer<'de>,
208        {
209            let raw_vec = Vec::<String>::deserialize(deserializer)?;
210            raw_vec
211                .into_iter()
212                .map(|s| {
213                    let deser = s.into_deserializer();
214                    deserialize(deser)
215                })
216                .collect()
217        }
218
219        pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
220        where
221            S: Serializer,
222        {
223            serializer.serialize_str(&format!("{value:#x}"))
224        }
225    }
226    pub mod hex_str_padding {
227        use super::*;
228
229        pub fn deserialize<'de, D>(d: D) -> Result<u64, D::Error>
230        where
231            D: Deserializer<'de>,
232        {
233            super::hex_str::deserialize(d)
234        }
235
236        pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
237        where
238            S: Serializer,
239        {
240            serializer.serialize_str(&format!("{value:#018x}"))
241        }
242    }
243
244    pub mod hex_str_opt {
245        use serde::Serialize;
246
247        use super::*;
248
249        pub fn serialize<S>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
250        where
251            S: Serializer,
252        {
253            Option::<String>::serialize(&value.map(|v| format!("{v:#x}")), serializer)
254        }
255
256        pub fn deserialize<'de, D>(d: D) -> Result<Option<u64>, D::Error>
257        where
258            D: Deserializer<'de>,
259        {
260            let value: Option<serde_json::Value> = Option::deserialize(d)?;
261            match value {
262                Some(serde_json::Value::String(s)) if !s.is_empty() => {
263                    u64::from_str_radix(s.trim_start_matches("0x"), 16)
264                        .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
265                        .map(Some)
266                }
267                Some(serde_json::Value::Number(n)) => n
268                    .as_u64()
269                    .ok_or_else(|| D::Error::custom("Failed to deserialize u64 value"))
270                    .map(Some),
271                _ => Ok(None),
272            }
273        }
274    }
275
276    pub mod hex_str_opt_padded {
277        use serde::Serialize;
278
279        use super::*;
280        pub fn serialize<S>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
281        where
282            S: Serializer,
283        {
284            Option::<String>::serialize(&value.map(|v| format!("{v:#018x}")), serializer)
285        }
286
287        pub fn deserialize<'de, D>(d: D) -> Result<Option<u64>, D::Error>
288        where
289            D: Deserializer<'de>,
290        {
291            super::hex_str_opt::deserialize(d)
292        }
293    }
294
295    pub fn deser_dec_str<'de, D>(d: D) -> Result<u64, D::Error>
296    where
297        D: Deserializer<'de>,
298    {
299        let value = String::deserialize(d)?;
300        value
301            .parse()
302            .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
303    }
304
305    pub fn deser_hex_or_dec_str<'de, D>(d: D) -> Result<u64, D::Error>
306    where
307        D: Deserializer<'de>,
308    {
309        let value = String::deserialize(d)?;
310        if value.starts_with("0x") {
311            u64::from_str_radix(value.trim_start_matches("0x"), 16)
312                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
313        } else {
314            value
315                .parse()
316                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
317        }
318    }
319}
320
321pub mod u128 {
322    use super::*;
323
324    pub mod hex_str {
325        use super::*;
326
327        pub fn deserialize<'de, D>(d: D) -> Result<u128, D::Error>
328        where
329            D: Deserializer<'de>,
330        {
331            let value = String::deserialize(d)?;
332            u128::from_str_radix(value.trim_start_matches("0x"), 16)
333                .map_err(|_| D::Error::custom("Failed to deserialize u128 value"))
334        }
335
336        pub fn serialize<S>(value: &u128, serializer: S) -> Result<S::Ok, S::Error>
337        where
338            S: Serializer,
339        {
340            serializer.serialize_str(&format!("{value:#x}"))
341        }
342    }
343
344    /// Deserializes an `Option<u128>` from either a `0x`-hex string or a bare
345    /// JSON number, for geth/reth genesis compatibility.
346    ///
347    /// **Precision**: bare numbers above `u64::MAX` are read through an `f64`
348    /// cast, losing ~3 decimal digits. Acceptable for sentinel-only fields like
349    /// `terminalTotalDifficulty`; if you need bit-exact `u128` here, add a new
350    /// deserializer rather than reusing this one.
351    pub mod hex_str_opt {
352        use serde::Serialize;
353
354        use super::*;
355
356        pub fn serialize<S>(value: &Option<u128>, serializer: S) -> Result<S::Ok, S::Error>
357        where
358            S: Serializer,
359        {
360            Option::<String>::serialize(&value.map(|v| format!("{v:#x}")), serializer)
361        }
362
363        pub fn deserialize<'de, D>(d: D) -> Result<Option<u128>, D::Error>
364        where
365            D: Deserializer<'de>,
366        {
367            let value: Option<serde_json::Value> = Option::deserialize(d)?;
368            match value {
369                Some(serde_json::Value::Number(n)) => {
370                    // Values above u64::MAX (e.g. mainnet's TTD) are stored by
371                    // serde_json as f64, so `n.to_string()` can be "5.875e22",
372                    // which won't parse as u128. Read the integer directly and
373                    // fall back to f64 for the out-of-u64-range case (TTD is only
374                    // used as a post-merge sentinel, so f64 imprecision is moot).
375                    let v = if let Some(u) = n.as_u64() {
376                        u as u128
377                    } else if let Some(f) = n.as_f64().filter(|f| f.is_finite() && *f >= 0.0) {
378                        // `f as u128` saturates negatives to 0, which would mean
379                        // "PoS active" for TTD; reject them above instead.
380                        f as u128
381                    } else {
382                        return Err(D::Error::custom(format!(
383                            "u128 value must be a finite, non-negative number; got {n}"
384                        )));
385                    };
386                    Ok(Some(v))
387                }
388                Some(serde_json::Value::String(s)) if !s.is_empty() => {
389                    u128::from_str_radix(s.trim_start_matches("0x"), 16)
390                        .map_err(|_| D::Error::custom("Failed to deserialize u128 value"))
391                        .map(Some)
392                }
393                _ => Ok(None),
394            }
395        }
396    }
397}
398
399pub mod vec_u8 {
400    use ::bytes::Bytes;
401
402    use super::*;
403
404    pub fn deserialize<'de, D>(d: D) -> Result<Vec<u8>, D::Error>
405    where
406        D: Deserializer<'de>,
407    {
408        let value = String::deserialize(d)?;
409        let bytes = hex_simd::decode_to_vec(value.trim_start_matches("0x"))
410            .map_err(|e| D::Error::custom(e.to_string()))?;
411        Ok(bytes)
412    }
413
414    pub fn serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
415    where
416        S: Serializer,
417    {
418        serializer.serialize_str(&format!("0x{:x}", Bytes::copy_from_slice(value)))
419    }
420}
421
422/// Serializes to and deserializes from 0x prefixed hex string
423pub mod bytes {
424    use ::bytes::Bytes;
425
426    use super::*;
427
428    pub fn deserialize<'de, D>(d: D) -> Result<Bytes, D::Error>
429    where
430        D: Deserializer<'de>,
431    {
432        let value = String::deserialize(d)?;
433        let bytes = hex_simd::decode_to_vec(value.trim_start_matches("0x"))
434            .map_err(|e| D::Error::custom(e.to_string()))?;
435        Ok(Bytes::from(bytes))
436    }
437
438    pub fn serialize<S>(value: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
439    where
440        S: Serializer,
441    {
442        serializer.serialize_str(&format!("0x{value:x}"))
443    }
444
445    pub mod vec {
446        use super::*;
447
448        pub fn deserialize<'de, D>(d: D) -> Result<Vec<Bytes>, D::Error>
449        where
450            D: Deserializer<'de>,
451        {
452            let value = Vec::<String>::deserialize(d)?;
453            let mut output = Vec::new();
454            for str in value {
455                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
456                    .map_err(|e| D::Error::custom(e.to_string()))?
457                    .into();
458                output.push(bytes);
459            }
460            Ok(output)
461        }
462
463        pub fn serialize<S>(value: &Vec<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
464        where
465            S: Serializer,
466        {
467            serialize_vec_of_hex_encodables(value, serializer)
468        }
469    }
470}
471
472/// Serializes to and deserializes from 0x prefixed hex string
473pub mod bool {
474    use super::*;
475
476    pub fn deserialize<'de, D>(d: D) -> Result<bool, D::Error>
477    where
478        D: Deserializer<'de>,
479    {
480        let value = String::deserialize(d)?;
481        Ok(u8::from_str_radix(value.trim_start_matches("0x"), 16)
482            .map_err(|_| D::Error::custom("Failed to deserialize hex string to boolean value"))?
483            != 0)
484    }
485
486    pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
487    where
488        S: Serializer,
489    {
490        serializer.serialize_str(&format!("{:#x}", *value as u8))
491    }
492}
493
494pub mod bytes48 {
495    use super::*;
496
497    pub fn serialize<S>(value: &[u8; 48], serializer: S) -> Result<S::Ok, S::Error>
498    where
499        S: Serializer,
500    {
501        serializer.serialize_str(&format!("0x{}", hex::encode(value)))
502    }
503
504    pub mod vec {
505        use super::*;
506
507        pub fn serialize<S>(value: &Vec<[u8; 48]>, serializer: S) -> Result<S::Ok, S::Error>
508        where
509            S: Serializer,
510        {
511            serialize_vec_of_hex_encodables(value, serializer)
512        }
513
514        pub fn deserialize<'de, D>(d: D) -> Result<Vec<[u8; 48]>, D::Error>
515        where
516            D: Deserializer<'de>,
517        {
518            let value = Vec::<String>::deserialize(d)?;
519            let mut output = Vec::new();
520            for str in value {
521                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
522                    .map_err(|e| D::Error::custom(e.to_string()))?;
523                if bytes.len() != 48 {
524                    return Err(D::Error::custom(format!(
525                        "Expected 48 bytes, got {}",
526                        bytes.len()
527                    )));
528                }
529                let mut blob = [0u8; 48];
530                blob.copy_from_slice(&bytes);
531                output.push(blob);
532            }
533            Ok(output)
534        }
535    }
536}
537
538pub mod blob {
539    use super::*;
540    use crate::types::BYTES_PER_BLOB;
541
542    pub fn serialize<S>(value: &[u8; BYTES_PER_BLOB], serializer: S) -> Result<S::Ok, S::Error>
543    where
544        S: Serializer,
545    {
546        serializer.serialize_str(&format!("0x{}", hex::encode(value)))
547    }
548
549    pub mod vec {
550        use super::*;
551
552        pub fn serialize<S>(
553            value: &Vec<[u8; BYTES_PER_BLOB]>,
554            serializer: S,
555        ) -> Result<S::Ok, S::Error>
556        where
557            S: Serializer,
558        {
559            serialize_vec_of_hex_encodables(value, serializer)
560        }
561
562        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<[u8; BYTES_PER_BLOB]>, D::Error>
563        where
564            D: Deserializer<'de>,
565        {
566            let value = Vec::<String>::deserialize(deserializer)?;
567            let mut output = Vec::new();
568            for str in value {
569                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
570                    .map_err(|e| D::Error::custom(e.to_string()))?;
571                if bytes.len() != BYTES_PER_BLOB {
572                    return Err(D::Error::custom(format!(
573                        "Expected {} bytes, got {}",
574                        BYTES_PER_BLOB,
575                        bytes.len()
576                    )));
577                }
578                let mut blob = [0u8; BYTES_PER_BLOB];
579                blob.copy_from_slice(&bytes);
580                output.push(blob);
581            }
582            Ok(output)
583        }
584    }
585}
586
587// Const generics are not supported on `Serialize` impls so we need separate impls for different array sizes
588fn serialize_vec_of_hex_encodables<S: Serializer, T: std::convert::AsRef<[u8]>>(
589    value: &Vec<T>,
590    serializer: S,
591) -> Result<S::Ok, S::Error> {
592    let mut seq_serializer = serializer.serialize_seq(Some(value.len()))?;
593    for encoded in value {
594        seq_serializer.serialize_element(&format!("0x{}", hex::encode(encoded)))?;
595    }
596    seq_serializer.end()
597}
598
599pub mod duration {
600    use std::time::Duration;
601
602    use super::*;
603    pub fn deserialize<'de, D>(d: D) -> Result<Duration, D::Error>
604    where
605        D: Deserializer<'de>,
606    {
607        let value = String::deserialize(d)?;
608        parse_duration(value.clone())
609            .ok_or_else(|| D::Error::custom(format!("Failed to parse Duration: {value}")))
610    }
611
612    pub mod opt {
613        use super::*;
614
615        pub fn deserialize<'de, D>(d: D) -> Result<Option<Duration>, D::Error>
616        where
617            D: Deserializer<'de>,
618        {
619            if let Some(value) = Option::<String>::deserialize(d)? {
620                Ok(Some(parse_duration(value.clone()).ok_or_else(|| {
621                    D::Error::custom(format!("Failed to parse Duration: {value}"))
622                })?))
623            } else {
624                Ok(None)
625            }
626        }
627    }
628}
629
630/// Parses a Duration in string format
631/// The acceptable format is a concatentation of positive numeric values (with decimals allowed) followed by a time unit of measurement.
632/// The units accepted are: Hours(h), Minutes(m), Senconds(s), Milliseconds(ms), Microseconds (us|µs) and Nanoseconds(ns)
633/// For example, a duration such as "1h30m" or "1.6m" will be accepted but "-1s" or "30mh" will not
634/// Some imprecision can be expected when using milliseconds/microseconds/nanoseconds with significant decimal components
635/// If the format is incorrect this function will return None
636pub fn parse_duration(input: String) -> Option<Duration> {
637    let mut res = Duration::ZERO;
638    let mut integer_buffer = String::new();
639    let mut chars = input.chars().peekable();
640    while let Some(char) = chars.next() {
641        match char {
642            // Numeric Value
643            char @ '0'..='9' | char @ '.' => integer_buffer.push(char),
644            // Unit of Measurement
645            char => {
646                // Parse the numeric value we collected
647                let integer: f64 = integer_buffer.parse().ok()?;
648                // Obtain the duration component based off of the unit of measurement
649                let duration_component = match char {
650                    // Hour
651                    'h' => Duration::from_secs_f64(60_f64 * 60_f64 * integer),
652                    'm' => {
653                        if chars.peek().is_some_and(|c| *c == 's') {
654                            chars.next();
655                            // Millisecond
656                            Duration::from_micros((integer * 1000_f64).round() as u64)
657                        } else {
658                            // Minute
659                            Duration::from_secs_f64(60_f64 * integer)
660                        }
661                    }
662                    // Second
663                    's' => Duration::from_secs_f64(integer),
664                    // Microsecond
665                    'u' | 'µ' => {
666                        if chars.next().is_some_and(|c| c == 's') {
667                            Duration::from_nanos((integer * 1000_f64).round() as u64)
668                        } else {
669                            return None;
670                        }
671                    }
672                    // Nanosecond
673                    'n' => {
674                        if chars.next().is_some_and(|c| c == 's') {
675                            Duration::from_nanos(integer.round() as u64)
676                        } else {
677                            return None;
678                        }
679                    }
680                    _ => return None,
681                };
682                // Add duration component to result
683                res += duration_component;
684                // Clear state so we can parse the next value
685                integer_buffer.clear();
686            }
687        }
688    }
689    Some(res)
690}
691
692pub mod block_access_list {
693
694    use super::*;
695    use ethrex_rlp::decode::RLPDecode;
696    use ethrex_rlp::encode::RLPEncode;
697
698    pub mod rlp_str {
699
700        use crate::types::block_access_list::BlockAccessList;
701
702        use super::*;
703        pub fn deserialize<'de, D>(d: D) -> Result<BlockAccessList, D::Error>
704        where
705            D: Deserializer<'de>,
706        {
707            let value = String::deserialize(d)?;
708            // EIP-7928 blockAccessList is a DATA field (`^0x[0-9a-f]*$`): the `0x`
709            // prefix is mandatory. Require it rather than silently trimming, so an
710            // unprefixed (schema-invalid) value is rejected as strict clients do.
711            let hex_body = value
712                .strip_prefix("0x")
713                .ok_or_else(|| D::Error::custom("blockAccessList must be 0x-prefixed"))?;
714            let bytes = hex::decode(hex_body).map_err(|e| D::Error::custom(e.to_string()))?;
715            BlockAccessList::decode(&bytes)
716                .map_err(|_| D::Error::custom("Failed to RLP decode BAL"))
717        }
718
719        pub fn serialize<S>(value: &BlockAccessList, serializer: S) -> Result<S::Ok, S::Error>
720        where
721            S: Serializer,
722        {
723            let buf = value.encode_to_vec();
724            serializer.serialize_str(&format!("0x{}", hex::encode(buf)))
725        }
726    }
727
728    pub mod rlp_str_opt {
729
730        use serde::Serialize;
731
732        use crate::types::block_access_list::BlockAccessList;
733
734        use super::*;
735        pub fn deserialize<'de, D>(d: D) -> Result<Option<BlockAccessList>, D::Error>
736        where
737            D: Deserializer<'de>,
738        {
739            let value = Option::<String>::deserialize(d)?;
740            match value {
741                Some(s) => {
742                    // EIP-7928 blockAccessList is a DATA field: the `0x` prefix is
743                    // mandatory. An empty hex string ("0x") encodes the absence of a
744                    // BAL (not an empty list), so pre-Amsterdam newPayload calls that
745                    // send "0x" deserialize to None instead of failing RLP decode.
746                    let hex_body = s
747                        .strip_prefix("0x")
748                        .ok_or_else(|| D::Error::custom("blockAccessList must be 0x-prefixed"))?;
749                    if hex_body.is_empty() {
750                        return Ok(None);
751                    }
752                    let bytes =
753                        hex::decode(hex_body).map_err(|e| D::Error::custom(e.to_string()))?;
754                    BlockAccessList::decode(&bytes)
755                        .map(Some)
756                        .map_err(|_| D::Error::custom("Failed to RLP decode BAL"))
757                }
758                None => Ok(None),
759            }
760        }
761
762        pub fn serialize<S>(
763            value: &Option<BlockAccessList>,
764            serializer: S,
765        ) -> Result<S::Ok, S::Error>
766        where
767            S: Serializer,
768        {
769            let bal = value
770                .as_ref()
771                .map(|bal| bal.encode_to_vec())
772                .map(|bytes| format!("0x{}", hex::encode(bytes)));
773            Option::<String>::serialize(&bal, serializer)
774        }
775    }
776}