soroban_spec_tools/
lib.rs

1#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
2use std::fmt::Write;
3use std::str::FromStr;
4
5use itertools::Itertools;
6use serde_json::{json, Value};
7use stellar_xdr::curr::{
8    AccountId, BytesM, ContractExecutable, ContractId, Error as XdrError, Hash, Int128Parts,
9    Int256Parts, PublicKey, ScAddress, ScBytes, ScContractInstance, ScMap, ScMapEntry, ScNonceKey,
10    ScSpecEntry, ScSpecEventV0, ScSpecFunctionV0, ScSpecTypeDef as ScType, ScSpecTypeMap,
11    ScSpecTypeOption, ScSpecTypeResult, ScSpecTypeTuple, ScSpecTypeUdt, ScSpecTypeVec,
12    ScSpecUdtEnumV0, ScSpecUdtErrorEnumCaseV0, ScSpecUdtErrorEnumV0, ScSpecUdtStructV0,
13    ScSpecUdtUnionCaseTupleV0, ScSpecUdtUnionCaseV0, ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0,
14    ScString, ScSymbol, ScVal, ScVec, StringM, UInt128Parts, UInt256Parts, Uint256, VecM,
15};
16
17pub mod contract;
18pub mod utils;
19
20#[derive(thiserror::Error, Debug)]
21pub enum Error {
22    #[error("an unknown error occurred")]
23    Unknown,
24    #[error("Invalid pair {0:#?} {1:#?}")]
25    InvalidPair(ScVal, ScType),
26    #[error("value is not parseable to {0:#?}")]
27    InvalidValue(Option<ScType>),
28    #[error("Unknown case {0} for {1}")]
29    EnumCase(String, String),
30    #[error("Enum {0} missing value for type {1}")]
31    EnumMissingSecondValue(String, String),
32    #[error("Enum {0} is illformed")]
33    IllFormedEnum(String),
34    #[error("Unknown const case {0}")]
35    EnumConst(u32),
36    #[error("Enum const value must be a u32 or smaller")]
37    EnumConstTooLarge(u64),
38    #[error("Missing Entry {0}")]
39    MissingEntry(String),
40    #[error("Missing Spec")]
41    MissingSpec,
42    #[error(transparent)]
43    Xdr(XdrError),
44    #[error(transparent)]
45    Serde(#[from] serde_json::Error),
46    #[error(transparent)]
47    Ethnum(#[from] core::num::ParseIntError),
48
49    #[error("Missing key {0} in map")]
50    MissingKey(String),
51    #[error("Failed to convert {0} to number")]
52    FailedNumConversion(serde_json::Number),
53    #[error("First argument in an enum must be a sybmol")]
54    EnumFirstValueNotSymbol,
55    #[error("Failed to find enum case {0}")]
56    FailedToFindEnumCase(String),
57    #[error(transparent)]
58    FailedSilceToByte(#[from] std::array::TryFromSliceError),
59    #[error(transparent)]
60    Infallible(#[from] std::convert::Infallible),
61    #[error("Missing Error case {0}")]
62    MissingErrorCase(u32),
63    #[error(transparent)]
64    Spec(#[from] soroban_spec::read::FromWasmError),
65    #[error(transparent)]
66    Base64Spec(#[from] soroban_spec::read::ParseSpecBase64Error),
67}
68
69#[derive(Default, Clone)]
70pub struct Spec(pub Option<Vec<ScSpecEntry>>);
71
72impl TryInto<Spec> for &[u8] {
73    type Error = soroban_spec::read::FromWasmError;
74
75    fn try_into(self) -> Result<Spec, Self::Error> {
76        let spec = soroban_spec::read::from_wasm(self)?;
77        Ok(Spec::new(spec.as_slice()))
78    }
79}
80
81impl Spec {
82    pub fn new(entries: &[ScSpecEntry]) -> Self {
83        Self(Some(entries.to_vec()))
84    }
85
86    pub fn from_wasm(wasm: &[u8]) -> Result<Spec, Error> {
87        let spec = soroban_spec::read::from_wasm(wasm)?;
88        Ok(Spec::new(spec.as_slice()))
89    }
90
91    pub fn parse_base64(base64: &str) -> Result<Spec, Error> {
92        let spec = soroban_spec::read::parse_base64(base64.as_bytes())?;
93        Ok(Spec::new(spec.as_slice()))
94    }
95}
96
97impl Spec {
98    /// # Errors
99    /// Could fail to find User Defined Type
100    pub fn doc(&self, name: &str, type_: &ScType) -> Result<Option<&'static str>, Error> {
101        let mut str = match type_ {
102            ScType::Val
103            | ScType::U64
104            | ScType::I64
105            | ScType::U128
106            | ScType::I128
107            | ScType::U32
108            | ScType::I32
109            | ScType::Result(_)
110            | ScType::Vec(_)
111            | ScType::Map(_)
112            | ScType::Tuple(_)
113            | ScType::BytesN(_)
114            | ScType::Symbol
115            | ScType::Error
116            | ScType::Bytes
117            | ScType::Void
118            | ScType::Timepoint
119            | ScType::Duration
120            | ScType::U256
121            | ScType::I256
122            | ScType::String
123            | ScType::Bool => String::new(),
124            ScType::MuxedAddress => {
125                String::from("Can be public key (G13..), contract ID (C13...), or a muxed account (M13..), or an identity")
126            }
127            ScType::Address => String::from(
128                "Can be public key (G13..), a contract ID (C13...) or an identity (alice), ",
129            ),
130            ScType::Option(type_) => return self.doc(name, &type_.value_type),
131            ScType::Udt(ScSpecTypeUdt { name }) => {
132                let spec_type = self.find(&name.to_utf8_string_lossy())?;
133                match spec_type {
134                    ScSpecEntry::FunctionV0(ScSpecFunctionV0 { doc, .. })
135                    | ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { doc, .. })
136                    | ScSpecEntry::UdtUnionV0(ScSpecUdtUnionV0 { doc, .. })
137                    | ScSpecEntry::UdtEnumV0(ScSpecUdtEnumV0 { doc, .. })
138                    | ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { doc, .. })
139                    | ScSpecEntry::EventV0(ScSpecEventV0 { doc, .. }) => doc,
140                }
141                .to_utf8_string_lossy()
142            }
143        };
144        if let Some(mut ex) = self.example(type_) {
145            if ex.contains(' ') {
146                ex = format!("'{ex}'");
147            } else if ex.contains('"') {
148                ex = ex.replace('"', "");
149            }
150            if matches!(type_, ScType::Bool) {
151                ex = String::new();
152            }
153            let sep = if str.is_empty() { "" } else { "\n" };
154            str = format!("{str}{sep}Example:\n  --{name} {ex}");
155            if ex.contains('"') {}
156        }
157        if str.is_empty() {
158            Ok(None)
159        } else {
160            Ok(Some(Box::leak(str.into_boxed_str())))
161        }
162    }
163
164    /// # Errors
165    ///
166    /// Might return errors
167    pub fn find(&self, name: &str) -> Result<&ScSpecEntry, Error> {
168        self.0
169            .as_ref()
170            .and_then(|specs| {
171                specs.iter().find(|e| {
172                    let entry_name = match e {
173                        ScSpecEntry::FunctionV0(x) => x.name.to_utf8_string_lossy(),
174                        ScSpecEntry::UdtStructV0(x) => x.name.to_utf8_string_lossy(),
175                        ScSpecEntry::UdtUnionV0(x) => x.name.to_utf8_string_lossy(),
176                        ScSpecEntry::UdtEnumV0(x) => x.name.to_utf8_string_lossy(),
177                        ScSpecEntry::UdtErrorEnumV0(x) => x.name.to_utf8_string_lossy(),
178                        ScSpecEntry::EventV0(x) => x.name.to_utf8_string_lossy(),
179                    };
180                    name == entry_name
181                })
182            })
183            .ok_or_else(|| Error::MissingEntry(name.to_owned()))
184    }
185
186    /// # Errors
187    ///
188    /// Might return errors
189    pub fn find_function(&self, name: &str) -> Result<&ScSpecFunctionV0, Error> {
190        match self.find(name)? {
191            ScSpecEntry::FunctionV0(f) => Ok(f),
192            _ => Err(Error::MissingEntry(name.to_owned())),
193        }
194    }
195    //
196    /// # Errors
197    ///
198    pub fn find_functions(&self) -> Result<impl Iterator<Item = &ScSpecFunctionV0>, Error> {
199        Ok(self
200            .0
201            .as_deref()
202            .ok_or(Error::MissingSpec)?
203            .iter()
204            .filter_map(|e| match e {
205                ScSpecEntry::FunctionV0(x) => Some(x),
206                _ => None,
207            }))
208    }
209
210    /// # Errors
211    ///
212    pub fn find_error_type(&self, value: u32) -> Result<&ScSpecUdtErrorEnumCaseV0, Error> {
213        if let ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { cases, .. }) =
214            self.find("Error")?
215        {
216            if let Some(case) = cases.iter().find(|case| value == case.value) {
217                return Ok(case);
218            }
219        }
220        Err(Error::MissingErrorCase(value))
221    }
222
223    /// # Errors
224    ///
225    /// Might return errors
226    pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
227        Self::default().from_string(s, t)
228    }
229
230    /// # Errors
231    ///
232    /// Might return errors
233    #[allow(clippy::wrong_self_convention)]
234    pub fn from_string(&self, s: &str, t: &ScType) -> Result<ScVal, Error> {
235        if let ScType::Option(b) = t {
236            if s == "null" {
237                return Ok(ScVal::Void);
238            }
239            let ScSpecTypeOption { value_type } = b.as_ref().clone();
240            let v = value_type.as_ref().clone();
241            return self.from_string(s, &v);
242        }
243        // Parse as string and for special types assume Value::String
244        serde_json::from_str(s)
245            .map_or_else(
246                |e| match t {
247                    ScType::Symbol
248                    | ScType::String
249                    | ScType::Bytes
250                    | ScType::BytesN(_)
251                    | ScType::U256
252                    | ScType::I256
253                    | ScType::U128
254                    | ScType::I128
255                    | ScType::Address => Ok(Value::String(s.to_owned())),
256                    ScType::Udt(ScSpecTypeUdt { name })
257                        if matches!(
258                            self.find(&name.to_utf8_string_lossy())?,
259                            ScSpecEntry::UdtUnionV0(_) | ScSpecEntry::UdtStructV0(_)
260                        ) =>
261                    {
262                        Ok(Value::String(s.to_owned()))
263                    }
264                    _ => Err(Error::Serde(e)),
265                },
266                |val| match t {
267                    ScType::U128 | ScType::I128 | ScType::U256 | ScType::I256 => {
268                        Ok(Value::String(s.to_owned()))
269                    }
270                    _ => Ok(val),
271                },
272            )
273            .and_then(|raw| self.from_json(&raw, t))
274    }
275
276    /// # Errors
277    ///
278    /// Might return errors
279    #[allow(clippy::wrong_self_convention)]
280    pub fn from_json(&self, v: &Value, t: &ScType) -> Result<ScVal, Error> {
281        let val: ScVal = match (t, v) {
282            (
283                ScType::Bool
284                | ScType::U128
285                | ScType::I128
286                | ScType::U256
287                | ScType::I256
288                | ScType::I32
289                | ScType::I64
290                | ScType::U32
291                | ScType::U64
292                | ScType::String
293                | ScType::Symbol
294                | ScType::Address
295                | ScType::Bytes
296                | ScType::BytesN(_),
297                _,
298            ) => from_json_primitives(v, t)?,
299
300            // Vec parsing
301            (ScType::Vec(elem), Value::Array(raw)) => {
302                let converted: ScVec = raw
303                    .iter()
304                    .map(|item| self.from_json(item, &elem.element_type))
305                    .collect::<Result<Vec<ScVal>, Error>>()?
306                    .try_into()
307                    .map_err(Error::Xdr)?;
308                ScVal::Vec(Some(converted))
309            }
310
311            // Map parsing
312            (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?,
313
314            // Option parsing
315            (ScType::Option(_), Value::Null) => ScVal::Void,
316            (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?,
317
318            // Tuple parsing
319            (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?,
320
321            // User defined types parsing
322            (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?,
323
324            // TODO: Implement the rest of these
325            (_, raw) => serde_json::from_value(raw.clone()).map_err(Error::Serde)?,
326        };
327        Ok(val)
328    }
329
330    fn parse_udt(&self, name: &StringM<60>, value: &Value) -> Result<ScVal, Error> {
331        let name = &name.to_utf8_string_lossy();
332        match (self.find(name)?, value) {
333            (ScSpecEntry::UdtStructV0(strukt), Value::Object(map)) => {
334                if strukt
335                    .fields
336                    .iter()
337                    .any(|f| f.name.to_utf8_string_lossy() == "0")
338                {
339                    self.parse_tuple_strukt(
340                        strukt,
341                        &(0..map.len())
342                            .map(|i| map.get(&i.to_string()).unwrap().clone())
343                            .collect::<Vec<_>>(),
344                    )
345                } else {
346                    self.parse_strukt(strukt, map)
347                }
348            }
349            (ScSpecEntry::UdtStructV0(strukt), Value::Array(arr)) => {
350                self.parse_tuple_strukt(strukt, arr)
351            }
352            (
353                ScSpecEntry::UdtUnionV0(union),
354                val @ (Value::Array(_) | Value::String(_) | Value::Object(_)),
355            ) => self.parse_union(union, val),
356            (ScSpecEntry::UdtEnumV0(enum_), Value::Number(num)) => parse_const_enum(num, enum_),
357            (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
358        }
359    }
360
361    fn parse_tuple_strukt(
362        &self,
363        strukt: &ScSpecUdtStructV0,
364        array: &[Value],
365    ) -> Result<ScVal, Error> {
366        let items = strukt
367            .fields
368            .to_vec()
369            .iter()
370            .zip(array.iter())
371            .map(|(f, v)| {
372                let val = self.from_json(v, &f.type_)?;
373                Ok(val)
374            })
375            .collect::<Result<Vec<_>, Error>>()?;
376        Ok(ScVal::Vec(Some(items.try_into().map_err(Error::Xdr)?)))
377    }
378
379    fn parse_strukt(
380        &self,
381        strukt: &ScSpecUdtStructV0,
382        map: &serde_json::Map<String, Value>,
383    ) -> Result<ScVal, Error> {
384        let items = strukt
385            .fields
386            .to_vec()
387            .iter()
388            .map(|f| {
389                let name = &f.name.to_utf8_string_lossy();
390                let v = map
391                    .get(name)
392                    .ok_or_else(|| Error::MissingKey(name.clone()))?;
393                let val = self.from_json(v, &f.type_)?;
394                let key = StringM::from_str(name).unwrap();
395                Ok(ScMapEntry {
396                    key: ScVal::Symbol(key.into()),
397                    val,
398                })
399            })
400            .collect::<Result<Vec<_>, Error>>()?;
401        let map = ScMap::sorted_from(items).map_err(Error::Xdr)?;
402        Ok(ScVal::Map(Some(map)))
403    }
404
405    fn parse_union(&self, union: &ScSpecUdtUnionV0, value: &Value) -> Result<ScVal, Error> {
406        let (enum_case, rest) = match value {
407            Value::String(s) => (s, None),
408            Value::Object(o) if o.len() == 1 => {
409                let res = o.values().next().map(|v| match v {
410                    Value::Object(obj) if obj.contains_key("0") => {
411                        let len = obj.len();
412                        Value::Array(
413                            (0..len)
414                                .map(|i| obj.get(&i.to_string()).unwrap().clone())
415                                .collect::<Vec<_>>(),
416                        )
417                    }
418                    _ => v.clone(),
419                });
420                (o.keys().next().unwrap(), res)
421            }
422            _ => todo!(),
423        };
424        let case = union
425            .cases
426            .iter()
427            .find(|c| {
428                let name = match c {
429                    ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
430                    ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
431                };
432                enum_case == &name.to_utf8_string_lossy()
433            })
434            .ok_or_else(|| {
435                Error::EnumCase(enum_case.to_string(), union.name.to_utf8_string_lossy())
436            })?;
437
438        let mut res = vec![ScVal::Symbol(ScSymbol(
439            enum_case.try_into().map_err(Error::Xdr)?,
440        ))];
441
442        match (case, rest) {
443            (ScSpecUdtUnionCaseV0::VoidV0(_), _) | (ScSpecUdtUnionCaseV0::TupleV0(_), None) => (),
444            (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), Some(arr))
445                if type_.len() == 1 =>
446            {
447                res.push(self.from_json(&arr, &type_[0])?);
448            }
449            (
450                ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }),
451                Some(Value::Array(arr)),
452            ) => {
453                res.extend(
454                    arr.iter()
455                        .zip(type_.iter())
456                        .map(|(elem, ty)| self.from_json(elem, ty))
457                        .collect::<Result<Vec<_>, _>>()?,
458                );
459            }
460            (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { .. }), Some(_)) => {}
461        }
462        Ok(ScVal::Vec(Some(res.try_into().map_err(Error::Xdr)?)))
463    }
464
465    fn parse_tuple(
466        &self,
467        t: &ScType,
468        tuple: &ScSpecTypeTuple,
469        items: &[Value],
470    ) -> Result<ScVal, Error> {
471        let ScSpecTypeTuple { value_types } = tuple;
472        if items.len() != value_types.len() {
473            return Err(Error::InvalidValue(Some(t.clone())));
474        }
475        let parsed: Result<Vec<ScVal>, Error> = items
476            .iter()
477            .zip(value_types.iter())
478            .map(|(item, t)| self.from_json(item, t))
479            .collect();
480        let converted: ScVec = parsed?.try_into().map_err(Error::Xdr)?;
481        Ok(ScVal::Vec(Some(converted)))
482    }
483
484    fn parse_map(
485        &self,
486        map: &ScSpecTypeMap,
487        value_map: &serde_json::Map<String, Value>,
488    ) -> Result<ScVal, Error> {
489        let ScSpecTypeMap {
490            key_type,
491            value_type,
492        } = map;
493        // TODO: What do we do if the expected key_type is not a string or symbol?
494        let parsed: Result<Vec<ScMapEntry>, Error> = value_map
495            .iter()
496            .map(|(k, v)| -> Result<ScMapEntry, Error> {
497                let key = self.from_string(k, key_type)?;
498                let val = self.from_json(v, value_type)?;
499                Ok(ScMapEntry { key, val })
500            })
501            .collect();
502        Ok(ScVal::Map(Some(
503            ScMap::sorted_from(parsed?).map_err(Error::Xdr)?,
504        )))
505    }
506}
507
508impl Spec {
509    /// # Errors
510    ///
511    /// Might return `Error::InvalidValue`
512    ///
513    /// # Panics
514    ///
515    /// May panic
516    pub fn xdr_to_json(&self, val: &ScVal, output: &ScType) -> Result<Value, Error> {
517        Ok(match (val, output) {
518            (ScVal::Void, ScType::Val | ScType::Option(_) | ScType::Tuple(_))
519            | (ScVal::Map(None) | ScVal::Vec(None), ScType::Option(_)) => Value::Null,
520            (ScVal::Bool(_), ScType::Bool)
521            | (ScVal::Void, ScType::Void)
522            | (ScVal::String(_), ScType::String)
523            | (ScVal::Symbol(_), ScType::Symbol)
524            | (ScVal::U64(_), ScType::U64)
525            | (ScVal::I64(_), ScType::I64)
526            | (ScVal::U32(_), ScType::U32)
527            | (ScVal::I32(_), ScType::I32)
528            | (ScVal::U128(_), ScType::U128)
529            | (ScVal::I128(_), ScType::I128)
530            | (ScVal::U256(_), ScType::U256)
531            | (ScVal::I256(_), ScType::I256)
532            | (ScVal::Duration(_), ScType::Duration)
533            | (ScVal::Timepoint(_), ScType::Timepoint)
534            | (
535                ScVal::ContractInstance(_)
536                | ScVal::LedgerKeyContractInstance
537                | ScVal::LedgerKeyNonce(_),
538                _,
539            )
540            | (ScVal::Address(_), ScType::Address)
541            | (ScVal::Bytes(_), ScType::Bytes | ScType::BytesN(_)) => to_json(val)?,
542
543            (val, ScType::Result(inner)) => self.xdr_to_json(val, &inner.ok_type)?,
544
545            (val, ScType::Option(inner)) => self.xdr_to_json(val, &inner.value_type)?,
546            (ScVal::Map(Some(_)) | ScVal::Vec(Some(_)) | ScVal::U32(_), type_) => {
547                self.sc_object_to_json(val, type_)?
548            }
549
550            (ScVal::Error(_), ScType::Error) => todo!(),
551            (v, typed) => todo!("{v:#?} doesn't have a matching {typed:#?}"),
552        })
553    }
554
555    /// # Errors
556    ///
557    /// Might return an error
558    pub fn vec_m_to_json<const MAX: u32>(
559        &self,
560        vec_m: &VecM<ScVal, MAX>,
561        type_: &ScType,
562    ) -> Result<Value, Error> {
563        Ok(Value::Array(
564            vec_m
565                .to_vec()
566                .iter()
567                .map(|sc_val| self.xdr_to_json(sc_val, type_))
568                .collect::<Result<Vec<_>, Error>>()?,
569        ))
570    }
571
572    /// # Errors
573    ///
574    /// Might return an error
575    pub fn sc_map_to_json(&self, sc_map: &ScMap, type_: &ScSpecTypeMap) -> Result<Value, Error> {
576        let v = sc_map
577            .iter()
578            .map(|ScMapEntry { key, val }| {
579                let key_s = self.xdr_to_json(key, &type_.key_type)?.to_string();
580                let val_value = self.xdr_to_json(val, &type_.value_type)?;
581                Ok((key_s, val_value))
582            })
583            .collect::<Result<serde_json::Map<String, Value>, Error>>()?;
584        Ok(Value::Object(v))
585    }
586
587    /// # Errors
588    ///
589    /// Might return an error
590    ///
591    /// # Panics
592    ///
593    /// May panic
594    pub fn udt_to_json(&self, name: &StringM<60>, sc_obj: &ScVal) -> Result<Value, Error> {
595        let name = &name.to_utf8_string_lossy();
596        let udt = self.find(name)?;
597        Ok(match (sc_obj, udt) {
598            (ScVal::Map(Some(map)), ScSpecEntry::UdtStructV0(strukt)) => serde_json::Value::Object(
599                strukt
600                    .fields
601                    .iter()
602                    .zip(map.iter())
603                    .map(|(field, entry)| {
604                        let val = self.xdr_to_json(&entry.val, &field.type_)?;
605                        Ok((field.name.to_utf8_string_lossy(), val))
606                    })
607                    .collect::<Result<serde_json::Map<String, _>, Error>>()?,
608            ),
609            (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtStructV0(strukt)) => Value::Array(
610                strukt
611                    .fields
612                    .iter()
613                    .zip(vec_.iter())
614                    .map(|(field, entry)| self.xdr_to_json(entry, &field.type_))
615                    .collect::<Result<Vec<_>, Error>>()?,
616            ),
617            (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtUnionV0(union)) => {
618                let v = vec_.to_vec();
619                // let val = &v[0];
620                let (first, rest) = match v.split_at(1) {
621                    ([first], []) => (first, None),
622                    ([first], rest) => (first, Some(rest)),
623                    _ => return Err(Error::IllFormedEnum(union.name.to_utf8_string_lossy())),
624                };
625
626                let ScVal::Symbol(case_name) = first else {
627                    return Err(Error::EnumFirstValueNotSymbol);
628                };
629                let case = union
630                    .cases
631                    .iter()
632                    .find(|case| {
633                        let name = match case {
634                            ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
635                            ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
636                        };
637                        name.as_vec() == case_name.as_vec()
638                    })
639                    .ok_or_else(|| Error::FailedToFindEnumCase(case_name.to_utf8_string_lossy()))?;
640
641                let case_name = case_name.to_utf8_string_lossy();
642                match case {
643                    ScSpecUdtUnionCaseV0::TupleV0(v) => {
644                        let rest = rest.ok_or_else(|| {
645                            Error::EnumMissingSecondValue(
646                                union.name.to_utf8_string_lossy(),
647                                case_name.clone(),
648                            )
649                        })?;
650                        let val = if v.type_.len() == 1 {
651                            self.xdr_to_json(&rest[0], &v.type_[0])?
652                        } else {
653                            Value::Array(
654                                v.type_
655                                    .iter()
656                                    .zip(rest.iter())
657                                    .map(|(type_, val)| self.xdr_to_json(val, type_))
658                                    .collect::<Result<Vec<_>, Error>>()?,
659                            )
660                        };
661
662                        Value::Object([(case_name, val)].into_iter().collect())
663                    }
664                    ScSpecUdtUnionCaseV0::VoidV0(_) => Value::String(case_name),
665                }
666            }
667            (ScVal::U32(v), ScSpecEntry::UdtEnumV0(_enum_)) => {
668                Value::Number(serde_json::Number::from(*v))
669            }
670            (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
671        })
672    }
673
674    /// # Errors
675    ///
676    /// Might return an error
677    ///
678    /// # Panics
679    ///
680    /// Some types are not yet supported and will cause a panic if supplied
681    pub fn sc_object_to_json(&self, val: &ScVal, spec_type: &ScType) -> Result<Value, Error> {
682        Ok(match (val, spec_type) {
683            (ScVal::Vec(Some(ScVec(vec_m))), ScType::Vec(type_)) => {
684                self.vec_m_to_json(vec_m, &type_.element_type)?
685            }
686            (ScVal::Vec(Some(ScVec(vec_m))), ScType::Tuple(tuple_type)) => Value::Array(
687                vec_m
688                    .iter()
689                    .zip(tuple_type.value_types.iter())
690                    .map(|(v, t)| self.xdr_to_json(v, t))
691                    .collect::<Result<Vec<_>, _>>()?,
692            ),
693            (
694                sc_obj @ (ScVal::Vec(_) | ScVal::Map(_) | ScVal::U32(_)),
695                ScType::Udt(ScSpecTypeUdt { name }),
696            ) => self.udt_to_json(name, sc_obj)?,
697
698            (ScVal::Map(Some(map)), ScType::Map(map_type)) => self.sc_map_to_json(map, map_type)?,
699
700            (ScVal::U64(u64_), ScType::U64) => Value::Number(serde_json::Number::from(*u64_)),
701
702            (ScVal::I64(i64_), ScType::I64) => Value::Number(serde_json::Number::from(*i64_)),
703            (int @ ScVal::U128(_), ScType::U128) => {
704                // Always output u128s as strings
705                let v: u128 = int
706                    .clone()
707                    .try_into()
708                    .map_err(|()| Error::InvalidValue(Some(ScType::U128)))?;
709                Value::String(v.to_string())
710            }
711
712            (int @ ScVal::I128(_), ScType::I128) => {
713                // Always output u128s as strings
714                let v: i128 = int
715                    .clone()
716                    .try_into()
717                    .map_err(|()| Error::InvalidValue(Some(ScType::I128)))?;
718                Value::String(v.to_string())
719            }
720
721            (ScVal::Bytes(v), ScType::Bytes | ScType::BytesN(_)) => {
722                Value::String(to_lower_hex(v.as_slice()))
723            }
724
725            (ScVal::Bytes(_), ScType::Udt(_)) => todo!(),
726
727            (ScVal::ContractInstance(_), _) => todo!(),
728
729            (ScVal::Address(v), ScType::Address) => sc_address_to_json(v),
730
731            (ok_val, ScType::Result(result_type)) => {
732                let ScSpecTypeResult { ok_type, .. } = result_type.as_ref();
733                self.xdr_to_json(ok_val, ok_type)?
734            }
735
736            (x, y) => return Err(Error::InvalidPair(x.clone(), y.clone())),
737        })
738    }
739}
740
741/// # Errors
742///
743/// Might return an error
744pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
745    Spec::from_string_primitive(s, t)
746}
747
748fn parse_const_enum(num: &serde_json::Number, enum_: &ScSpecUdtEnumV0) -> Result<ScVal, Error> {
749    let num = num
750        .as_u64()
751        .ok_or_else(|| Error::FailedNumConversion(num.clone()))?;
752    let num = u32::try_from(num).map_err(|_| Error::EnumConstTooLarge(num))?;
753    enum_
754        .cases
755        .iter()
756        .find(|c| c.value == num)
757        .ok_or(Error::EnumConst(num))
758        .map(|c| ScVal::U32(c.value))
759}
760
761/// # Errors
762///
763/// Might return an error
764#[allow(clippy::too_many_lines)]
765pub fn from_json_primitives(v: &Value, t: &ScType) -> Result<ScVal, Error> {
766    let val: ScVal = match (t, v) {
767        // Boolean parsing
768        (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true),
769        (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false),
770
771        // Number parsing
772        (ScType::U128, Value::String(s)) => {
773            let val: u128 = u128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
774            let bytes = val.to_be_bytes();
775            let (hi, lo) = bytes.split_at(8);
776            ScVal::U128(UInt128Parts {
777                hi: u64::from_be_bytes(hi.try_into()?),
778                lo: u64::from_be_bytes(lo.try_into()?),
779            })
780        }
781
782        (ScType::I128, Value::String(s)) => {
783            let val: i128 = i128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
784            let bytes = val.to_be_bytes();
785            let (hi, lo) = bytes.split_at(8);
786            ScVal::I128(Int128Parts {
787                hi: i64::from_be_bytes(hi.try_into()?),
788                lo: u64::from_be_bytes(lo.try_into()?),
789            })
790        }
791
792        // Number parsing
793        (ScType::U256, Value::String(s)) => {
794            let (hi, lo) = ethnum::U256::from_str_prefixed(s)?.into_words();
795            let hi_bytes = hi.to_be_bytes();
796            let (hi_hi, hi_lo) = hi_bytes.split_at(8);
797            let lo_bytes = lo.to_be_bytes();
798            let (lo_hi, lo_lo) = lo_bytes.split_at(8);
799            ScVal::U256(UInt256Parts {
800                hi_hi: u64::from_be_bytes(hi_hi.try_into()?),
801                hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
802                lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
803                lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
804            })
805        }
806        (ScType::I256, Value::String(s)) => {
807            let (hi, lo) = ethnum::I256::from_str_prefixed(s)?.into_words();
808            let hi_bytes = hi.to_be_bytes();
809            let (hi_hi, hi_lo) = hi_bytes.split_at(8);
810            let lo_bytes = lo.to_be_bytes();
811            let (lo_hi, lo_lo) = lo_bytes.split_at(8);
812            ScVal::I256(Int256Parts {
813                hi_hi: i64::from_be_bytes(hi_hi.try_into()?),
814                hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
815                lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
816                lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
817            })
818        }
819
820        (ScType::I32, Value::Number(n)) => ScVal::I32(
821            n.as_i64()
822                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
823                .try_into()
824                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
825        ),
826        (ScType::U32, Value::Number(n)) => ScVal::U32(
827            n.as_u64()
828                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
829                .try_into()
830                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
831        ),
832        (ScType::I64, Value::Number(n)) => ScVal::I64(
833            n.as_i64()
834                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
835        ),
836        (ScType::U64 | ScType::Timepoint | ScType::Duration, Value::Number(n)) => ScVal::U64(
837            n.as_u64()
838                .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
839        ),
840
841        // Symbol parsing
842        (ScType::Symbol, Value::String(s)) => ScVal::Symbol(ScSymbol(
843            s.as_bytes()
844                .try_into()
845                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
846        )),
847
848        (ScType::Address, Value::String(s)) => sc_address_from_json(s)?,
849
850        // Bytes parsing
851        (bytes @ ScType::BytesN(_), Value::Number(n)) => {
852            from_json_primitives(&Value::String(format!("{n}")), bytes)?
853        }
854        (ScType::BytesN(bytes), Value::String(s)) => ScVal::Bytes(ScBytes({
855            if bytes.n == 32 {
856                // Bytes might be a strkey, try parsing it as one. Contract devs should use the new
857                // proper Address type, but for backwards compatibility some contracts might use a
858                // BytesN<32> to represent an Address.
859                if let Ok(key) = sc_address_from_json(s) {
860                    return Ok(key);
861                }
862            }
863            // Bytes are not an address, just parse as a hex string
864            utils::padded_hex_from_str(s, bytes.n as usize)
865                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
866                .try_into()
867                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
868        })),
869        (ScType::Bytes, Value::Number(n)) => {
870            from_json_primitives(&Value::String(format!("{n}")), &ScType::Bytes)?
871        }
872        (ScType::Bytes, Value::String(s)) => ScVal::Bytes(
873            hex::decode(s)
874                .map_err(|_| Error::InvalidValue(Some(t.clone())))?
875                .try_into()
876                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
877        ),
878        (ScType::Bytes | ScType::BytesN(_), Value::Array(raw)) => {
879            let b: Result<Vec<u8>, Error> = raw
880                .iter()
881                .map(|item| {
882                    item.as_u64()
883                        .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
884                        .try_into()
885                        .map_err(|_| Error::InvalidValue(Some(t.clone())))
886                })
887                .collect();
888            let converted: BytesM<{ u32::MAX }> = b?.try_into().map_err(Error::Xdr)?;
889            ScVal::Bytes(ScBytes(converted))
890        }
891
892        (ScType::String, Value::String(s)) => ScVal::String(ScString(
893            s.try_into()
894                .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
895        )),
896        // Todo make proper error Which shouldn't exist
897        (_, raw) => serde_json::from_value(raw.clone())?,
898    };
899    Ok(val)
900}
901
902/// # Errors
903///
904/// Might return an error
905pub fn to_string(v: &ScVal) -> Result<String, Error> {
906    #[allow(clippy::match_same_arms)]
907    Ok(match v {
908        // If symbols are a top-level thing we omit the wrapping quotes
909        // TODO: Decide if this is a good idea or not.
910        ScVal::Symbol(v) => std::str::from_utf8(v.as_slice())
911            .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
912            .to_string(),
913        ScVal::LedgerKeyContractInstance => "LedgerKeyContractInstance".to_string(),
914        _ => serde_json::to_string(&to_json(v)?)?,
915    })
916}
917
918/// # Errors
919///
920/// Might return an error
921#[allow(clippy::too_many_lines)]
922pub fn to_json(v: &ScVal) -> Result<Value, Error> {
923    #[allow(clippy::match_same_arms)]
924    let val: Value = match v {
925        ScVal::Bool(b) => Value::Bool(*b),
926        ScVal::Void => Value::Null,
927        ScVal::LedgerKeyContractInstance => Value::String("LedgerKeyContractInstance".to_string()),
928        ScVal::U64(v) => Value::Number(serde_json::Number::from(*v)),
929        ScVal::Timepoint(tp) => Value::Number(serde_json::Number::from(tp.0)),
930        ScVal::Duration(d) => Value::Number(serde_json::Number::from(d.0)),
931        ScVal::I64(v) => Value::Number(serde_json::Number::from(*v)),
932        ScVal::U32(v) => Value::Number(serde_json::Number::from(*v)),
933        ScVal::I32(v) => Value::Number(serde_json::Number::from(*v)),
934        ScVal::Symbol(v) => Value::String(
935            std::str::from_utf8(v.as_slice())
936                .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
937                .to_string(),
938        ),
939        ScVal::String(v) => Value::String(
940            std::str::from_utf8(v.as_slice())
941                .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
942                .to_string(),
943        ),
944        ScVal::Vec(v) => {
945            let values: Result<Vec<Value>, Error> = v.as_ref().map_or_else(
946                || Ok(vec![]),
947                |v| {
948                    v.iter()
949                        .map(|item| -> Result<Value, Error> { to_json(item) })
950                        .collect()
951                },
952            );
953            Value::Array(values?)
954        }
955        ScVal::Map(None) => Value::Object(serde_json::Map::with_capacity(0)),
956        ScVal::Map(Some(v)) => {
957            // TODO: What do we do if the key is not a string?
958            let mut m = serde_json::Map::<String, Value>::with_capacity(v.len());
959            for ScMapEntry { key, val } in v.iter() {
960                let k: String = to_string(key)?;
961                let v: Value = to_json(val).map_err(|_| Error::InvalidValue(None))?;
962                m.insert(k, v);
963            }
964            Value::Object(m)
965        }
966        ScVal::Bytes(v) => Value::String(to_lower_hex(v.as_slice())),
967        ScVal::Address(v) => sc_address_to_json(v),
968        ScVal::U128(n) => {
969            let hi: [u8; 8] = n.hi.to_be_bytes();
970            let lo: [u8; 8] = n.lo.to_be_bytes();
971            let bytes = [hi, lo].concat();
972            // Always output u128s as strings
973            let v = u128::from_be_bytes(
974                bytes
975                    .as_slice()
976                    .try_into()
977                    .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
978            )
979            .to_string();
980            Value::String(v)
981        }
982        ScVal::I128(n) => {
983            let hi: [u8; 8] = n.hi.to_be_bytes();
984            let lo: [u8; 8] = n.lo.to_be_bytes();
985            let bytes = [hi, lo].concat();
986            // Always output u128s as strings
987            let v = i128::from_be_bytes(
988                bytes
989                    .as_slice()
990                    .try_into()
991                    .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
992            )
993            .to_string();
994            Value::String(v)
995        }
996        ScVal::U256(u256parts) => {
997            let bytes = [
998                u256parts.hi_hi.to_be_bytes(),
999                u256parts.hi_lo.to_be_bytes(),
1000                u256parts.lo_hi.to_be_bytes(),
1001                u256parts.lo_lo.to_be_bytes(),
1002            ]
1003            .concat();
1004            let u256 = ethnum::U256::from_be_bytes(
1005                bytes
1006                    .as_slice()
1007                    .try_into()
1008                    .map_err(|_| Error::InvalidValue(Some(ScType::U256)))?,
1009            );
1010            Value::String(u256.to_string())
1011        }
1012        ScVal::I256(i256parts) => {
1013            let bytes = [
1014                i256parts.hi_hi.to_be_bytes(),
1015                i256parts.hi_lo.to_be_bytes(),
1016                i256parts.lo_hi.to_be_bytes(),
1017                i256parts.lo_lo.to_be_bytes(),
1018            ]
1019            .concat();
1020            let i256 = ethnum::I256::from_be_bytes(
1021                bytes
1022                    .as_slice()
1023                    .try_into()
1024                    .map_err(|_| Error::InvalidValue(Some(ScType::I256)))?,
1025            );
1026            Value::String(i256.to_string())
1027        }
1028        ScVal::ContractInstance(ScContractInstance {
1029            executable: ContractExecutable::Wasm(hash),
1030            ..
1031        }) => json!({ "hash": hash }),
1032        ScVal::ContractInstance(ScContractInstance {
1033            executable: ContractExecutable::StellarAsset,
1034            ..
1035        }) => json!({"SAC": true}),
1036        ScVal::LedgerKeyNonce(ScNonceKey { nonce }) => {
1037            Value::Number(serde_json::Number::from(*nonce))
1038        }
1039        ScVal::Error(e) => serde_json::to_value(e)?,
1040    };
1041    Ok(val)
1042}
1043
1044fn sc_address_to_json(v: &ScAddress) -> Value {
1045    match v {
1046        ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => {
1047            Value::String(stellar_strkey::ed25519::PublicKey(*k).to_string())
1048        }
1049        ScAddress::Contract(ContractId(h)) => {
1050            Value::String(stellar_strkey::Contract(h.clone().into()).to_string())
1051        }
1052        ScAddress::MuxedAccount(_) => todo!("MuxedAddress is not supported yet"),
1053        ScAddress::ClaimableBalance(_) => todo!("ClaimableBalance is not supported"),
1054        ScAddress::LiquidityPool(_) => todo!("LiquidityPool is not supported"),
1055    }
1056}
1057
1058fn sc_address_from_json(s: &str) -> Result<ScVal, Error> {
1059    stellar_strkey::Strkey::from_string(s)
1060        .map_err(|_| Error::InvalidValue(Some(ScType::Address)))
1061        .map(|parsed| match parsed {
1062            stellar_strkey::Strkey::PublicKeyEd25519(p) => Some(ScVal::Address(
1063                ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(p.0)))),
1064            )),
1065            stellar_strkey::Strkey::Contract(c) => {
1066                Some(ScVal::Address(ScAddress::Contract(ContractId(Hash(c.0)))))
1067            }
1068            _ => None,
1069        })?
1070        .ok_or(Error::InvalidValue(Some(ScType::Address)))
1071}
1072
1073fn to_lower_hex(bytes: &[u8]) -> String {
1074    let mut res = String::with_capacity(bytes.len());
1075    for b in bytes {
1076        let _ = write!(res, "{b:02x}");
1077    }
1078    res
1079}
1080
1081impl Spec {
1082    #[must_use]
1083    pub fn arg_value_name(&self, type_: &ScType, depth: usize) -> Option<String> {
1084        match type_ {
1085            ScType::U64 => Some("u64".to_string()),
1086            ScType::I64 => Some("i64".to_string()),
1087            ScType::U128 => Some("u128".to_string()),
1088            ScType::I128 => Some("i128".to_string()),
1089            ScType::U32 => Some("u32".to_string()),
1090            ScType::I32 => Some("i32".to_string()),
1091            ScType::Bool => Some("bool".to_string()),
1092            ScType::Symbol => Some("Symbol".to_string()),
1093            ScType::Error => Some("Error".to_string()),
1094            ScType::Bytes => Some("hex_bytes".to_string()),
1095            ScType::Address => Some("Address".to_string()),
1096            ScType::Void => Some("Null".to_string()),
1097            ScType::Timepoint => Some("Timepoint".to_string()),
1098            ScType::Duration => Some("Duration".to_string()),
1099            ScType::U256 => Some("u256".to_string()),
1100            ScType::I256 => Some("i256".to_string()),
1101            ScType::String => Some("String".to_string()),
1102            ScType::MuxedAddress => Some("MuxedAddress".to_string()),
1103            ScType::Option(val) => {
1104                let ScSpecTypeOption { value_type } = val.as_ref();
1105                let inner = self.arg_value_name(value_type.as_ref(), depth + 1)?;
1106                Some(format!("Option<{inner}>"))
1107            }
1108            ScType::Vec(val) => {
1109                let ScSpecTypeVec { element_type } = val.as_ref();
1110                let inner = self.arg_value_name(element_type.as_ref(), depth + 1)?;
1111                Some(format!("Array<{inner}>"))
1112            }
1113            ScType::Result(val) => {
1114                let ScSpecTypeResult {
1115                    ok_type,
1116                    error_type,
1117                } = val.as_ref();
1118                let ok = self.arg_value_name(ok_type.as_ref(), depth + 1)?;
1119                let error = self.arg_value_name(error_type.as_ref(), depth + 1)?;
1120                Some(format!("Result<{ok}, {error}>"))
1121            }
1122            ScType::Tuple(val) => {
1123                let ScSpecTypeTuple { value_types } = val.as_ref();
1124                let names = value_types
1125                    .iter()
1126                    .map(|t| self.arg_value_name(t, depth + 1))
1127                    .collect::<Option<Vec<_>>>()?
1128                    .join(", ");
1129                Some(format!("Tuple<{names}>"))
1130            }
1131            ScType::Map(val) => {
1132                let ScSpecTypeMap {
1133                    key_type,
1134                    value_type,
1135                } = val.as_ref();
1136                let (key, val) = (
1137                    self.arg_value_name(key_type.as_ref(), depth + 1)?,
1138                    self.arg_value_name(value_type.as_ref(), depth + 1)?,
1139                );
1140                Some(format!("Map<{key}, {val}>"))
1141            }
1142            ScType::BytesN(t) => Some(format!("{}_hex_bytes", t.n)),
1143            ScType::Udt(ScSpecTypeUdt { name }) => {
1144                match self.find(&name.to_utf8_string_lossy()).ok()? {
1145                    ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. })
1146                        if fields
1147                            .first()
1148                            .is_some_and(|f| f.name.to_utf8_string_lossy() == "0") =>
1149                    {
1150                        let fields = fields
1151                            .iter()
1152                            .map(|t| self.arg_value_name(&t.type_, depth + 1))
1153                            .collect::<Option<Vec<_>>>()?
1154                            .join(", ");
1155                        Some(format!("[{fields}]"))
1156                    }
1157                    ScSpecEntry::UdtStructV0(strukt) => self.arg_value_udt(strukt, depth),
1158                    ScSpecEntry::UdtUnionV0(union) => self.arg_value_union(union, depth),
1159                    ScSpecEntry::UdtEnumV0(enum_) => Some(arg_value_enum(enum_)),
1160                    ScSpecEntry::FunctionV0(_)
1161                    | ScSpecEntry::UdtErrorEnumV0(_)
1162                    | ScSpecEntry::EventV0(_) => None,
1163                }
1164            }
1165            // No specific value name for these yet.
1166            ScType::Val => None,
1167        }
1168    }
1169
1170    fn arg_value_udt(&self, strukt: &ScSpecUdtStructV0, depth: usize) -> Option<String> {
1171        let inner = strukt
1172            .fields
1173            .iter()
1174            .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1175            .map(|(name, type_)| {
1176                let type_ = self.arg_value_name(type_, depth + 1)?;
1177                Some(format!("{name}: {type_}"))
1178            })
1179            .collect::<Option<Vec<_>>>()?
1180            .join(", ");
1181        Some(format!("{{ {inner} }}"))
1182    }
1183
1184    fn arg_value_union(&self, union: &ScSpecUdtUnionV0, depth: usize) -> Option<String> {
1185        union
1186            .cases
1187            .iter()
1188            .map(|f| {
1189                Some(match f {
1190                    ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1191                        name.to_utf8_string_lossy()
1192                    }
1193                    ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1194                        name,
1195                        type_,
1196                        ..
1197                    }) => format!(
1198                        "{}({})",
1199                        name.to_utf8_string_lossy(),
1200                        type_
1201                            .iter()
1202                            .map(|type_| self.arg_value_name(type_, depth + 1))
1203                            .collect::<Option<Vec<String>>>()?
1204                            .join(",")
1205                    ),
1206                })
1207            })
1208            .collect::<Option<Vec<_>>>()
1209            .map(|v| v.join(" | "))
1210    }
1211}
1212
1213fn arg_value_enum(enum_: &ScSpecUdtEnumV0) -> String {
1214    enum_
1215        .cases
1216        .iter()
1217        .map(|case| case.value.to_string())
1218        .join(" | ")
1219}
1220
1221// Example implementation
1222impl Spec {
1223    #[must_use]
1224    pub fn example(&self, type_: &ScType) -> Option<String> {
1225        #[allow(clippy::match_same_arms)]
1226        match type_ {
1227            ScType::U64 => Some("1".to_string()),
1228            ScType::I64 => Some("1".to_string()),
1229            ScType::U128 => Some("\"1\"".to_string()),
1230            ScType::I128 => Some("\"1\"".to_string()),
1231            ScType::U32 => Some("1".to_string()),
1232            ScType::I32 => Some("1".to_string()),
1233            ScType::Bool => Some("true".to_string()),
1234            ScType::Symbol => Some("\"hello\"".to_string()),
1235            ScType::Error => Some("Error".to_string()),
1236            ScType::Bytes => Some("\"0000000000000000\"".to_string()),
1237            ScType::Address => {
1238                Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1239            }
1240            ScType::Void => Some("null".to_string()),
1241            ScType::Timepoint => Some("1743010492".to_string()),
1242            ScType::Duration => Some("1".to_string()),
1243            ScType::U256 => Some("\"1\"".to_string()),
1244            ScType::I256 => Some("\"1\"".to_string()),
1245            ScType::String => Some("\"hello world\"".to_string()),
1246            ScType::Option(val) => {
1247                let ScSpecTypeOption { value_type } = val.as_ref();
1248                self.example(value_type.as_ref())
1249            }
1250            ScType::Vec(val) => {
1251                let ScSpecTypeVec { element_type } = val.as_ref();
1252                let inner = self.example(element_type.as_ref())?;
1253                Some(format!("[ {inner} ]"))
1254            }
1255            ScType::Result(val) => {
1256                let ScSpecTypeResult {
1257                    ok_type,
1258                    error_type,
1259                } = val.as_ref();
1260                let ok = self.example(ok_type.as_ref())?;
1261                let error = self.example(error_type.as_ref())?;
1262                Some(format!("Result<{ok}, {error}>"))
1263            }
1264            ScType::Tuple(val) => {
1265                let ScSpecTypeTuple { value_types } = val.as_ref();
1266                let names = value_types
1267                    .iter()
1268                    .map(|t| self.example(t))
1269                    .collect::<Option<Vec<_>>>()?
1270                    .join(", ");
1271                Some(format!("[{names}]"))
1272            }
1273            ScType::Map(map) => {
1274                let ScSpecTypeMap {
1275                    key_type,
1276                    value_type,
1277                } = map.as_ref();
1278                let (mut key, val) = (
1279                    self.example(key_type.as_ref())?,
1280                    self.example(value_type.as_ref())?,
1281                );
1282                if !matches!(key_type.as_ref(), ScType::Symbol) {
1283                    key = format!("\"{key}\"");
1284                }
1285                Some(format!("{{ {key}: {val} }}"))
1286            }
1287            ScType::BytesN(n) => {
1288                let n = n.n as usize;
1289                let res = "00".repeat(n);
1290                Some(format!("\"{res}\""))
1291            }
1292            ScType::Udt(ScSpecTypeUdt { name }) => {
1293                self.example_udts(name.to_utf8_string_lossy().as_ref())
1294            }
1295            ScType::MuxedAddress => {
1296                Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1297            }
1298            // No specific value name for these yet.
1299            ScType::Val => None,
1300        }
1301    }
1302
1303    fn example_udts(&self, name: &str) -> Option<String> {
1304        match self.find(name).ok() {
1305            Some(ScSpecEntry::UdtStructV0(strukt)) => {
1306                // Check if a tuple strukt
1307                if !strukt.fields.is_empty() && strukt.fields[0].name.to_utf8_string_lossy() == "0"
1308                {
1309                    let value_types = strukt
1310                        .fields
1311                        .iter()
1312                        .map(|f| f.type_.clone())
1313                        .collect::<Vec<_>>()
1314                        .try_into()
1315                        .ok()?;
1316                    return self.example(&ScType::Tuple(Box::new(ScSpecTypeTuple { value_types })));
1317                }
1318                let inner = strukt
1319                    .fields
1320                    .iter()
1321                    .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1322                    .map(|(name, type_)| {
1323                        let type_ = self.example(type_)?;
1324                        let name = format!(r#""{name}""#);
1325                        Some(format!("{name}: {type_}"))
1326                    })
1327                    .collect::<Option<Vec<_>>>()?
1328                    .join(", ");
1329                Some(format!(r"{{ {inner} }}"))
1330            }
1331            Some(ScSpecEntry::UdtUnionV0(union)) => self.example_union(union),
1332            Some(ScSpecEntry::UdtEnumV0(enum_)) => {
1333                enum_.cases.iter().next().map(|c| c.value.to_string())
1334            }
1335            Some(
1336                ScSpecEntry::FunctionV0(_)
1337                | ScSpecEntry::UdtErrorEnumV0(_)
1338                | ScSpecEntry::EventV0(_),
1339            )
1340            | None => None,
1341        }
1342    }
1343
1344    fn example_union(&self, union: &ScSpecUdtUnionV0) -> Option<String> {
1345        let res = union
1346            .cases
1347            .iter()
1348            .map(|case| match case {
1349                ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1350                    Some(format!("\"{}\"", name.to_utf8_string_lossy()))
1351                }
1352                ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1353                    name, type_, ..
1354                }) => {
1355                    if type_.len() == 1 {
1356                        let single = self.example(&type_[0])?;
1357                        Some(format!("{{\"{}\":{single}}}", name.to_utf8_string_lossy()))
1358                    } else {
1359                        let names = type_
1360                            .iter()
1361                            .map(|t| self.example(t))
1362                            .collect::<Option<Vec<_>>>()?
1363                            .join(", ");
1364                        Some(format!("{{\"{}\":[{names}]}}", name.to_utf8_string_lossy()))
1365                    }
1366                }
1367            })
1368            .collect::<Option<Vec<_>>>()?;
1369        Some(res.join("|"))
1370    }
1371}
1372
1373#[cfg(test)]
1374mod tests {
1375    use super::*;
1376
1377    use stellar_xdr::curr::ScSpecTypeBytesN;
1378
1379    #[test]
1380    fn from_json_primitives_bytesn() {
1381        // TODO: Add test for parsing addresses
1382
1383        // Check it parses hex-encoded bytes
1384        let b = from_json_primitives(
1385            &Value::String("beefface".to_string()),
1386            &ScType::BytesN(ScSpecTypeBytesN { n: 4 }),
1387        )
1388        .unwrap();
1389        assert_eq!(
1390            b,
1391            ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1392        );
1393
1394        // Check it parses hex-encoded bytes when they are all numbers. Normally the json would
1395        // interpret the CLI arg as a number, so we need a special case there.
1396        let b = from_json_primitives(
1397            &Value::Number(4554.into()),
1398            &ScType::BytesN(ScSpecTypeBytesN { n: 2 }),
1399        )
1400        .unwrap();
1401        assert_eq!(
1402            b,
1403            ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1404        );
1405    }
1406
1407    #[test]
1408    fn from_json_primitives_bytes() {
1409        // Check it parses hex-encoded bytes
1410        let b =
1411            from_json_primitives(&Value::String("beefface".to_string()), &ScType::Bytes).unwrap();
1412        assert_eq!(
1413            b,
1414            ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1415        );
1416
1417        // Check it parses hex-encoded bytes when they are all numbers. Normally the json would
1418        // interpret the CLI arg as a number, so we need a special case there.
1419        let b = from_json_primitives(&Value::Number(4554.into()), &ScType::Bytes).unwrap();
1420        assert_eq!(
1421            b,
1422            ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1423        );
1424    }
1425
1426    #[test]
1427    fn test_sc_address_from_json_strkey() {
1428        // All zero contract address
1429        match sc_address_from_json("CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4") {
1430            Ok(addr) => assert_eq!(
1431                addr,
1432                ScVal::Address(ScAddress::Contract(ContractId(Hash([0; 32]))))
1433            ),
1434            Err(e) => panic!("Unexpected error: {e}"),
1435        }
1436
1437        // Real contract address
1438        match sc_address_from_json("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") {
1439            Ok(addr) => assert_eq!(
1440                addr,
1441                ScVal::Address(ScAddress::Contract(ContractId::from(Hash([
1442                    0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7,
1443                    0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c,
1444                    0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1445                ]))))
1446            ),
1447            Err(e) => panic!("Unexpected error: {e}"),
1448        }
1449
1450        // All zero user account address
1451        match sc_address_from_json("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF") {
1452            Ok(addr) => assert_eq!(
1453                addr,
1454                ScVal::Address(ScAddress::Account(AccountId(
1455                    PublicKey::PublicKeyTypeEd25519([0; 32].into())
1456                )))
1457            ),
1458            Err(e) => panic!("Unexpected error: {e}"),
1459        }
1460
1461        // Real user account address
1462        match sc_address_from_json("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") {
1463            Ok(addr) => assert_eq!(
1464                addr,
1465                ScVal::Address(ScAddress::Account(AccountId(
1466                    PublicKey::PublicKeyTypeEd25519(
1467                        [
1468                            0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88,
1469                            0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0,
1470                            0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1471                        ]
1472                        .into()
1473                    )
1474                )))
1475            ),
1476            Err(e) => panic!("Unexpected error: {e}"),
1477        }
1478    }
1479
1480    #[test]
1481    fn test_u128_conversions() {
1482        // Test basic positive value
1483        let val = 42u128;
1484        let json_val = Value::String(val.to_string());
1485        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1486        assert_eq!(to_json(&scval).unwrap(), json_val);
1487
1488        // Test zero
1489        let val = 0u128;
1490        let json_val = Value::String(val.to_string());
1491        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1492        assert_eq!(to_json(&scval).unwrap(), json_val);
1493
1494        // Test maximum value
1495        let val = u128::MAX;
1496        let json_val = Value::String(val.to_string());
1497        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1498        assert_eq!(to_json(&scval).unwrap(), json_val);
1499
1500        // Test large value
1501        let val = 340_282_366_920_938_463_463_374_607_431_768_211_455u128;
1502        let json_val = Value::String(val.to_string());
1503        let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1504        assert_eq!(to_json(&scval).unwrap(), json_val);
1505    }
1506
1507    #[test]
1508    fn test_i128_conversions() {
1509        // Test basic positive value
1510        let val = 42i128;
1511        let json_val = Value::String(val.to_string());
1512        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1513        assert_eq!(to_json(&scval).unwrap(), json_val);
1514
1515        // Test basic negative value
1516        let val = -42i128;
1517        let json_val = Value::String(val.to_string());
1518        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1519        assert_eq!(to_json(&scval).unwrap(), json_val);
1520
1521        // Test zero
1522        let val = 0i128;
1523        let json_val = Value::String(val.to_string());
1524        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1525        assert_eq!(to_json(&scval).unwrap(), json_val);
1526
1527        // Test maximum value
1528        let val = i128::MAX;
1529        let json_val = Value::String(val.to_string());
1530        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1531        assert_eq!(to_json(&scval).unwrap(), json_val);
1532
1533        // Test minimum value
1534        let val = i128::MIN;
1535        let json_val = Value::String(val.to_string());
1536        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1537        assert_eq!(to_json(&scval).unwrap(), json_val);
1538
1539        // Test large negative value
1540        let val = -170_141_183_460_469_231_731_687_303_715_884_105_728i128;
1541        let json_val = Value::String(val.to_string());
1542        let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1543        assert_eq!(to_json(&scval).unwrap(), json_val);
1544    }
1545
1546    #[test]
1547    fn test_u256_conversions() {
1548        // Test basic positive value
1549        let val = "42";
1550        let json_val = Value::String(val.to_string());
1551        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1552        assert_eq!(to_json(&scval).unwrap(), json_val);
1553
1554        // Test zero
1555        let val = "0";
1556        let json_val = Value::String(val.to_string());
1557        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1558        assert_eq!(to_json(&scval).unwrap(), json_val);
1559
1560        // Test large value
1561        let val = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
1562        let json_val = Value::String(val.to_string());
1563        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1564        assert_eq!(to_json(&scval).unwrap(), json_val);
1565
1566        // Test hex format
1567        let val = "0xff";
1568        let json_val = Value::String(val.to_string());
1569        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1570        let expected = Value::String("255".to_string());
1571        assert_eq!(to_json(&scval).unwrap(), expected);
1572
1573        // Test hex format with 0x prefix
1574        let val = "0x1234567890abcdef";
1575        let json_val = Value::String(val.to_string());
1576        let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1577        let expected = Value::String("1311768467294899695".to_string());
1578        assert_eq!(to_json(&scval).unwrap(), expected);
1579    }
1580
1581    #[test]
1582    fn test_i256_conversions() {
1583        // Test basic positive value
1584        let val = "42";
1585        let json_val = Value::String(val.to_string());
1586        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1587        assert_eq!(to_json(&scval).unwrap(), json_val);
1588
1589        // Test basic negative value
1590        let val = "-42";
1591        let json_val = Value::String(val.to_string());
1592        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1593        assert_eq!(to_json(&scval).unwrap(), json_val);
1594
1595        // Test zero
1596        let val = "0";
1597        let json_val = Value::String(val.to_string());
1598        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1599        assert_eq!(to_json(&scval).unwrap(), json_val);
1600
1601        // Test large positive value
1602        let val = "57896044618658097711785492504343953926634992332820282019728792003956564819967";
1603        let json_val = Value::String(val.to_string());
1604        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1605        assert_eq!(to_json(&scval).unwrap(), json_val);
1606
1607        // Test large negative value
1608        let val = "-57896044618658097711785492504343953926634992332820282019728792003956564819968";
1609        let json_val = Value::String(val.to_string());
1610        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1611        assert_eq!(to_json(&scval).unwrap(), json_val);
1612
1613        // Test hex format
1614        let val = "0xff";
1615        let json_val = Value::String(val.to_string());
1616        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1617        let expected = Value::String("255".to_string());
1618        assert_eq!(to_json(&scval).unwrap(), expected);
1619
1620        // Test negative hex format
1621        let val = "-0xff";
1622        let json_val = Value::String(val.to_string());
1623        let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1624        let expected = Value::String("-255".to_string());
1625        assert_eq!(to_json(&scval).unwrap(), expected);
1626    }
1627
1628    #[test]
1629    fn test_integer_conversion_edge_cases() {
1630        // Test invalid string for U128
1631        let json_val = Value::String("not_a_number".to_string());
1632        let result = from_json_primitives(&json_val, &ScType::U128);
1633        assert!(result.is_err());
1634
1635        // Test invalid string for I128
1636        let json_val = Value::String("not_a_number".to_string());
1637        let result = from_json_primitives(&json_val, &ScType::I128);
1638        assert!(result.is_err());
1639
1640        // Test invalid string for U256
1641        let json_val = Value::String("not_a_number".to_string());
1642        let result = from_json_primitives(&json_val, &ScType::U256);
1643        assert!(result.is_err());
1644
1645        // Test invalid string for I256
1646        let json_val = Value::String("not_a_number".to_string());
1647        let result = from_json_primitives(&json_val, &ScType::I256);
1648        assert!(result.is_err());
1649
1650        // Test negative value for U128 (should fail)
1651        let json_val = Value::String("-42".to_string());
1652        let result = from_json_primitives(&json_val, &ScType::U128);
1653        assert!(result.is_err());
1654
1655        // Test negative value for U256 (should fail)
1656        let json_val = Value::String("-42".to_string());
1657        let result = from_json_primitives(&json_val, &ScType::U256);
1658        assert!(result.is_err());
1659    }
1660
1661    #[test]
1662    fn test_basic_numeric_conversions() {
1663        // Test U32
1664        let val = 42u32;
1665        let json_val = Value::Number(val.into());
1666        let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1667        assert_eq!(to_json(&scval).unwrap(), json_val);
1668
1669        // Test I32
1670        let val = -42i32;
1671        let json_val = Value::Number(val.into());
1672        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1673        assert_eq!(to_json(&scval).unwrap(), json_val);
1674
1675        // Test U64
1676        let val = 42u64;
1677        let json_val = Value::Number(val.into());
1678        let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1679        assert_eq!(to_json(&scval).unwrap(), json_val);
1680
1681        // Test I64
1682        let val = -42i64;
1683        let json_val = Value::Number(val.into());
1684        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1685        assert_eq!(to_json(&scval).unwrap(), json_val);
1686
1687        // Test boolean
1688        let scval = from_json_primitives(&Value::Bool(true), &ScType::Bool).unwrap();
1689        assert_eq!(to_json(&scval).unwrap(), Value::Bool(true));
1690
1691        let scval = from_json_primitives(&Value::Bool(false), &ScType::Bool).unwrap();
1692        assert_eq!(to_json(&scval).unwrap(), Value::Bool(false));
1693    }
1694
1695    #[test]
1696    fn test_numeric_extremes() {
1697        // Test U32 maximum
1698        let val = u32::MAX;
1699        let json_val = Value::Number(val.into());
1700        let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1701        assert_eq!(to_json(&scval).unwrap(), json_val);
1702
1703        // Test I32 minimum and maximum
1704        let val = i32::MIN;
1705        let json_val = Value::Number(val.into());
1706        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1707        assert_eq!(to_json(&scval).unwrap(), json_val);
1708
1709        let val = i32::MAX;
1710        let json_val = Value::Number(val.into());
1711        let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1712        assert_eq!(to_json(&scval).unwrap(), json_val);
1713
1714        // Test U64 maximum
1715        let val = u64::MAX;
1716        let json_val = Value::Number(val.into());
1717        let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1718        assert_eq!(to_json(&scval).unwrap(), json_val);
1719
1720        // Test I64 minimum and maximum
1721        let val = i64::MIN;
1722        let json_val = Value::Number(val.into());
1723        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1724        assert_eq!(to_json(&scval).unwrap(), json_val);
1725
1726        let val = i64::MAX;
1727        let json_val = Value::Number(val.into());
1728        let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1729        assert_eq!(to_json(&scval).unwrap(), json_val);
1730    }
1731
1732    #[test]
1733    fn test_string_primitive_integration() {
1734        // Test that from_string_primitive works with various types
1735        // U128
1736        let val = "42";
1737        let scval = from_string_primitive(val, &ScType::U128).unwrap();
1738        assert_eq!(to_string(&scval).unwrap(), "\"42\"");
1739
1740        // I128
1741        let val = "-42";
1742        let scval = from_string_primitive(val, &ScType::I128).unwrap();
1743        assert_eq!(to_string(&scval).unwrap(), "\"-42\"");
1744
1745        // U256
1746        let val = "12345678901234567890123456789012345678901234567890";
1747        let scval = from_string_primitive(val, &ScType::U256).unwrap();
1748        assert_eq!(
1749            to_string(&scval).unwrap(),
1750            "\"12345678901234567890123456789012345678901234567890\""
1751        );
1752
1753        // I256
1754        let val = "-12345678901234567890123456789012345678901234567890";
1755        let scval = from_string_primitive(val, &ScType::I256).unwrap();
1756        assert_eq!(
1757            to_string(&scval).unwrap(),
1758            "\"-12345678901234567890123456789012345678901234567890\""
1759        );
1760
1761        // Boolean
1762        let scval = from_string_primitive("true", &ScType::Bool).unwrap();
1763        assert_eq!(to_string(&scval).unwrap(), "true");
1764
1765        let scval = from_string_primitive("false", &ScType::Bool).unwrap();
1766        assert_eq!(to_string(&scval).unwrap(), "false");
1767    }
1768}