1#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
2use std::str::FromStr;
3
4use itertools::Itertools;
5use serde_json::{json, Value};
6use stellar_xdr::curr::{
7 AccountId, BytesM, ContractExecutable, Error as XdrError, Hash, Int128Parts, Int256Parts,
8 PublicKey, ScAddress, ScBytes, ScContractInstance, ScMap, ScMapEntry, ScNonceKey, ScSpecEntry,
9 ScSpecFunctionV0, ScSpecTypeDef as ScType, ScSpecTypeMap, ScSpecTypeOption, ScSpecTypeResult,
10 ScSpecTypeTuple, ScSpecTypeUdt, ScSpecTypeVec, ScSpecUdtEnumV0, ScSpecUdtErrorEnumCaseV0,
11 ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionCaseTupleV0, ScSpecUdtUnionCaseV0,
12 ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0, ScString, ScSymbol, ScVal, ScVec, StringM,
13 UInt128Parts, UInt256Parts, Uint256, VecM,
14};
15
16pub mod contract;
17pub mod utils;
18
19#[derive(thiserror::Error, Debug)]
20pub enum Error {
21 #[error("an unknown error occurred")]
22 Unknown,
23 #[error("Invalid pair {0:#?} {1:#?}")]
24 InvalidPair(ScVal, ScType),
25 #[error("value is not parseable to {0:#?}")]
26 InvalidValue(Option<ScType>),
27 #[error("Unknown case {0} for {1}")]
28 EnumCase(String, String),
29 #[error("Enum {0} missing value for type {1}")]
30 EnumMissingSecondValue(String, String),
31 #[error("Enum {0} is illformed")]
32 IllFormedEnum(String),
33 #[error("Unknown const case {0}")]
34 EnumConst(u32),
35 #[error("Enum const value must be a u32 or smaller")]
36 EnumConstTooLarge(u64),
37 #[error("Missing Entry {0}")]
38 MissingEntry(String),
39 #[error("Missing Spec")]
40 MissingSpec,
41 #[error(transparent)]
42 Xdr(XdrError),
43 #[error(transparent)]
44 Serde(#[from] serde_json::Error),
45 #[error(transparent)]
46 Ethnum(#[from] core::num::ParseIntError),
47
48 #[error("Missing key {0} in map")]
49 MissingKey(String),
50 #[error("Failed to convert {0} to number")]
51 FailedNumConversion(serde_json::Number),
52 #[error("First argument in an enum must be a sybmol")]
53 EnumFirstValueNotSymbol,
54 #[error("Failed to find enum case {0}")]
55 FailedToFindEnumCase(String),
56 #[error(transparent)]
57 FailedSilceToByte(#[from] std::array::TryFromSliceError),
58 #[error(transparent)]
59 Infallible(#[from] std::convert::Infallible),
60 #[error("Missing Error case {0}")]
61 MissingErrorCase(u32),
62 #[error(transparent)]
63 Spec(#[from] soroban_spec::read::FromWasmError),
64 #[error(transparent)]
65 Base64Spec(#[from] soroban_spec::read::ParseSpecBase64Error),
66}
67
68#[derive(Default, Clone)]
69pub struct Spec(pub Option<Vec<ScSpecEntry>>);
70
71impl TryInto<Spec> for &[u8] {
72 type Error = soroban_spec::read::FromWasmError;
73
74 fn try_into(self) -> Result<Spec, Self::Error> {
75 let spec = soroban_spec::read::from_wasm(self)?;
76 Ok(Spec::new(spec))
77 }
78}
79
80impl Spec {
81 pub fn new(entries: Vec<ScSpecEntry>) -> Self {
82 Self(Some(entries))
83 }
84
85 pub fn from_wasm(wasm: &[u8]) -> Result<Spec, Error> {
86 let spec = soroban_spec::read::from_wasm(wasm)?;
87 Ok(Spec::new(spec))
88 }
89
90 pub fn parse_base64(base64: &str) -> Result<Spec, Error> {
91 let spec = soroban_spec::read::parse_base64(base64.as_bytes())?;
92 Ok(Spec::new(spec))
93 }
94}
95
96impl Spec {
97 pub fn doc(&self, name: &str, type_: &ScType) -> Result<Option<&'static str>, Error> {
100 let mut str = match type_ {
101 ScType::Val
102 | ScType::U64
103 | ScType::I64
104 | ScType::U128
105 | ScType::I128
106 | ScType::U32
107 | ScType::I32
108 | ScType::Result(_)
109 | ScType::Vec(_)
110 | ScType::Map(_)
111 | ScType::Tuple(_)
112 | ScType::BytesN(_)
113 | ScType::Symbol
114 | ScType::Error
115 | ScType::Bytes
116 | ScType::Void
117 | ScType::Timepoint
118 | ScType::Duration
119 | ScType::U256
120 | ScType::I256
121 | ScType::String
122 | ScType::Bool => String::new(),
123 ScType::Address => String::from(
124 "Can be public key (G13..), a contract hash (6c45307) or an identity (alice), ",
125 ),
126 ScType::Option(type_) => return self.doc(name, &type_.value_type),
127 ScType::Udt(ScSpecTypeUdt { name }) => {
128 let spec_type = self.find(&name.to_utf8_string_lossy())?;
129 match spec_type {
130 ScSpecEntry::FunctionV0(ScSpecFunctionV0 { doc, .. })
131 | ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { doc, .. })
132 | ScSpecEntry::UdtUnionV0(ScSpecUdtUnionV0 { doc, .. })
133 | ScSpecEntry::UdtEnumV0(ScSpecUdtEnumV0 { doc, .. })
134 | ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { doc, .. }) => doc,
135 }
136 .to_utf8_string_lossy()
137 }
138 };
139 if let Some(mut ex) = self.example(type_) {
140 if ex.contains(' ') {
141 ex = format!("'{ex}'");
142 } else if ex.contains('"') {
143 ex = ex.replace('"', "");
144 }
145 if matches!(type_, ScType::Bool) {
146 ex = String::new();
147 }
148 let sep = if str.is_empty() { "" } else { "\n" };
149 str = format!("{str}{sep}Example:\n --{name} {ex}");
150 if ex.contains('"') {}
151 }
152 if str.is_empty() {
153 Ok(None)
154 } else {
155 Ok(Some(Box::leak(str.into_boxed_str())))
156 }
157 }
158
159 pub fn find(&self, name: &str) -> Result<&ScSpecEntry, Error> {
163 self.0
164 .as_ref()
165 .and_then(|specs| {
166 specs.iter().find(|e| {
167 let entry_name = match e {
168 ScSpecEntry::FunctionV0(x) => x.name.to_utf8_string_lossy(),
169 ScSpecEntry::UdtStructV0(x) => x.name.to_utf8_string_lossy(),
170 ScSpecEntry::UdtUnionV0(x) => x.name.to_utf8_string_lossy(),
171 ScSpecEntry::UdtEnumV0(x) => x.name.to_utf8_string_lossy(),
172 ScSpecEntry::UdtErrorEnumV0(x) => x.name.to_utf8_string_lossy(),
173 };
174 name == entry_name
175 })
176 })
177 .ok_or_else(|| Error::MissingEntry(name.to_owned()))
178 }
179
180 pub fn find_function(&self, name: &str) -> Result<&ScSpecFunctionV0, Error> {
184 match self.find(name)? {
185 ScSpecEntry::FunctionV0(f) => Ok(f),
186 _ => Err(Error::MissingEntry(name.to_owned())),
187 }
188 }
189 pub fn find_functions(&self) -> Result<impl Iterator<Item = &ScSpecFunctionV0>, Error> {
193 Ok(self
194 .0
195 .as_ref()
196 .ok_or(Error::MissingSpec)?
197 .iter()
198 .filter_map(|e| match e {
199 ScSpecEntry::FunctionV0(x) => Some(x),
200 _ => None,
201 }))
202 }
203
204 pub fn find_error_type(&self, value: u32) -> Result<&ScSpecUdtErrorEnumCaseV0, Error> {
207 if let ScSpecEntry::UdtErrorEnumV0(ScSpecUdtErrorEnumV0 { cases, .. }) =
208 self.find("Error")?
209 {
210 if let Some(case) = cases.iter().find(|case| value == case.value) {
211 return Ok(case);
212 }
213 }
214 Err(Error::MissingErrorCase(value))
215 }
216
217 pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
221 Self::default().from_string(s, t)
222 }
223
224 #[allow(clippy::wrong_self_convention)]
228 pub fn from_string(&self, s: &str, t: &ScType) -> Result<ScVal, Error> {
229 if let ScType::Option(b) = t {
230 if s == "null" {
231 return Ok(ScVal::Void);
232 }
233 let ScSpecTypeOption { value_type } = b.as_ref().clone();
234 let v = value_type.as_ref().clone();
235 return self.from_string(s, &v);
236 }
237 serde_json::from_str(s)
239 .map_or_else(
240 |e| match t {
241 ScType::Symbol
242 | ScType::String
243 | ScType::Bytes
244 | ScType::BytesN(_)
245 | ScType::U256
246 | ScType::I256
247 | ScType::U128
248 | ScType::I128
249 | ScType::Address => Ok(Value::String(s.to_owned())),
250 ScType::Udt(ScSpecTypeUdt { name })
251 if matches!(
252 self.find(&name.to_utf8_string_lossy())?,
253 ScSpecEntry::UdtUnionV0(_) | ScSpecEntry::UdtStructV0(_)
254 ) =>
255 {
256 Ok(Value::String(s.to_owned()))
257 }
258 _ => Err(Error::Serde(e)),
259 },
260 |val| match t {
261 ScType::U128 | ScType::I128 | ScType::U256 | ScType::I256 => {
262 Ok(Value::String(s.to_owned()))
263 }
264 _ => Ok(val),
265 },
266 )
267 .and_then(|raw| self.from_json(&raw, t))
268 }
269
270 #[allow(clippy::wrong_self_convention)]
274 pub fn from_json(&self, v: &Value, t: &ScType) -> Result<ScVal, Error> {
275 let val: ScVal = match (t, v) {
276 (
277 ScType::Bool
278 | ScType::U128
279 | ScType::I128
280 | ScType::U256
281 | ScType::I256
282 | ScType::I32
283 | ScType::I64
284 | ScType::U32
285 | ScType::U64
286 | ScType::String
287 | ScType::Symbol
288 | ScType::Address
289 | ScType::Bytes
290 | ScType::BytesN(_),
291 _,
292 ) => from_json_primitives(v, t)?,
293
294 (ScType::Vec(elem), Value::Array(raw)) => {
296 let converted: ScVec = raw
297 .iter()
298 .map(|item| self.from_json(item, &elem.element_type))
299 .collect::<Result<Vec<ScVal>, Error>>()?
300 .try_into()
301 .map_err(Error::Xdr)?;
302 ScVal::Vec(Some(converted))
303 }
304
305 (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?,
307
308 (ScType::Option(_), Value::Null) => ScVal::Void,
310 (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?,
311
312 (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?,
314
315 (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?,
317
318 (_, raw) => serde_json::from_value(raw.clone()).map_err(Error::Serde)?,
320 };
321 Ok(val)
322 }
323
324 fn parse_udt(&self, name: &StringM<60>, value: &Value) -> Result<ScVal, Error> {
325 let name = &name.to_utf8_string_lossy();
326 match (self.find(name)?, value) {
327 (ScSpecEntry::UdtStructV0(strukt), Value::Object(map)) => {
328 if strukt
329 .fields
330 .iter()
331 .any(|f| f.name.to_utf8_string_lossy() == "0")
332 {
333 self.parse_tuple_strukt(
334 strukt,
335 &(0..map.len())
336 .map(|i| map.get(&i.to_string()).unwrap().clone())
337 .collect::<Vec<_>>(),
338 )
339 } else {
340 self.parse_strukt(strukt, map)
341 }
342 }
343 (ScSpecEntry::UdtStructV0(strukt), Value::Array(arr)) => {
344 self.parse_tuple_strukt(strukt, arr)
345 }
346 (
347 ScSpecEntry::UdtUnionV0(union),
348 val @ (Value::Array(_) | Value::String(_) | Value::Object(_)),
349 ) => self.parse_union(union, val),
350 (ScSpecEntry::UdtEnumV0(enum_), Value::Number(num)) => parse_const_enum(num, enum_),
351 (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
352 }
353 }
354
355 fn parse_tuple_strukt(
356 &self,
357 strukt: &ScSpecUdtStructV0,
358 array: &[Value],
359 ) -> Result<ScVal, Error> {
360 let items = strukt
361 .fields
362 .to_vec()
363 .iter()
364 .zip(array.iter())
365 .map(|(f, v)| {
366 let val = self.from_json(v, &f.type_)?;
367 Ok(val)
368 })
369 .collect::<Result<Vec<_>, Error>>()?;
370 Ok(ScVal::Vec(Some(items.try_into().map_err(Error::Xdr)?)))
371 }
372
373 fn parse_strukt(
374 &self,
375 strukt: &ScSpecUdtStructV0,
376 map: &serde_json::Map<String, Value>,
377 ) -> Result<ScVal, Error> {
378 let items = strukt
379 .fields
380 .to_vec()
381 .iter()
382 .map(|f| {
383 let name = &f.name.to_utf8_string_lossy();
384 let v = map
385 .get(name)
386 .ok_or_else(|| Error::MissingKey(name.clone()))?;
387 let val = self.from_json(v, &f.type_)?;
388 let key = StringM::from_str(name).unwrap();
389 Ok(ScMapEntry {
390 key: ScVal::Symbol(key.into()),
391 val,
392 })
393 })
394 .collect::<Result<Vec<_>, Error>>()?;
395 let map = ScMap::sorted_from(items).map_err(Error::Xdr)?;
396 Ok(ScVal::Map(Some(map)))
397 }
398
399 fn parse_union(&self, union: &ScSpecUdtUnionV0, value: &Value) -> Result<ScVal, Error> {
400 let (enum_case, rest) = match value {
401 Value::String(s) => (s, None),
402 Value::Object(o) if o.len() == 1 => {
403 let res = o.values().next().map(|v| match v {
404 Value::Object(obj) if obj.contains_key("0") => {
405 let len = obj.len();
406 Value::Array(
407 (0..len)
408 .map(|i| obj.get(&i.to_string()).unwrap().clone())
409 .collect::<Vec<_>>(),
410 )
411 }
412 _ => v.clone(),
413 });
414 (o.keys().next().unwrap(), res)
415 }
416 _ => todo!(),
417 };
418 let case = union
419 .cases
420 .iter()
421 .find(|c| {
422 let name = match c {
423 ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
424 ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
425 };
426 enum_case == &name.to_utf8_string_lossy()
427 })
428 .ok_or_else(|| {
429 Error::EnumCase(enum_case.to_string(), union.name.to_utf8_string_lossy())
430 })?;
431
432 let mut res = vec![ScVal::Symbol(ScSymbol(
433 enum_case.try_into().map_err(Error::Xdr)?,
434 ))];
435
436 match (case, rest) {
437 (ScSpecUdtUnionCaseV0::VoidV0(_), _) | (ScSpecUdtUnionCaseV0::TupleV0(_), None) => (),
438 (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), Some(arr))
439 if type_.len() == 1 =>
440 {
441 res.push(self.from_json(&arr, &type_[0])?);
442 }
443 (
444 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }),
445 Some(Value::Array(arr)),
446 ) => {
447 res.extend(
448 arr.iter()
449 .zip(type_.iter())
450 .map(|(elem, ty)| self.from_json(elem, ty))
451 .collect::<Result<Vec<_>, _>>()?,
452 );
453 }
454 (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { .. }), Some(_)) => {}
455 };
456 Ok(ScVal::Vec(Some(res.try_into().map_err(Error::Xdr)?)))
457 }
458
459 fn parse_tuple(
460 &self,
461 t: &ScType,
462 tuple: &ScSpecTypeTuple,
463 items: &[Value],
464 ) -> Result<ScVal, Error> {
465 let ScSpecTypeTuple { value_types } = tuple;
466 if items.len() != value_types.len() {
467 return Err(Error::InvalidValue(Some(t.clone())));
468 };
469 let parsed: Result<Vec<ScVal>, Error> = items
470 .iter()
471 .zip(value_types.iter())
472 .map(|(item, t)| self.from_json(item, t))
473 .collect();
474 let converted: ScVec = parsed?.try_into().map_err(Error::Xdr)?;
475 Ok(ScVal::Vec(Some(converted)))
476 }
477
478 fn parse_map(
479 &self,
480 map: &ScSpecTypeMap,
481 value_map: &serde_json::Map<String, Value>,
482 ) -> Result<ScVal, Error> {
483 let ScSpecTypeMap {
484 key_type,
485 value_type,
486 } = map;
487 let parsed: Result<Vec<ScMapEntry>, Error> = value_map
489 .iter()
490 .map(|(k, v)| -> Result<ScMapEntry, Error> {
491 let key = self.from_string(k, key_type)?;
492 let val = self.from_json(v, value_type)?;
493 Ok(ScMapEntry { key, val })
494 })
495 .collect();
496 Ok(ScVal::Map(Some(
497 ScMap::sorted_from(parsed?).map_err(Error::Xdr)?,
498 )))
499 }
500}
501
502impl Spec {
503 pub fn xdr_to_json(&self, val: &ScVal, output: &ScType) -> Result<Value, Error> {
511 Ok(match (val, output) {
512 (ScVal::Void, ScType::Val | ScType::Option(_) | ScType::Tuple(_))
513 | (ScVal::Map(None) | ScVal::Vec(None), ScType::Option(_)) => Value::Null,
514 (ScVal::Bool(_), ScType::Bool)
515 | (ScVal::Void, ScType::Void)
516 | (ScVal::String(_), ScType::String)
517 | (ScVal::Symbol(_), ScType::Symbol)
518 | (ScVal::U64(_), ScType::U64)
519 | (ScVal::I64(_), ScType::I64)
520 | (ScVal::U32(_), ScType::U32)
521 | (ScVal::I32(_), ScType::I32)
522 | (ScVal::U128(_), ScType::U128)
523 | (ScVal::I128(_), ScType::I128)
524 | (ScVal::U256(_), ScType::U256)
525 | (ScVal::I256(_), ScType::I256)
526 | (ScVal::Duration(_), ScType::Duration)
527 | (ScVal::Timepoint(_), ScType::Timepoint)
528 | (
529 ScVal::ContractInstance(_)
530 | ScVal::LedgerKeyContractInstance
531 | ScVal::LedgerKeyNonce(_),
532 _,
533 )
534 | (ScVal::Address(_), ScType::Address)
535 | (ScVal::Bytes(_), ScType::Bytes | ScType::BytesN(_)) => to_json(val)?,
536
537 (val, ScType::Result(inner)) => self.xdr_to_json(val, &inner.ok_type)?,
538
539 (val, ScType::Option(inner)) => self.xdr_to_json(val, &inner.value_type)?,
540 (ScVal::Map(Some(_)) | ScVal::Vec(Some(_)) | ScVal::U32(_), type_) => {
541 self.sc_object_to_json(val, type_)?
542 }
543
544 (ScVal::Error(_), ScType::Error) => todo!(),
545 (v, typed) => todo!("{v:#?} doesn't have a matching {typed:#?}"),
546 })
547 }
548
549 pub fn vec_m_to_json<const MAX: u32>(
553 &self,
554 vec_m: &VecM<ScVal, MAX>,
555 type_: &ScType,
556 ) -> Result<Value, Error> {
557 Ok(Value::Array(
558 vec_m
559 .to_vec()
560 .iter()
561 .map(|sc_val| self.xdr_to_json(sc_val, type_))
562 .collect::<Result<Vec<_>, Error>>()?,
563 ))
564 }
565
566 pub fn sc_map_to_json(&self, sc_map: &ScMap, type_: &ScSpecTypeMap) -> Result<Value, Error> {
570 let v = sc_map
571 .iter()
572 .map(|ScMapEntry { key, val }| {
573 let key_s = self.xdr_to_json(key, &type_.key_type)?.to_string();
574 let val_value = self.xdr_to_json(val, &type_.value_type)?;
575 Ok((key_s, val_value))
576 })
577 .collect::<Result<serde_json::Map<String, Value>, Error>>()?;
578 Ok(Value::Object(v))
579 }
580
581 pub fn udt_to_json(&self, name: &StringM<60>, sc_obj: &ScVal) -> Result<Value, Error> {
589 let name = &name.to_utf8_string_lossy();
590 let udt = self.find(name)?;
591 Ok(match (sc_obj, udt) {
592 (ScVal::Map(Some(map)), ScSpecEntry::UdtStructV0(strukt)) => serde_json::Value::Object(
593 strukt
594 .fields
595 .iter()
596 .zip(map.iter())
597 .map(|(field, entry)| {
598 let val = self.xdr_to_json(&entry.val, &field.type_)?;
599 Ok((field.name.to_utf8_string_lossy(), val))
600 })
601 .collect::<Result<serde_json::Map<String, _>, Error>>()?,
602 ),
603 (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtStructV0(strukt)) => Value::Array(
604 strukt
605 .fields
606 .iter()
607 .zip(vec_.iter())
608 .map(|(field, entry)| self.xdr_to_json(entry, &field.type_))
609 .collect::<Result<Vec<_>, Error>>()?,
610 ),
611 (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtUnionV0(union)) => {
612 let v = vec_.to_vec();
613 let (first, rest) = match v.split_at(1) {
615 ([first], []) => (first, None),
616 ([first], rest) => (first, Some(rest)),
617 _ => return Err(Error::IllFormedEnum(union.name.to_utf8_string_lossy())),
618 };
619
620 let ScVal::Symbol(case_name) = first else {
621 return Err(Error::EnumFirstValueNotSymbol);
622 };
623 let case = union
624 .cases
625 .iter()
626 .find(|case| {
627 let name = match case {
628 ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
629 ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
630 };
631 name.as_vec() == case_name.as_vec()
632 })
633 .ok_or_else(|| Error::FailedToFindEnumCase(case_name.to_utf8_string_lossy()))?;
634
635 let case_name = case_name.to_utf8_string_lossy();
636 match case {
637 ScSpecUdtUnionCaseV0::TupleV0(v) => {
638 let rest = rest.ok_or_else(|| {
639 Error::EnumMissingSecondValue(
640 union.name.to_utf8_string_lossy(),
641 case_name.clone(),
642 )
643 })?;
644 let val = if v.type_.len() == 1 {
645 self.xdr_to_json(&rest[0], &v.type_[0])?
646 } else {
647 Value::Array(
648 v.type_
649 .iter()
650 .zip(rest.iter())
651 .map(|(type_, val)| self.xdr_to_json(val, type_))
652 .collect::<Result<Vec<_>, Error>>()?,
653 )
654 };
655
656 Value::Object([(case_name, val)].into_iter().collect())
657 }
658 ScSpecUdtUnionCaseV0::VoidV0(_) => Value::String(case_name),
659 }
660 }
661 (ScVal::U32(v), ScSpecEntry::UdtEnumV0(_enum_)) => {
662 Value::Number(serde_json::Number::from(*v))
663 }
664 (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
665 })
666 }
667
668 pub fn sc_object_to_json(&self, val: &ScVal, spec_type: &ScType) -> Result<Value, Error> {
676 Ok(match (val, spec_type) {
677 (ScVal::Vec(Some(ScVec(vec_m))), ScType::Vec(type_)) => {
678 self.vec_m_to_json(vec_m, &type_.element_type)?
679 }
680 (ScVal::Vec(Some(ScVec(vec_m))), ScType::Tuple(tuple_type)) => Value::Array(
681 vec_m
682 .iter()
683 .zip(tuple_type.value_types.iter())
684 .map(|(v, t)| self.xdr_to_json(v, t))
685 .collect::<Result<Vec<_>, _>>()?,
686 ),
687 (
688 sc_obj @ (ScVal::Vec(_) | ScVal::Map(_) | ScVal::U32(_)),
689 ScType::Udt(ScSpecTypeUdt { name }),
690 ) => self.udt_to_json(name, sc_obj)?,
691
692 (ScVal::Map(Some(map)), ScType::Map(map_type)) => self.sc_map_to_json(map, map_type)?,
693
694 (ScVal::U64(u64_), ScType::U64) => Value::Number(serde_json::Number::from(*u64_)),
695
696 (ScVal::I64(i64_), ScType::I64) => Value::Number(serde_json::Number::from(*i64_)),
697 (int @ ScVal::U128(_), ScType::U128) => {
698 let v: u128 = int
700 .clone()
701 .try_into()
702 .map_err(|()| Error::InvalidValue(Some(ScType::U128)))?;
703 Value::String(v.to_string())
704 }
705
706 (int @ ScVal::I128(_), ScType::I128) => {
707 let v: i128 = int
709 .clone()
710 .try_into()
711 .map_err(|()| Error::InvalidValue(Some(ScType::I128)))?;
712 Value::String(v.to_string())
713 }
714
715 (ScVal::Bytes(v), ScType::Bytes | ScType::BytesN(_)) => {
716 Value::String(to_lower_hex(v.as_slice()))
717 }
718
719 (ScVal::Bytes(_), ScType::Udt(_)) => todo!(),
720
721 (ScVal::ContractInstance(_), _) => todo!(),
722
723 (ScVal::Address(v), ScType::Address) => sc_address_to_json(v),
724
725 (ok_val, ScType::Result(result_type)) => {
726 let ScSpecTypeResult { ok_type, .. } = result_type.as_ref();
727 self.xdr_to_json(ok_val, ok_type)?
728 }
729
730 (x, y) => return Err(Error::InvalidPair(x.clone(), y.clone())),
731 })
732 }
733}
734
735pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
739 Spec::from_string_primitive(s, t)
740}
741
742fn parse_const_enum(num: &serde_json::Number, enum_: &ScSpecUdtEnumV0) -> Result<ScVal, Error> {
743 let num = num
744 .as_u64()
745 .ok_or_else(|| Error::FailedNumConversion(num.clone()))?;
746 let num = u32::try_from(num).map_err(|_| Error::EnumConstTooLarge(num))?;
747 enum_
748 .cases
749 .iter()
750 .find(|c| c.value == num)
751 .ok_or(Error::EnumConst(num))
752 .map(|c| ScVal::U32(c.value))
753}
754
755#[allow(clippy::too_many_lines)]
759pub fn from_json_primitives(v: &Value, t: &ScType) -> Result<ScVal, Error> {
760 let val: ScVal = match (t, v) {
761 (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true),
763 (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false),
764
765 (ScType::U128, Value::String(s)) => {
767 let val: u128 = u128::from_str(s)
768 .map(Into::into)
769 .map_err(|_| Error::InvalidValue(Some(t.clone())))?;
770 let bytes = val.to_be_bytes();
771 let (hi, lo) = bytes.split_at(8);
772 ScVal::U128(UInt128Parts {
773 hi: u64::from_be_bytes(hi.try_into()?),
774 lo: u64::from_be_bytes(lo.try_into()?),
775 })
776 }
777
778 (ScType::I128, Value::String(s)) => {
779 let val: i128 = i128::from_str(s)
780 .map(Into::into)
781 .map_err(|_| Error::InvalidValue(Some(t.clone())))?;
782 let bytes = val.to_be_bytes();
783 let (hi, lo) = bytes.split_at(8);
784 ScVal::I128(Int128Parts {
785 hi: i64::from_be_bytes(hi.try_into()?),
786 lo: u64::from_be_bytes(lo.try_into()?),
787 })
788 }
789
790 (ScType::U256, Value::String(s)) => {
792 let (hi, lo) = ethnum::U256::from_str_prefixed(s)?.into_words();
793 let hi_bytes = hi.to_be_bytes();
794 let (hi_hi, hi_lo) = hi_bytes.split_at(8);
795 let lo_bytes = lo.to_be_bytes();
796 let (lo_hi, lo_lo) = lo_bytes.split_at(8);
797 ScVal::U256(UInt256Parts {
798 hi_hi: u64::from_be_bytes(hi_hi.try_into()?),
799 hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
800 lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
801 lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
802 })
803 }
804 (ScType::I256, Value::String(s)) => {
805 let (hi, lo) = ethnum::I256::from_str_prefixed(s)?.into_words();
806 let hi_bytes = hi.to_be_bytes();
807 let (hi_hi, hi_lo) = hi_bytes.split_at(8);
808 let lo_bytes = lo.to_be_bytes();
809 let (lo_hi, lo_lo) = lo_bytes.split_at(8);
810 ScVal::I256(Int256Parts {
811 hi_hi: i64::from_be_bytes(hi_hi.try_into()?),
812 hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
813 lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
814 lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
815 })
816 }
817
818 (ScType::I32, Value::Number(n)) => ScVal::I32(
819 n.as_i64()
820 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
821 .try_into()
822 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
823 ),
824 (ScType::U32, Value::Number(n)) => ScVal::U32(
825 n.as_u64()
826 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
827 .try_into()
828 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
829 ),
830 (ScType::I64, Value::Number(n)) => ScVal::I64(
831 n.as_i64()
832 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
833 ),
834 (ScType::U64 | ScType::Timepoint | ScType::Duration, Value::Number(n)) => ScVal::U64(
835 n.as_u64()
836 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
837 ),
838
839 (ScType::Symbol, Value::String(s)) => ScVal::Symbol(ScSymbol(
841 s.as_bytes()
842 .try_into()
843 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
844 )),
845
846 (ScType::Address, Value::String(s)) => sc_address_from_json(s)?,
847
848 (bytes @ ScType::BytesN(_), Value::Number(n)) => {
850 from_json_primitives(&Value::String(format!("{n}")), bytes)?
851 }
852 (ScType::BytesN(bytes), Value::String(s)) => ScVal::Bytes(ScBytes({
853 if bytes.n == 32 {
854 if let Ok(key) = sc_address_from_json(s) {
858 return Ok(key);
859 }
860 }
861 utils::padded_hex_from_str(s, bytes.n as usize)
863 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
864 .try_into()
865 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
866 })),
867 (ScType::Bytes, Value::Number(n)) => {
868 from_json_primitives(&Value::String(format!("{n}")), &ScType::Bytes)?
869 }
870 (ScType::Bytes, Value::String(s)) => ScVal::Bytes(
871 hex::decode(s)
872 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
873 .try_into()
874 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
875 ),
876 (ScType::Bytes | ScType::BytesN(_), Value::Array(raw)) => {
877 let b: Result<Vec<u8>, Error> = raw
878 .iter()
879 .map(|item| {
880 item.as_u64()
881 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
882 .try_into()
883 .map_err(|_| Error::InvalidValue(Some(t.clone())))
884 })
885 .collect();
886 let converted: BytesM<{ u32::MAX }> = b?.try_into().map_err(Error::Xdr)?;
887 ScVal::Bytes(ScBytes(converted))
888 }
889
890 (ScType::String, Value::String(s)) => ScVal::String(ScString(
891 s.try_into()
892 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
893 )),
894 (_, raw) => serde_json::from_value(raw.clone())?,
896 };
897 Ok(val)
898}
899
900pub fn to_string(v: &ScVal) -> Result<String, Error> {
904 #[allow(clippy::match_same_arms)]
905 Ok(match v {
906 ScVal::Symbol(v) => std::str::from_utf8(v.as_slice())
909 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
910 .to_string(),
911 ScVal::LedgerKeyContractInstance => "LedgerKeyContractInstance".to_string(),
912 _ => serde_json::to_string(&to_json(v)?)?,
913 })
914}
915
916#[allow(clippy::too_many_lines)]
920pub fn to_json(v: &ScVal) -> Result<Value, Error> {
921 #[allow(clippy::match_same_arms)]
922 let val: Value = match v {
923 ScVal::Bool(b) => Value::Bool(*b),
924 ScVal::Void => Value::Null,
925 ScVal::LedgerKeyContractInstance => Value::String("LedgerKeyContractInstance".to_string()),
926 ScVal::U64(v) => Value::Number(serde_json::Number::from(*v)),
927 ScVal::Timepoint(tp) => Value::Number(serde_json::Number::from(tp.0)),
928 ScVal::Duration(d) => Value::Number(serde_json::Number::from(d.0)),
929 ScVal::I64(v) => Value::Number(serde_json::Number::from(*v)),
930 ScVal::U32(v) => Value::Number(serde_json::Number::from(*v)),
931 ScVal::I32(v) => Value::Number(serde_json::Number::from(*v)),
932 ScVal::Symbol(v) => Value::String(
933 std::str::from_utf8(v.as_slice())
934 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
935 .to_string(),
936 ),
937 ScVal::String(v) => Value::String(
938 std::str::from_utf8(v.as_slice())
939 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
940 .to_string(),
941 ),
942 ScVal::Vec(v) => {
943 let values: Result<Vec<Value>, Error> = v.as_ref().map_or_else(
944 || Ok(vec![]),
945 |v| {
946 v.iter()
947 .map(|item| -> Result<Value, Error> { to_json(item) })
948 .collect()
949 },
950 );
951 Value::Array(values?)
952 }
953 ScVal::Map(None) => Value::Object(serde_json::Map::with_capacity(0)),
954 ScVal::Map(Some(v)) => {
955 let mut m = serde_json::Map::<String, Value>::with_capacity(v.len());
957 for ScMapEntry { key, val } in v.iter() {
958 let k: String = to_string(key)?;
959 let v: Value = to_json(val).map_err(|_| Error::InvalidValue(None))?;
960 m.insert(k, v);
961 }
962 Value::Object(m)
963 }
964 ScVal::Bytes(v) => Value::String(to_lower_hex(v.as_slice())),
965 ScVal::Address(v) => sc_address_to_json(v),
966 ScVal::U128(n) => {
967 let hi: [u8; 8] = n.hi.to_be_bytes();
968 let lo: [u8; 8] = n.lo.to_be_bytes();
969 let bytes = [hi, lo].concat();
970 let v = u128::from_be_bytes(
972 bytes
973 .as_slice()
974 .try_into()
975 .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
976 )
977 .to_string();
978 Value::String(v)
979 }
980 ScVal::I128(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 let v = i128::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::U256(u256parts) => {
995 let bytes = [
996 u256parts.hi_hi.to_be_bytes(),
997 u256parts.hi_lo.to_be_bytes(),
998 u256parts.lo_hi.to_be_bytes(),
999 u256parts.lo_lo.to_be_bytes(),
1000 ]
1001 .concat();
1002 let u256 = ethnum::U256::from_be_bytes(
1003 bytes
1004 .as_slice()
1005 .try_into()
1006 .map_err(|_| Error::InvalidValue(Some(ScType::U256)))?,
1007 );
1008 Value::String(u256.to_string())
1009 }
1010 ScVal::I256(i256parts) => {
1011 let bytes = [
1012 i256parts.hi_hi.to_be_bytes(),
1013 i256parts.hi_lo.to_be_bytes(),
1014 i256parts.lo_hi.to_be_bytes(),
1015 i256parts.lo_lo.to_be_bytes(),
1016 ]
1017 .concat();
1018 let i256 = ethnum::I256::from_be_bytes(
1019 bytes
1020 .as_slice()
1021 .try_into()
1022 .map_err(|_| Error::InvalidValue(Some(ScType::I256)))?,
1023 );
1024 Value::String(i256.to_string())
1025 }
1026 ScVal::ContractInstance(ScContractInstance {
1027 executable: ContractExecutable::Wasm(hash),
1028 ..
1029 }) => json!({ "hash": hash }),
1030 ScVal::ContractInstance(ScContractInstance {
1031 executable: ContractExecutable::StellarAsset,
1032 ..
1033 }) => json!({"SAC": true}),
1034 ScVal::LedgerKeyNonce(ScNonceKey { nonce }) => {
1035 Value::Number(serde_json::Number::from(*nonce))
1036 }
1037 ScVal::Error(e) => serde_json::to_value(e)?,
1038 };
1039 Ok(val)
1040}
1041
1042fn sc_address_to_json(v: &ScAddress) -> Value {
1043 match v {
1044 ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => {
1045 Value::String(stellar_strkey::ed25519::PublicKey(*k).to_string())
1046 }
1047 ScAddress::Contract(Hash(h)) => Value::String(stellar_strkey::Contract(*h).to_string()),
1048 }
1049}
1050
1051fn sc_address_from_json(s: &str) -> Result<ScVal, Error> {
1052 stellar_strkey::Strkey::from_string(s)
1053 .map_err(|_| Error::InvalidValue(Some(ScType::Address)))
1054 .map(|parsed| match parsed {
1055 stellar_strkey::Strkey::PublicKeyEd25519(p) => Some(ScVal::Address(
1056 ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(p.0)))),
1057 )),
1058 stellar_strkey::Strkey::Contract(c) => {
1059 Some(ScVal::Address(ScAddress::Contract(Hash(c.0))))
1060 }
1061 _ => None,
1062 })?
1063 .ok_or(Error::InvalidValue(Some(ScType::Address)))
1064}
1065
1066fn to_lower_hex(bytes: &[u8]) -> String {
1067 let mut res = String::with_capacity(bytes.len());
1068 for b in bytes {
1069 res.push_str(&format!("{b:02x}"));
1070 }
1071 res
1072}
1073
1074impl Spec {
1075 #[must_use]
1076 pub fn arg_value_name(&self, type_: &ScType, depth: usize) -> Option<String> {
1077 match type_ {
1078 ScType::U64 => Some("u64".to_string()),
1079 ScType::I64 => Some("i64".to_string()),
1080 ScType::U128 => Some("u128".to_string()),
1081 ScType::I128 => Some("i128".to_string()),
1082 ScType::U32 => Some("u32".to_string()),
1083 ScType::I32 => Some("i32".to_string()),
1084 ScType::Bool => Some("bool".to_string()),
1085 ScType::Symbol => Some("Symbol".to_string()),
1086 ScType::Error => Some("Error".to_string()),
1087 ScType::Bytes => Some("hex_bytes".to_string()),
1088 ScType::Address => Some("Address".to_string()),
1089 ScType::Void => Some("Null".to_string()),
1090 ScType::Timepoint => Some("Timepoint".to_string()),
1091 ScType::Duration => Some("Duration".to_string()),
1092 ScType::U256 => Some("u256".to_string()),
1093 ScType::I256 => Some("i256".to_string()),
1094 ScType::String => Some("String".to_string()),
1095 ScType::Option(val) => {
1096 let ScSpecTypeOption { value_type } = val.as_ref();
1097 let inner = self.arg_value_name(value_type.as_ref(), depth + 1)?;
1098 Some(format!("Option<{inner}>"))
1099 }
1100 ScType::Vec(val) => {
1101 let ScSpecTypeVec { element_type } = val.as_ref();
1102 let inner = self.arg_value_name(element_type.as_ref(), depth + 1)?;
1103 Some(format!("Array<{inner}>"))
1104 }
1105 ScType::Result(val) => {
1106 let ScSpecTypeResult {
1107 ok_type,
1108 error_type,
1109 } = val.as_ref();
1110 let ok = self.arg_value_name(ok_type.as_ref(), depth + 1)?;
1111 let error = self.arg_value_name(error_type.as_ref(), depth + 1)?;
1112 Some(format!("Result<{ok}, {error}>"))
1113 }
1114 ScType::Tuple(val) => {
1115 let ScSpecTypeTuple { value_types } = val.as_ref();
1116 let names = value_types
1117 .iter()
1118 .map(|t| self.arg_value_name(t, depth + 1))
1119 .collect::<Option<Vec<_>>>()?
1120 .join(", ");
1121 Some(format!("Tuple<{names}>"))
1122 }
1123 ScType::Map(val) => {
1124 let ScSpecTypeMap {
1125 key_type,
1126 value_type,
1127 } = val.as_ref();
1128 let (key, val) = (
1129 self.arg_value_name(key_type.as_ref(), depth + 1)?,
1130 self.arg_value_name(value_type.as_ref(), depth + 1)?,
1131 );
1132 Some(format!("Map<{key}, {val}>"))
1133 }
1134 ScType::BytesN(t) => Some(format!("{}_hex_bytes", t.n)),
1135 ScType::Udt(ScSpecTypeUdt { name }) => {
1136 match self.find(&name.to_utf8_string_lossy()).ok()? {
1137 ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. })
1138 if fields
1139 .first()
1140 .is_some_and(|f| f.name.to_utf8_string_lossy() == "0") =>
1141 {
1142 let fields = fields
1143 .iter()
1144 .map(|t| self.arg_value_name(&t.type_, depth + 1))
1145 .collect::<Option<Vec<_>>>()?
1146 .join(", ");
1147 Some(format!("[{fields}]"))
1148 }
1149 ScSpecEntry::UdtStructV0(strukt) => self.arg_value_udt(strukt, depth),
1150 ScSpecEntry::UdtUnionV0(union) => self.arg_value_union(union, depth),
1151 ScSpecEntry::UdtEnumV0(enum_) => Some(arg_value_enum(enum_)),
1152 ScSpecEntry::FunctionV0(_) | ScSpecEntry::UdtErrorEnumV0(_) => None,
1153 }
1154 }
1155 ScType::Val => None,
1157 }
1158 }
1159
1160 fn arg_value_udt(&self, strukt: &ScSpecUdtStructV0, depth: usize) -> Option<String> {
1161 let inner = strukt
1162 .fields
1163 .iter()
1164 .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1165 .map(|(name, type_)| {
1166 let type_ = self.arg_value_name(type_, depth + 1)?;
1167 Some(format!("{name}: {type_}"))
1168 })
1169 .collect::<Option<Vec<_>>>()?
1170 .join(", ");
1171 Some(format!("{{ {inner} }}"))
1172 }
1173
1174 fn arg_value_union(&self, union: &ScSpecUdtUnionV0, depth: usize) -> Option<String> {
1175 union
1176 .cases
1177 .iter()
1178 .map(|f| {
1179 Some(match f {
1180 ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1181 name.to_utf8_string_lossy()
1182 }
1183 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1184 name,
1185 type_,
1186 ..
1187 }) => format!(
1188 "{}({})",
1189 name.to_utf8_string_lossy(),
1190 type_
1191 .iter()
1192 .map(|type_| self.arg_value_name(type_, depth + 1))
1193 .collect::<Option<Vec<String>>>()?
1194 .join(",")
1195 ),
1196 })
1197 })
1198 .collect::<Option<Vec<_>>>()
1199 .map(|v| v.join(" | "))
1200 }
1201}
1202
1203fn arg_value_enum(enum_: &ScSpecUdtEnumV0) -> String {
1204 enum_
1205 .cases
1206 .iter()
1207 .map(|case| case.value.to_string())
1208 .join(" | ")
1209}
1210
1211impl Spec {
1213 #[must_use]
1214 pub fn example(&self, type_: &ScType) -> Option<String> {
1215 match type_ {
1216 ScType::U64 => Some("42".to_string()),
1217 ScType::I64 => Some("-42".to_string()),
1218 ScType::U128 => Some("\"1000\"".to_string()),
1219 ScType::I128 => Some("\"-100\"".to_string()),
1220 ScType::U32 => Some("1".to_string()),
1221 ScType::I32 => Some("-1".to_string()),
1222 ScType::Bool => Some("true".to_string()),
1223 ScType::Symbol => Some("\"hello\"".to_string()),
1224 ScType::Error => Some("Error".to_string()),
1225 ScType::Bytes => Some("\"beefface123\"".to_string()),
1226 ScType::Address => {
1227 Some("\"GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4\"".to_string())
1228 }
1229 ScType::Void => Some("null".to_string()),
1230 ScType::Timepoint => Some("1234".to_string()),
1231 ScType::Duration => Some("9999".to_string()),
1232 ScType::U256 => Some("\"2000\"".to_string()),
1233 ScType::I256 => Some("\"-20000\"".to_string()),
1234 ScType::String => Some("\"hello world\"".to_string()),
1235 ScType::Option(val) => {
1236 let ScSpecTypeOption { value_type } = val.as_ref();
1237 self.example(value_type.as_ref())
1238 }
1239 ScType::Vec(val) => {
1240 let ScSpecTypeVec { element_type } = val.as_ref();
1241 let inner = self.example(element_type.as_ref())?;
1242 Some(format!("[ {inner} ]"))
1243 }
1244 ScType::Result(val) => {
1245 let ScSpecTypeResult {
1246 ok_type,
1247 error_type,
1248 } = val.as_ref();
1249 let ok = self.example(ok_type.as_ref())?;
1250 let error = self.example(error_type.as_ref())?;
1251 Some(format!("Result<{ok}, {error}>"))
1252 }
1253 ScType::Tuple(val) => {
1254 let ScSpecTypeTuple { value_types } = val.as_ref();
1255 let names = value_types
1256 .iter()
1257 .map(|t| self.example(t))
1258 .collect::<Option<Vec<_>>>()?
1259 .join(", ");
1260 Some(format!("[{names}]"))
1261 }
1262 ScType::Map(map) => {
1263 let ScSpecTypeMap {
1264 key_type,
1265 value_type,
1266 } = map.as_ref();
1267 let (mut key, val) = (
1268 self.example(key_type.as_ref())?,
1269 self.example(value_type.as_ref())?,
1270 );
1271 if !matches!(key_type.as_ref(), ScType::Symbol) {
1272 key = format!("\"{key}\"");
1273 }
1274 Some(format!("{{ {key}: {val} }}"))
1275 }
1276 ScType::BytesN(n) => {
1277 let n = n.n as usize;
1278 let res = if n % 2 == 0 {
1279 "ef".repeat(n)
1280 } else {
1281 let mut s = "ef".repeat(n - 1);
1282 s.push('e');
1283 s
1284 };
1285 Some(format!("\"{res}\""))
1286 }
1287 ScType::Udt(ScSpecTypeUdt { name }) => {
1288 self.example_udts(name.to_utf8_string_lossy().as_ref())
1289 }
1290 ScType::Val => None,
1292 }
1293 }
1294
1295 fn example_udts(&self, name: &str) -> Option<String> {
1296 match self.find(name).ok() {
1297 Some(ScSpecEntry::UdtStructV0(strukt)) => {
1298 if !strukt.fields.is_empty() && strukt.fields[0].name.to_utf8_string_lossy() == "0"
1300 {
1301 let value_types = strukt
1302 .fields
1303 .iter()
1304 .map(|f| f.type_.clone())
1305 .collect::<Vec<_>>()
1306 .try_into()
1307 .ok()?;
1308 return self.example(&ScType::Tuple(Box::new(ScSpecTypeTuple { value_types })));
1309 }
1310 let inner = strukt
1311 .fields
1312 .iter()
1313 .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1314 .map(|(name, type_)| {
1315 let type_ = self.example(type_)?;
1316 let name = format!(r#""{name}""#);
1317 Some(format!("{name}: {type_}"))
1318 })
1319 .collect::<Option<Vec<_>>>()?
1320 .join(", ");
1321 Some(format!(r#"{{ {inner} }}"#))
1322 }
1323 Some(ScSpecEntry::UdtUnionV0(union)) => self.example_union(union),
1324 Some(ScSpecEntry::UdtEnumV0(enum_)) => {
1325 enum_.cases.iter().next().map(|c| c.value.to_string())
1326 }
1327 Some(ScSpecEntry::FunctionV0(_) | ScSpecEntry::UdtErrorEnumV0(_)) | None => None,
1328 }
1329 }
1330
1331 fn example_union(&self, union: &ScSpecUdtUnionV0) -> Option<String> {
1332 let res = union
1333 .cases
1334 .iter()
1335 .map(|case| match case {
1336 ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1337 Some(format!("\"{}\"", name.to_utf8_string_lossy()))
1338 }
1339 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1340 name, type_, ..
1341 }) => {
1342 if type_.len() == 1 {
1343 let single = self.example(&type_[0])?;
1344 Some(format!("{{\"{}\":{single}}}", name.to_utf8_string_lossy()))
1345 } else {
1346 let names = type_
1347 .iter()
1348 .map(|t| self.example(t))
1349 .collect::<Option<Vec<_>>>()?
1350 .join(", ");
1351 Some(format!("{{\"{}\":[{names}]}}", name.to_utf8_string_lossy()))
1352 }
1353 }
1354 })
1355 .collect::<Option<Vec<_>>>()?;
1356 Some(res.join("|"))
1357 }
1358}
1359
1360#[cfg(test)]
1361mod tests {
1362 use super::*;
1363
1364 use stellar_xdr::curr::ScSpecTypeBytesN;
1365
1366 #[test]
1367 fn from_json_primitives_bytesn() {
1368 let b = from_json_primitives(
1372 &Value::String("beefface".to_string()),
1373 &ScType::BytesN(ScSpecTypeBytesN { n: 4 }),
1374 )
1375 .unwrap();
1376 assert_eq!(
1377 b,
1378 ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1379 );
1380
1381 let b = from_json_primitives(
1384 &Value::Number(4554.into()),
1385 &ScType::BytesN(ScSpecTypeBytesN { n: 2 }),
1386 )
1387 .unwrap();
1388 assert_eq!(
1389 b,
1390 ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1391 );
1392 }
1393
1394 #[test]
1395 fn from_json_primitives_bytes() {
1396 let b =
1398 from_json_primitives(&Value::String("beefface".to_string()), &ScType::Bytes).unwrap();
1399 assert_eq!(
1400 b,
1401 ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1402 );
1403
1404 let b = from_json_primitives(&Value::Number(4554.into()), &ScType::Bytes).unwrap();
1407 assert_eq!(
1408 b,
1409 ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1410 );
1411 }
1412
1413 #[test]
1414 fn test_sc_address_from_json_strkey() {
1415 match sc_address_from_json("CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4") {
1417 Ok(addr) => assert_eq!(addr, ScVal::Address(ScAddress::Contract(Hash([0; 32])))),
1418 Err(e) => panic!("Unexpected error: {e}"),
1419 }
1420
1421 match sc_address_from_json("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") {
1423 Ok(addr) => assert_eq!(
1424 addr,
1425 ScVal::Address(ScAddress::Contract(
1426 [
1427 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88,
1428 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0,
1429 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1430 ]
1431 .into()
1432 ))
1433 ),
1434 Err(e) => panic!("Unexpected error: {e}"),
1435 }
1436
1437 match sc_address_from_json("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF") {
1439 Ok(addr) => assert_eq!(
1440 addr,
1441 ScVal::Address(ScAddress::Account(AccountId(
1442 PublicKey::PublicKeyTypeEd25519([0; 32].into())
1443 )))
1444 ),
1445 Err(e) => panic!("Unexpected error: {e}"),
1446 }
1447
1448 match sc_address_from_json("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") {
1450 Ok(addr) => assert_eq!(
1451 addr,
1452 ScVal::Address(ScAddress::Account(AccountId(
1453 PublicKey::PublicKeyTypeEd25519(
1454 [
1455 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88,
1456 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0,
1457 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1458 ]
1459 .into()
1460 )
1461 )))
1462 ),
1463 Err(e) => panic!("Unexpected error: {e}"),
1464 }
1465 }
1466}