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