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