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 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 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 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 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 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 pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
227 Self::default().from_string(s, t)
228 }
229
230 #[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 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 #[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 (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 (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?,
313
314 (ScType::Option(_), Value::Null) => ScVal::Void,
316 (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?,
317
318 (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?,
320
321 (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?,
323
324 (_, 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 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 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 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 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 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 (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 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 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 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
741pub 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#[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 (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true),
769 (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false),
770
771 (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 (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 (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 @ 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 if let Ok(key) = sc_address_from_json(s) {
860 return Ok(key);
861 }
862 }
863 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 (_, raw) => serde_json::from_value(raw.clone())?,
898 };
899 Ok(val)
900}
901
902pub fn to_string(v: &ScVal) -> Result<String, Error> {
906 #[allow(clippy::match_same_arms)]
907 Ok(match v {
908 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#[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 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 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 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 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
1221impl 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let val = "42";
1737 let scval = from_string_primitive(val, &ScType::U128).unwrap();
1738 assert_eq!(to_string(&scval).unwrap(), "\"42\"");
1739
1740 let val = "-42";
1742 let scval = from_string_primitive(val, &ScType::I128).unwrap();
1743 assert_eq!(to_string(&scval).unwrap(), "\"-42\"");
1744
1745 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 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 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}