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 u64 {
165    use serde::de::IntoDeserializer;
166
167    use super::*;
168
169    pub mod hex_str {
170        use super::*;
171
172        pub fn deserialize<'de, D>(d: D) -> Result<u64, D::Error>
173        where
174            D: Deserializer<'de>,
175        {
176            let value = String::deserialize(d)?;
177            u64::from_str_radix(value.trim_start_matches("0x"), 16)
178                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
179        }
180
181        pub fn deser_vec<'de, D>(deserializer: D) -> Result<Vec<u64>, D::Error>
182        where
183            D: Deserializer<'de>,
184        {
185            let raw_vec = Vec::<String>::deserialize(deserializer)?;
186            raw_vec
187                .into_iter()
188                .map(|s| {
189                    let deser = s.into_deserializer();
190                    deserialize(deser)
191                })
192                .collect()
193        }
194
195        pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
196        where
197            S: Serializer,
198        {
199            serializer.serialize_str(&format!("{value:#x}"))
200        }
201    }
202    pub mod hex_str_padding {
203        use super::*;
204
205        pub fn deserialize<'de, D>(d: D) -> Result<u64, D::Error>
206        where
207            D: Deserializer<'de>,
208        {
209            super::hex_str::deserialize(d)
210        }
211
212        pub fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
213        where
214            S: Serializer,
215        {
216            serializer.serialize_str(&format!("{value:#018x}"))
217        }
218    }
219
220    pub mod hex_str_opt {
221        use serde::Serialize;
222
223        use super::*;
224
225        pub fn serialize<S>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
226        where
227            S: Serializer,
228        {
229            Option::<String>::serialize(&value.map(|v| format!("{v:#x}")), serializer)
230        }
231
232        pub fn deserialize<'de, D>(d: D) -> Result<Option<u64>, D::Error>
233        where
234            D: Deserializer<'de>,
235        {
236            let value: Option<serde_json::Value> = Option::deserialize(d)?;
237            match value {
238                Some(serde_json::Value::String(s)) if !s.is_empty() => {
239                    u64::from_str_radix(s.trim_start_matches("0x"), 16)
240                        .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
241                        .map(Some)
242                }
243                Some(serde_json::Value::Number(n)) => n
244                    .as_u64()
245                    .ok_or_else(|| D::Error::custom("Failed to deserialize u64 value"))
246                    .map(Some),
247                _ => Ok(None),
248            }
249        }
250    }
251
252    pub mod hex_str_opt_padded {
253        use serde::Serialize;
254
255        use super::*;
256        pub fn serialize<S>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error>
257        where
258            S: Serializer,
259        {
260            Option::<String>::serialize(&value.map(|v| format!("{v:#018x}")), serializer)
261        }
262
263        pub fn deserialize<'de, D>(d: D) -> Result<Option<u64>, D::Error>
264        where
265            D: Deserializer<'de>,
266        {
267            super::hex_str_opt::deserialize(d)
268        }
269    }
270
271    pub fn deser_dec_str<'de, D>(d: D) -> Result<u64, D::Error>
272    where
273        D: Deserializer<'de>,
274    {
275        let value = String::deserialize(d)?;
276        value
277            .parse()
278            .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
279    }
280
281    pub fn deser_hex_or_dec_str<'de, D>(d: D) -> Result<u64, D::Error>
282    where
283        D: Deserializer<'de>,
284    {
285        let value = String::deserialize(d)?;
286        if value.starts_with("0x") {
287            u64::from_str_radix(value.trim_start_matches("0x"), 16)
288                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
289        } else {
290            value
291                .parse()
292                .map_err(|_| D::Error::custom("Failed to deserialize u64 value"))
293        }
294    }
295}
296
297pub mod u128 {
298    use super::*;
299
300    pub mod hex_str {
301        use super::*;
302
303        pub fn deserialize<'de, D>(d: D) -> Result<u128, D::Error>
304        where
305            D: Deserializer<'de>,
306        {
307            let value = String::deserialize(d)?;
308            u128::from_str_radix(value.trim_start_matches("0x"), 16)
309                .map_err(|_| D::Error::custom("Failed to deserialize u128 value"))
310        }
311
312        pub fn serialize<S>(value: &u128, serializer: S) -> Result<S::Ok, S::Error>
313        where
314            S: Serializer,
315        {
316            serializer.serialize_str(&format!("{value:#x}"))
317        }
318    }
319
320    /// Deserializes an `Option<u128>` from either a `0x`-hex string or a bare
321    /// JSON number, for geth/reth genesis compatibility.
322    ///
323    /// **Precision**: bare numbers above `u64::MAX` are read through an `f64`
324    /// cast, losing ~3 decimal digits. Acceptable for sentinel-only fields like
325    /// `terminalTotalDifficulty`; if you need bit-exact `u128` here, add a new
326    /// deserializer rather than reusing this one.
327    pub mod hex_str_opt {
328        use serde::Serialize;
329
330        use super::*;
331
332        pub fn serialize<S>(value: &Option<u128>, serializer: S) -> Result<S::Ok, S::Error>
333        where
334            S: Serializer,
335        {
336            Option::<String>::serialize(&value.map(|v| format!("{v:#x}")), serializer)
337        }
338
339        pub fn deserialize<'de, D>(d: D) -> Result<Option<u128>, D::Error>
340        where
341            D: Deserializer<'de>,
342        {
343            let value: Option<serde_json::Value> = Option::deserialize(d)?;
344            match value {
345                Some(serde_json::Value::Number(n)) => {
346                    // Values above u64::MAX (e.g. mainnet's TTD) are stored by
347                    // serde_json as f64, so `n.to_string()` can be "5.875e22",
348                    // which won't parse as u128. Read the integer directly and
349                    // fall back to f64 for the out-of-u64-range case (TTD is only
350                    // used as a post-merge sentinel, so f64 imprecision is moot).
351                    let v = if let Some(u) = n.as_u64() {
352                        u as u128
353                    } else if let Some(f) = n.as_f64().filter(|f| f.is_finite() && *f >= 0.0) {
354                        // `f as u128` saturates negatives to 0, which would mean
355                        // "PoS active" for TTD; reject them above instead.
356                        f as u128
357                    } else {
358                        return Err(D::Error::custom(format!(
359                            "u128 value must be a finite, non-negative number; got {n}"
360                        )));
361                    };
362                    Ok(Some(v))
363                }
364                Some(serde_json::Value::String(s)) if !s.is_empty() => {
365                    u128::from_str_radix(s.trim_start_matches("0x"), 16)
366                        .map_err(|_| D::Error::custom("Failed to deserialize u128 value"))
367                        .map(Some)
368                }
369                _ => Ok(None),
370            }
371        }
372    }
373}
374
375pub mod vec_u8 {
376    use ::bytes::Bytes;
377
378    use super::*;
379
380    pub fn deserialize<'de, D>(d: D) -> Result<Vec<u8>, D::Error>
381    where
382        D: Deserializer<'de>,
383    {
384        let value = String::deserialize(d)?;
385        let bytes = hex_simd::decode_to_vec(value.trim_start_matches("0x"))
386            .map_err(|e| D::Error::custom(e.to_string()))?;
387        Ok(bytes)
388    }
389
390    pub fn serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
391    where
392        S: Serializer,
393    {
394        serializer.serialize_str(&format!("0x{:x}", Bytes::copy_from_slice(value)))
395    }
396}
397
398/// Serializes to and deserializes from 0x prefixed hex string
399pub mod bytes {
400    use ::bytes::Bytes;
401
402    use super::*;
403
404    pub fn deserialize<'de, D>(d: D) -> Result<Bytes, 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::from(bytes))
412    }
413
414    pub fn serialize<S>(value: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
415    where
416        S: Serializer,
417    {
418        serializer.serialize_str(&format!("0x{value:x}"))
419    }
420
421    pub mod vec {
422        use super::*;
423
424        pub fn deserialize<'de, D>(d: D) -> Result<Vec<Bytes>, D::Error>
425        where
426            D: Deserializer<'de>,
427        {
428            let value = Vec::<String>::deserialize(d)?;
429            let mut output = Vec::new();
430            for str in value {
431                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
432                    .map_err(|e| D::Error::custom(e.to_string()))?
433                    .into();
434                output.push(bytes);
435            }
436            Ok(output)
437        }
438
439        pub fn serialize<S>(value: &Vec<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
440        where
441            S: Serializer,
442        {
443            serialize_vec_of_hex_encodables(value, serializer)
444        }
445    }
446}
447
448/// Serializes to and deserializes from 0x prefixed hex string
449pub mod bool {
450    use super::*;
451
452    pub fn deserialize<'de, D>(d: D) -> Result<bool, D::Error>
453    where
454        D: Deserializer<'de>,
455    {
456        let value = String::deserialize(d)?;
457        Ok(u8::from_str_radix(value.trim_start_matches("0x"), 16)
458            .map_err(|_| D::Error::custom("Failed to deserialize hex string to boolean value"))?
459            != 0)
460    }
461
462    pub fn serialize<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
463    where
464        S: Serializer,
465    {
466        serializer.serialize_str(&format!("{:#x}", *value as u8))
467    }
468}
469
470pub mod bytes48 {
471    use super::*;
472
473    pub fn serialize<S>(value: &[u8; 48], serializer: S) -> Result<S::Ok, S::Error>
474    where
475        S: Serializer,
476    {
477        serializer.serialize_str(&format!("0x{}", hex::encode(value)))
478    }
479
480    pub mod vec {
481        use super::*;
482
483        pub fn serialize<S>(value: &Vec<[u8; 48]>, serializer: S) -> Result<S::Ok, S::Error>
484        where
485            S: Serializer,
486        {
487            serialize_vec_of_hex_encodables(value, serializer)
488        }
489
490        pub fn deserialize<'de, D>(d: D) -> Result<Vec<[u8; 48]>, D::Error>
491        where
492            D: Deserializer<'de>,
493        {
494            let value = Vec::<String>::deserialize(d)?;
495            let mut output = Vec::new();
496            for str in value {
497                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
498                    .map_err(|e| D::Error::custom(e.to_string()))?;
499                if bytes.len() != 48 {
500                    return Err(D::Error::custom(format!(
501                        "Expected 48 bytes, got {}",
502                        bytes.len()
503                    )));
504                }
505                let mut blob = [0u8; 48];
506                blob.copy_from_slice(&bytes);
507                output.push(blob);
508            }
509            Ok(output)
510        }
511    }
512}
513
514pub mod blob {
515    use super::*;
516    use crate::types::BYTES_PER_BLOB;
517
518    pub fn serialize<S>(value: &[u8; BYTES_PER_BLOB], serializer: S) -> Result<S::Ok, S::Error>
519    where
520        S: Serializer,
521    {
522        serializer.serialize_str(&format!("0x{}", hex::encode(value)))
523    }
524
525    pub mod vec {
526        use super::*;
527
528        pub fn serialize<S>(
529            value: &Vec<[u8; BYTES_PER_BLOB]>,
530            serializer: S,
531        ) -> Result<S::Ok, S::Error>
532        where
533            S: Serializer,
534        {
535            serialize_vec_of_hex_encodables(value, serializer)
536        }
537
538        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<[u8; BYTES_PER_BLOB]>, D::Error>
539        where
540            D: Deserializer<'de>,
541        {
542            let value = Vec::<String>::deserialize(deserializer)?;
543            let mut output = Vec::new();
544            for str in value {
545                let bytes = hex_simd::decode_to_vec(str.trim_start_matches("0x"))
546                    .map_err(|e| D::Error::custom(e.to_string()))?;
547                if bytes.len() != BYTES_PER_BLOB {
548                    return Err(D::Error::custom(format!(
549                        "Expected {} bytes, got {}",
550                        BYTES_PER_BLOB,
551                        bytes.len()
552                    )));
553                }
554                let mut blob = [0u8; BYTES_PER_BLOB];
555                blob.copy_from_slice(&bytes);
556                output.push(blob);
557            }
558            Ok(output)
559        }
560    }
561}
562
563// Const generics are not supported on `Serialize` impls so we need separate impls for different array sizes
564fn serialize_vec_of_hex_encodables<S: Serializer, T: std::convert::AsRef<[u8]>>(
565    value: &Vec<T>,
566    serializer: S,
567) -> Result<S::Ok, S::Error> {
568    let mut seq_serializer = serializer.serialize_seq(Some(value.len()))?;
569    for encoded in value {
570        seq_serializer.serialize_element(&format!("0x{}", hex::encode(encoded)))?;
571    }
572    seq_serializer.end()
573}
574
575pub mod duration {
576    use std::time::Duration;
577
578    use super::*;
579    pub fn deserialize<'de, D>(d: D) -> Result<Duration, D::Error>
580    where
581        D: Deserializer<'de>,
582    {
583        let value = String::deserialize(d)?;
584        parse_duration(value.clone())
585            .ok_or_else(|| D::Error::custom(format!("Failed to parse Duration: {value}")))
586    }
587
588    pub mod opt {
589        use super::*;
590
591        pub fn deserialize<'de, D>(d: D) -> Result<Option<Duration>, D::Error>
592        where
593            D: Deserializer<'de>,
594        {
595            if let Some(value) = Option::<String>::deserialize(d)? {
596                Ok(Some(parse_duration(value.clone()).ok_or_else(|| {
597                    D::Error::custom(format!("Failed to parse Duration: {value}"))
598                })?))
599            } else {
600                Ok(None)
601            }
602        }
603    }
604}
605
606/// Parses a Duration in string format
607/// The acceptable format is a concatentation of positive numeric values (with decimals allowed) followed by a time unit of measurement.
608/// The units accepted are: Hours(h), Minutes(m), Senconds(s), Milliseconds(ms), Microseconds (us|µs) and Nanoseconds(ns)
609/// For example, a duration such as "1h30m" or "1.6m" will be accepted but "-1s" or "30mh" will not
610/// Some imprecision can be expected when using milliseconds/microseconds/nanoseconds with significant decimal components
611/// If the format is incorrect this function will return None
612pub fn parse_duration(input: String) -> Option<Duration> {
613    let mut res = Duration::ZERO;
614    let mut integer_buffer = String::new();
615    let mut chars = input.chars().peekable();
616    while let Some(char) = chars.next() {
617        match char {
618            // Numeric Value
619            char @ '0'..='9' | char @ '.' => integer_buffer.push(char),
620            // Unit of Measurement
621            char => {
622                // Parse the numeric value we collected
623                let integer: f64 = integer_buffer.parse().ok()?;
624                // Obtain the duration component based off of the unit of measurement
625                let duration_component = match char {
626                    // Hour
627                    'h' => Duration::from_secs_f64(60_f64 * 60_f64 * integer),
628                    'm' => {
629                        if chars.peek().is_some_and(|c| *c == 's') {
630                            chars.next();
631                            // Millisecond
632                            Duration::from_micros((integer * 1000_f64).round() as u64)
633                        } else {
634                            // Minute
635                            Duration::from_secs_f64(60_f64 * integer)
636                        }
637                    }
638                    // Second
639                    's' => Duration::from_secs_f64(integer),
640                    // Microsecond
641                    'u' | 'µ' => {
642                        if chars.next().is_some_and(|c| c == 's') {
643                            Duration::from_nanos((integer * 1000_f64).round() as u64)
644                        } else {
645                            return None;
646                        }
647                    }
648                    // Nanosecond
649                    'n' => {
650                        if chars.next().is_some_and(|c| c == 's') {
651                            Duration::from_nanos(integer.round() as u64)
652                        } else {
653                            return None;
654                        }
655                    }
656                    _ => return None,
657                };
658                // Add duration component to result
659                res += duration_component;
660                // Clear state so we can parse the next value
661                integer_buffer.clear();
662            }
663        }
664    }
665    Some(res)
666}
667
668pub mod block_access_list {
669
670    use super::*;
671    use ethrex_rlp::decode::RLPDecode;
672    use ethrex_rlp::encode::RLPEncode;
673
674    pub mod rlp_str {
675
676        use crate::types::block_access_list::BlockAccessList;
677
678        use super::*;
679        pub fn deserialize<'de, D>(d: D) -> Result<BlockAccessList, D::Error>
680        where
681            D: Deserializer<'de>,
682        {
683            let value = String::deserialize(d)?;
684            let bytes = hex::decode(value.trim_start_matches("0x"))
685                .map_err(|e| D::Error::custom(e.to_string()))?;
686            BlockAccessList::decode(&bytes)
687                .map_err(|_| D::Error::custom("Failed to RLP decode BAL"))
688        }
689
690        pub fn serialize<S>(value: &BlockAccessList, serializer: S) -> Result<S::Ok, S::Error>
691        where
692            S: Serializer,
693        {
694            let buf = value.encode_to_vec();
695            serializer.serialize_str(&format!("0x{}", hex::encode(buf)))
696        }
697    }
698
699    pub mod rlp_str_opt {
700
701        use serde::Serialize;
702
703        use crate::types::block_access_list::BlockAccessList;
704
705        use super::*;
706        pub fn deserialize<'de, D>(d: D) -> Result<Option<BlockAccessList>, D::Error>
707        where
708            D: Deserializer<'de>,
709        {
710            let value = Option::<String>::deserialize(d)?;
711            match value {
712                // An empty hex string ("0x") encodes the absence of a BAL, not an
713                // empty list. Treat it as None so pre-Amsterdam newPayload calls
714                // (which send "0x") deserialize instead of failing RLP decode.
715                Some(s) if !s.trim_start_matches("0x").is_empty() => {
716                    hex::decode(s.trim_start_matches("0x"))
717                        .map_err(|e| D::Error::custom(e.to_string()))
718                        .and_then(|b| {
719                            BlockAccessList::decode(&b)
720                                .map_err(|_| D::Error::custom("Failed to RLP decode BAL"))
721                        })
722                        .map(Some)
723                }
724                _ => Ok(None),
725            }
726        }
727
728        pub fn serialize<S>(
729            value: &Option<BlockAccessList>,
730            serializer: S,
731        ) -> Result<S::Ok, S::Error>
732        where
733            S: Serializer,
734        {
735            let bal = value
736                .as_ref()
737                .map(|bal| bal.encode_to_vec())
738                .map(|bytes| format!("0x{}", hex::encode(bytes)));
739            Option::<String>::serialize(&bal, serializer)
740        }
741    }
742}