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 _ => Ok(val),
273 },
274 )
275 .and_then(|raw| self.from_json(&raw, t))
276 }
277
278 #[allow(clippy::wrong_self_convention)]
282 pub fn from_json(&self, v: &Value, t: &ScType) -> Result<ScVal, Error> {
283 let val: ScVal = match (t, v) {
284 (
285 ScType::Bool
286 | ScType::U128
287 | ScType::I128
288 | ScType::U256
289 | ScType::I256
290 | ScType::I32
291 | ScType::I64
292 | ScType::U32
293 | ScType::U64
294 | ScType::String
295 | ScType::Symbol
296 | ScType::Address
297 | ScType::MuxedAddress
298 | ScType::Bytes
299 | ScType::BytesN(_),
300 _,
301 ) => from_json_primitives(v, t)?,
302
303 (ScType::Vec(elem), Value::Array(raw)) => {
305 let converted: ScVec = raw
306 .iter()
307 .map(|item| self.from_json(item, &elem.element_type))
308 .collect::<Result<Vec<ScVal>, Error>>()?
309 .try_into()
310 .map_err(Error::Xdr)?;
311 ScVal::Vec(Some(converted))
312 }
313
314 (ScType::Map(map), Value::Object(raw)) => self.parse_map(map, raw)?,
316
317 (ScType::Option(_), Value::Null) => ScVal::Void,
319 (ScType::Option(elem), v) => self.from_json(v, &elem.value_type)?,
320
321 (ScType::Tuple(elem), Value::Array(raw)) => self.parse_tuple(t, elem, raw)?,
323
324 (ScType::Udt(ScSpecTypeUdt { name }), _) => self.parse_udt(name, v)?,
326
327 (_, raw) => serde_json::from_value(raw.clone()).map_err(Error::Serde)?,
329 };
330 Ok(val)
331 }
332
333 fn parse_udt(&self, name: &StringM<60>, value: &Value) -> Result<ScVal, Error> {
334 let name = &name.to_utf8_string_lossy();
335 match (self.find(name)?, value) {
336 (ScSpecEntry::UdtStructV0(strukt), Value::Object(map)) => {
337 if strukt
338 .fields
339 .iter()
340 .any(|f| f.name.to_utf8_string_lossy() == "0")
341 {
342 self.parse_tuple_strukt(
343 strukt,
344 &(0..map.len())
345 .map(|i| map.get(&i.to_string()).unwrap().clone())
346 .collect::<Vec<_>>(),
347 )
348 } else {
349 self.parse_strukt(strukt, map)
350 }
351 }
352 (ScSpecEntry::UdtStructV0(strukt), Value::Array(arr)) => {
353 self.parse_tuple_strukt(strukt, arr)
354 }
355 (
356 ScSpecEntry::UdtUnionV0(union),
357 val @ (Value::Array(_) | Value::String(_) | Value::Object(_)),
358 ) => self.parse_union(union, val),
359 (ScSpecEntry::UdtEnumV0(enum_), Value::Number(num)) => parse_const_enum(num, enum_),
360 (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
361 }
362 }
363
364 fn parse_tuple_strukt(
365 &self,
366 strukt: &ScSpecUdtStructV0,
367 array: &[Value],
368 ) -> Result<ScVal, Error> {
369 let items = strukt
370 .fields
371 .to_vec()
372 .iter()
373 .zip(array.iter())
374 .map(|(f, v)| {
375 let val = self.from_json(v, &f.type_)?;
376 Ok(val)
377 })
378 .collect::<Result<Vec<_>, Error>>()?;
379 Ok(ScVal::Vec(Some(items.try_into().map_err(Error::Xdr)?)))
380 }
381
382 fn parse_strukt(
383 &self,
384 strukt: &ScSpecUdtStructV0,
385 map: &serde_json::Map<String, Value>,
386 ) -> Result<ScVal, Error> {
387 let items = strukt
388 .fields
389 .to_vec()
390 .iter()
391 .map(|f| {
392 let name = &f.name.to_utf8_string_lossy();
393 let v = map
394 .get(name)
395 .ok_or_else(|| Error::MissingKey(name.clone()))?;
396 let val = self.from_json(v, &f.type_)?;
397 let key = StringM::from_str(name).unwrap();
398 Ok(ScMapEntry {
399 key: ScVal::Symbol(key.into()),
400 val,
401 })
402 })
403 .collect::<Result<Vec<_>, Error>>()?;
404 let map = ScMap::sorted_from(items).map_err(Error::Xdr)?;
405 Ok(ScVal::Map(Some(map)))
406 }
407
408 fn parse_union(&self, union: &ScSpecUdtUnionV0, value: &Value) -> Result<ScVal, Error> {
409 let (enum_case, rest) = match value {
410 Value::String(s) => (s, None),
411 Value::Object(o) if o.len() == 1 => {
412 let res = o.values().next().map(|v| match v {
413 Value::Object(obj) if obj.contains_key("0") => {
414 let len = obj.len();
415 Value::Array(
416 (0..len)
417 .map(|i| obj.get(&i.to_string()).unwrap().clone())
418 .collect::<Vec<_>>(),
419 )
420 }
421 _ => v.clone(),
422 });
423 (o.keys().next().unwrap(), res)
424 }
425 _ => todo!(),
426 };
427 let case = union
428 .cases
429 .iter()
430 .find(|c| {
431 let name = match c {
432 ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
433 ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
434 };
435 enum_case == &name.to_utf8_string_lossy()
436 })
437 .ok_or_else(|| {
438 Error::EnumCase(enum_case.to_string(), union.name.to_utf8_string_lossy())
439 })?;
440
441 let mut res = vec![ScVal::Symbol(ScSymbol(
442 enum_case.try_into().map_err(Error::Xdr)?,
443 ))];
444
445 match (case, rest) {
446 (ScSpecUdtUnionCaseV0::VoidV0(_), _) | (ScSpecUdtUnionCaseV0::TupleV0(_), None) => (),
447 (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }), Some(arr))
448 if type_.len() == 1 =>
449 {
450 res.push(self.from_json(&arr, &type_[0])?);
451 }
452 (
453 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { type_, .. }),
454 Some(Value::Array(arr)),
455 ) => {
456 res.extend(
457 arr.iter()
458 .zip(type_.iter())
459 .map(|(elem, ty)| self.from_json(elem, ty))
460 .collect::<Result<Vec<_>, _>>()?,
461 );
462 }
463 (ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 { .. }), Some(_)) => {}
464 }
465 Ok(ScVal::Vec(Some(res.try_into().map_err(Error::Xdr)?)))
466 }
467
468 fn parse_tuple(
469 &self,
470 t: &ScType,
471 tuple: &ScSpecTypeTuple,
472 items: &[Value],
473 ) -> Result<ScVal, Error> {
474 let ScSpecTypeTuple { value_types } = tuple;
475 if items.len() != value_types.len() {
476 return Err(Error::InvalidValue(Some(t.clone())));
477 }
478 let parsed: Result<Vec<ScVal>, Error> = items
479 .iter()
480 .zip(value_types.iter())
481 .map(|(item, t)| self.from_json(item, t))
482 .collect();
483 let converted: ScVec = parsed?.try_into().map_err(Error::Xdr)?;
484 Ok(ScVal::Vec(Some(converted)))
485 }
486
487 fn parse_map(
488 &self,
489 map: &ScSpecTypeMap,
490 value_map: &serde_json::Map<String, Value>,
491 ) -> Result<ScVal, Error> {
492 let ScSpecTypeMap {
493 key_type,
494 value_type,
495 } = map;
496 let parsed: Result<Vec<ScMapEntry>, Error> = value_map
498 .iter()
499 .map(|(k, v)| -> Result<ScMapEntry, Error> {
500 let key = self.from_string(k, key_type)?;
501 let val = self.from_json(v, value_type)?;
502 Ok(ScMapEntry { key, val })
503 })
504 .collect();
505 Ok(ScVal::Map(Some(
506 ScMap::sorted_from(parsed?).map_err(Error::Xdr)?,
507 )))
508 }
509}
510
511impl Spec {
512 pub fn xdr_to_json(&self, val: &ScVal, output: &ScType) -> Result<Value, Error> {
520 Ok(match (val, output) {
521 (ScVal::Void, ScType::Val | ScType::Option(_) | ScType::Tuple(_))
522 | (ScVal::Map(None) | ScVal::Vec(None), ScType::Option(_)) => Value::Null,
523 (ScVal::Bool(_), ScType::Bool)
524 | (ScVal::Void, ScType::Void)
525 | (ScVal::String(_), ScType::String)
526 | (ScVal::Symbol(_), ScType::Symbol)
527 | (ScVal::U64(_), ScType::U64)
528 | (ScVal::I64(_), ScType::I64)
529 | (ScVal::U32(_), ScType::U32)
530 | (ScVal::I32(_), ScType::I32)
531 | (ScVal::U128(_), ScType::U128)
532 | (ScVal::I128(_), ScType::I128)
533 | (ScVal::U256(_), ScType::U256)
534 | (ScVal::I256(_), ScType::I256)
535 | (ScVal::Duration(_), ScType::Duration)
536 | (ScVal::Timepoint(_), ScType::Timepoint)
537 | (
538 ScVal::ContractInstance(_)
539 | ScVal::LedgerKeyContractInstance
540 | ScVal::LedgerKeyNonce(_),
541 _,
542 )
543 | (ScVal::Address(_), ScType::Address | ScType::MuxedAddress)
544 | (ScVal::Bytes(_), ScType::Bytes | ScType::BytesN(_)) => to_json(val)?,
545
546 (val, ScType::Result(inner)) => self.xdr_to_json(val, &inner.ok_type)?,
547
548 (val, ScType::Option(inner)) => self.xdr_to_json(val, &inner.value_type)?,
549 (ScVal::Map(Some(_)) | ScVal::Vec(Some(_)) | ScVal::U32(_), type_) => {
550 self.sc_object_to_json(val, type_)?
551 }
552
553 (ScVal::Error(_), ScType::Error) => todo!(),
554 (v, typed) => todo!("{v:#?} doesn't have a matching {typed:#?}"),
555 })
556 }
557
558 pub fn vec_m_to_json<const MAX: u32>(
562 &self,
563 vec_m: &VecM<ScVal, MAX>,
564 type_: &ScType,
565 ) -> Result<Value, Error> {
566 Ok(Value::Array(
567 vec_m
568 .to_vec()
569 .iter()
570 .map(|sc_val| self.xdr_to_json(sc_val, type_))
571 .collect::<Result<Vec<_>, Error>>()?,
572 ))
573 }
574
575 pub fn sc_map_to_json(&self, sc_map: &ScMap, type_: &ScSpecTypeMap) -> Result<Value, Error> {
579 let v = sc_map
580 .iter()
581 .map(|ScMapEntry { key, val }| {
582 let key_s = self.xdr_to_json(key, &type_.key_type)?.to_string();
583 let val_value = self.xdr_to_json(val, &type_.value_type)?;
584 Ok((key_s, val_value))
585 })
586 .collect::<Result<serde_json::Map<String, Value>, Error>>()?;
587 Ok(Value::Object(v))
588 }
589
590 pub fn udt_to_json(&self, name: &StringM<60>, sc_obj: &ScVal) -> Result<Value, Error> {
598 let name = &name.to_utf8_string_lossy();
599 let udt = self.find(name)?;
600 Ok(match (sc_obj, udt) {
601 (ScVal::Map(Some(map)), ScSpecEntry::UdtStructV0(strukt)) => serde_json::Value::Object(
602 strukt
603 .fields
604 .iter()
605 .zip(map.iter())
606 .map(|(field, entry)| {
607 let val = self.xdr_to_json(&entry.val, &field.type_)?;
608 Ok((field.name.to_utf8_string_lossy(), val))
609 })
610 .collect::<Result<serde_json::Map<String, _>, Error>>()?,
611 ),
612 (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtStructV0(strukt)) => Value::Array(
613 strukt
614 .fields
615 .iter()
616 .zip(vec_.iter())
617 .map(|(field, entry)| self.xdr_to_json(entry, &field.type_))
618 .collect::<Result<Vec<_>, Error>>()?,
619 ),
620 (ScVal::Vec(Some(vec_)), ScSpecEntry::UdtUnionV0(union)) => {
621 let v = vec_.to_vec();
622 let (first, rest) = match v.split_at(1) {
624 ([first], []) => (first, None),
625 ([first], rest) => (first, Some(rest)),
626 _ => return Err(Error::IllFormedEnum(union.name.to_utf8_string_lossy())),
627 };
628
629 let ScVal::Symbol(case_name) = first else {
630 return Err(Error::EnumFirstValueNotSymbol);
631 };
632 let case = union
633 .cases
634 .iter()
635 .find(|case| {
636 let name = match case {
637 ScSpecUdtUnionCaseV0::VoidV0(v) => &v.name,
638 ScSpecUdtUnionCaseV0::TupleV0(v) => &v.name,
639 };
640 name.as_vec() == case_name.as_vec()
641 })
642 .ok_or_else(|| Error::FailedToFindEnumCase(case_name.to_utf8_string_lossy()))?;
643
644 let case_name = case_name.to_utf8_string_lossy();
645 match case {
646 ScSpecUdtUnionCaseV0::TupleV0(v) => {
647 let rest = rest.ok_or_else(|| {
648 Error::EnumMissingSecondValue(
649 union.name.to_utf8_string_lossy(),
650 case_name.clone(),
651 )
652 })?;
653 let val = if v.type_.len() == 1 {
654 self.xdr_to_json(&rest[0], &v.type_[0])?
655 } else {
656 Value::Array(
657 v.type_
658 .iter()
659 .zip(rest.iter())
660 .map(|(type_, val)| self.xdr_to_json(val, type_))
661 .collect::<Result<Vec<_>, Error>>()?,
662 )
663 };
664
665 Value::Object([(case_name, val)].into_iter().collect())
666 }
667 ScSpecUdtUnionCaseV0::VoidV0(_) => Value::String(case_name),
668 }
669 }
670 (ScVal::U32(v), ScSpecEntry::UdtEnumV0(_enum_)) => {
671 Value::Number(serde_json::Number::from(*v))
672 }
673 (s, v) => todo!("Not implemented for {s:#?} {v:#?}"),
674 })
675 }
676
677 pub fn sc_object_to_json(&self, val: &ScVal, spec_type: &ScType) -> Result<Value, Error> {
685 Ok(match (val, spec_type) {
686 (ScVal::Vec(Some(ScVec(vec_m))), ScType::Vec(type_)) => {
687 self.vec_m_to_json(vec_m, &type_.element_type)?
688 }
689 (ScVal::Vec(Some(ScVec(vec_m))), ScType::Tuple(tuple_type)) => Value::Array(
690 vec_m
691 .iter()
692 .zip(tuple_type.value_types.iter())
693 .map(|(v, t)| self.xdr_to_json(v, t))
694 .collect::<Result<Vec<_>, _>>()?,
695 ),
696 (
697 sc_obj @ (ScVal::Vec(_) | ScVal::Map(_) | ScVal::U32(_)),
698 ScType::Udt(ScSpecTypeUdt { name }),
699 ) => self.udt_to_json(name, sc_obj)?,
700
701 (ScVal::Map(Some(map)), ScType::Map(map_type)) => self.sc_map_to_json(map, map_type)?,
702
703 (ScVal::U64(u64_), ScType::U64) => Value::Number(serde_json::Number::from(*u64_)),
704
705 (ScVal::I64(i64_), ScType::I64) => Value::Number(serde_json::Number::from(*i64_)),
706 (int @ ScVal::U128(_), ScType::U128) => {
707 let v: u128 = int
709 .clone()
710 .try_into()
711 .map_err(|()| Error::InvalidValue(Some(ScType::U128)))?;
712 Value::String(v.to_string())
713 }
714
715 (int @ ScVal::I128(_), ScType::I128) => {
716 let v: i128 = int
718 .clone()
719 .try_into()
720 .map_err(|()| Error::InvalidValue(Some(ScType::I128)))?;
721 Value::String(v.to_string())
722 }
723
724 (ScVal::Bytes(v), ScType::Bytes | ScType::BytesN(_)) => {
725 Value::String(to_lower_hex(v.as_slice()))
726 }
727
728 (ScVal::Bytes(_), ScType::Udt(_)) => todo!(),
729
730 (ScVal::ContractInstance(_), _) => todo!(),
731
732 (ScVal::Address(v), ScType::Address | ScType::MuxedAddress) => sc_address_to_json(v),
733
734 (ok_val, ScType::Result(result_type)) => {
735 let ScSpecTypeResult { ok_type, .. } = result_type.as_ref();
736 self.xdr_to_json(ok_val, ok_type)?
737 }
738
739 (x, y) => return Err(Error::InvalidPair(x.clone(), y.clone())),
740 })
741 }
742}
743
744pub fn from_string_primitive(s: &str, t: &ScType) -> Result<ScVal, Error> {
748 Spec::from_string_primitive(s, t)
749}
750
751fn parse_const_enum(num: &serde_json::Number, enum_: &ScSpecUdtEnumV0) -> Result<ScVal, Error> {
752 let num = num
753 .as_u64()
754 .ok_or_else(|| Error::FailedNumConversion(num.clone()))?;
755 let num = u32::try_from(num).map_err(|_| Error::EnumConstTooLarge(num))?;
756 enum_
757 .cases
758 .iter()
759 .find(|c| c.value == num)
760 .ok_or(Error::EnumConst(num))
761 .map(|c| ScVal::U32(c.value))
762}
763
764#[allow(clippy::too_many_lines)]
768pub fn from_json_primitives(v: &Value, t: &ScType) -> Result<ScVal, Error> {
769 let val: ScVal = match (t, v) {
770 (ScType::Bool, Value::Bool(true)) => ScVal::Bool(true),
772 (ScType::Bool, Value::Bool(false)) => ScVal::Bool(false),
773
774 (ScType::U128, Value::String(s)) => {
776 let val: u128 = u128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
777 let bytes = val.to_be_bytes();
778 let (hi, lo) = bytes.split_at(8);
779 ScVal::U128(UInt128Parts {
780 hi: u64::from_be_bytes(hi.try_into()?),
781 lo: u64::from_be_bytes(lo.try_into()?),
782 })
783 }
784
785 (ScType::I128, Value::String(s)) => {
786 let val: i128 = i128::from_str(s).map_err(|_| Error::InvalidValue(Some(t.clone())))?;
787 let bytes = val.to_be_bytes();
788 let (hi, lo) = bytes.split_at(8);
789 ScVal::I128(Int128Parts {
790 hi: i64::from_be_bytes(hi.try_into()?),
791 lo: u64::from_be_bytes(lo.try_into()?),
792 })
793 }
794
795 (ScType::U256, Value::String(s)) => {
797 let (hi, lo) = ethnum::U256::from_str_prefixed(s)?.into_words();
798 let hi_bytes = hi.to_be_bytes();
799 let (hi_hi, hi_lo) = hi_bytes.split_at(8);
800 let lo_bytes = lo.to_be_bytes();
801 let (lo_hi, lo_lo) = lo_bytes.split_at(8);
802 ScVal::U256(UInt256Parts {
803 hi_hi: u64::from_be_bytes(hi_hi.try_into()?),
804 hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
805 lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
806 lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
807 })
808 }
809 (ScType::I256, Value::String(s)) => {
810 let (hi, lo) = ethnum::I256::from_str_prefixed(s)?.into_words();
811 let hi_bytes = hi.to_be_bytes();
812 let (hi_hi, hi_lo) = hi_bytes.split_at(8);
813 let lo_bytes = lo.to_be_bytes();
814 let (lo_hi, lo_lo) = lo_bytes.split_at(8);
815 ScVal::I256(Int256Parts {
816 hi_hi: i64::from_be_bytes(hi_hi.try_into()?),
817 hi_lo: u64::from_be_bytes(hi_lo.try_into()?),
818 lo_hi: u64::from_be_bytes(lo_hi.try_into()?),
819 lo_lo: u64::from_be_bytes(lo_lo.try_into()?),
820 })
821 }
822
823 (ScType::I32, Value::Number(n)) => ScVal::I32(
824 n.as_i64()
825 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
826 .try_into()
827 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
828 ),
829 (ScType::U32, Value::Number(n)) => ScVal::U32(
830 n.as_u64()
831 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
832 .try_into()
833 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
834 ),
835 (ScType::I64, Value::Number(n)) => ScVal::I64(
836 n.as_i64()
837 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
838 ),
839 (ScType::U64 | ScType::Timepoint | ScType::Duration, Value::Number(n)) => ScVal::U64(
840 n.as_u64()
841 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?,
842 ),
843
844 (ScType::Symbol, Value::String(s)) => ScVal::Symbol(ScSymbol(
846 s.as_bytes()
847 .try_into()
848 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
849 )),
850
851 (ScType::Address, Value::String(s)) => sc_address_from_json(s)?,
852
853 (ScType::MuxedAddress, Value::String(s)) => sc_address_from_json(s)?,
854
855 (bytes @ ScType::BytesN(_), Value::Number(n)) => {
857 from_json_primitives(&Value::String(format!("{n}")), bytes)?
858 }
859 (ScType::BytesN(bytes), Value::String(s)) => ScVal::Bytes(ScBytes({
860 if bytes.n == 32 {
861 if let Ok(key) = sc_address_from_json(s) {
865 return Ok(key);
866 }
867 }
868 utils::padded_hex_from_str(s, bytes.n as usize)
870 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
871 .try_into()
872 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
873 })),
874 (ScType::Bytes, Value::Number(n)) => {
875 from_json_primitives(&Value::String(format!("{n}")), &ScType::Bytes)?
876 }
877 (ScType::Bytes, Value::String(s)) => ScVal::Bytes(
878 hex::decode(s)
879 .map_err(|_| Error::InvalidValue(Some(t.clone())))?
880 .try_into()
881 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
882 ),
883 (ScType::Bytes | ScType::BytesN(_), Value::Array(raw)) => {
884 let b: Result<Vec<u8>, Error> = raw
885 .iter()
886 .map(|item| {
887 item.as_u64()
888 .ok_or_else(|| Error::InvalidValue(Some(t.clone())))?
889 .try_into()
890 .map_err(|_| Error::InvalidValue(Some(t.clone())))
891 })
892 .collect();
893 let converted: BytesM<{ u32::MAX }> = b?.try_into().map_err(Error::Xdr)?;
894 ScVal::Bytes(ScBytes(converted))
895 }
896
897 (ScType::String, Value::String(s)) => ScVal::String(ScString(
898 s.try_into()
899 .map_err(|_| Error::InvalidValue(Some(t.clone())))?,
900 )),
901 (_, raw) => serde_json::from_value(raw.clone())?,
903 };
904 Ok(val)
905}
906
907pub fn to_string(v: &ScVal) -> Result<String, Error> {
911 #[allow(clippy::match_same_arms)]
912 Ok(match v {
913 ScVal::Symbol(v) => std::str::from_utf8(v.as_slice())
916 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
917 .to_string(),
918 ScVal::LedgerKeyContractInstance => "LedgerKeyContractInstance".to_string(),
919 _ => serde_json::to_string(&to_json(v)?)?,
920 })
921}
922
923#[allow(clippy::too_many_lines)]
927pub fn to_json(v: &ScVal) -> Result<Value, Error> {
928 #[allow(clippy::match_same_arms)]
929 let val: Value = match v {
930 ScVal::Bool(b) => Value::Bool(*b),
931 ScVal::Void => Value::Null,
932 ScVal::LedgerKeyContractInstance => Value::String("LedgerKeyContractInstance".to_string()),
933 ScVal::U64(v) => Value::Number(serde_json::Number::from(*v)),
934 ScVal::Timepoint(tp) => Value::Number(serde_json::Number::from(tp.0)),
935 ScVal::Duration(d) => Value::Number(serde_json::Number::from(d.0)),
936 ScVal::I64(v) => Value::Number(serde_json::Number::from(*v)),
937 ScVal::U32(v) => Value::Number(serde_json::Number::from(*v)),
938 ScVal::I32(v) => Value::Number(serde_json::Number::from(*v)),
939 ScVal::Symbol(v) => Value::String(
940 std::str::from_utf8(v.as_slice())
941 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
942 .to_string(),
943 ),
944 ScVal::String(v) => Value::String(
945 std::str::from_utf8(v.as_slice())
946 .map_err(|_| Error::InvalidValue(Some(ScType::Symbol)))?
947 .to_string(),
948 ),
949 ScVal::Vec(v) => {
950 let values: Result<Vec<Value>, Error> = v.as_ref().map_or_else(
951 || Ok(vec![]),
952 |v| {
953 v.iter()
954 .map(|item| -> Result<Value, Error> { to_json(item) })
955 .collect()
956 },
957 );
958 Value::Array(values?)
959 }
960 ScVal::Map(None) => Value::Object(serde_json::Map::with_capacity(0)),
961 ScVal::Map(Some(v)) => {
962 let mut m = serde_json::Map::<String, Value>::with_capacity(v.len());
964 for ScMapEntry { key, val } in v.iter() {
965 let k: String = to_string(key)?;
966 let v: Value = to_json(val).map_err(|_| Error::InvalidValue(None))?;
967 m.insert(k, v);
968 }
969 Value::Object(m)
970 }
971 ScVal::Bytes(v) => Value::String(to_lower_hex(v.as_slice())),
972 ScVal::Address(v) => sc_address_to_json(v),
973 ScVal::U128(n) => {
974 let hi: [u8; 8] = n.hi.to_be_bytes();
975 let lo: [u8; 8] = n.lo.to_be_bytes();
976 let bytes = [hi, lo].concat();
977 let v = u128::from_be_bytes(
979 bytes
980 .as_slice()
981 .try_into()
982 .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
983 )
984 .to_string();
985 Value::String(v)
986 }
987 ScVal::I128(n) => {
988 let hi: [u8; 8] = n.hi.to_be_bytes();
989 let lo: [u8; 8] = n.lo.to_be_bytes();
990 let bytes = [hi, lo].concat();
991 let v = i128::from_be_bytes(
993 bytes
994 .as_slice()
995 .try_into()
996 .map_err(|_| Error::InvalidValue(Some(ScType::I128)))?,
997 )
998 .to_string();
999 Value::String(v)
1000 }
1001 ScVal::U256(u256parts) => {
1002 let bytes = [
1003 u256parts.hi_hi.to_be_bytes(),
1004 u256parts.hi_lo.to_be_bytes(),
1005 u256parts.lo_hi.to_be_bytes(),
1006 u256parts.lo_lo.to_be_bytes(),
1007 ]
1008 .concat();
1009 let u256 = ethnum::U256::from_be_bytes(
1010 bytes
1011 .as_slice()
1012 .try_into()
1013 .map_err(|_| Error::InvalidValue(Some(ScType::U256)))?,
1014 );
1015 Value::String(u256.to_string())
1016 }
1017 ScVal::I256(i256parts) => {
1018 let bytes = [
1019 i256parts.hi_hi.to_be_bytes(),
1020 i256parts.hi_lo.to_be_bytes(),
1021 i256parts.lo_hi.to_be_bytes(),
1022 i256parts.lo_lo.to_be_bytes(),
1023 ]
1024 .concat();
1025 let i256 = ethnum::I256::from_be_bytes(
1026 bytes
1027 .as_slice()
1028 .try_into()
1029 .map_err(|_| Error::InvalidValue(Some(ScType::I256)))?,
1030 );
1031 Value::String(i256.to_string())
1032 }
1033 ScVal::ContractInstance(ScContractInstance {
1034 executable: ContractExecutable::Wasm(hash),
1035 ..
1036 }) => json!({ "hash": hash }),
1037 ScVal::ContractInstance(ScContractInstance {
1038 executable: ContractExecutable::StellarAsset,
1039 ..
1040 }) => json!({"SAC": true}),
1041 ScVal::LedgerKeyNonce(ScNonceKey { nonce }) => {
1042 Value::Number(serde_json::Number::from(*nonce))
1043 }
1044 ScVal::Error(e) => serde_json::to_value(e)?,
1045 };
1046 Ok(val)
1047}
1048
1049fn sc_address_to_json(v: &ScAddress) -> Value {
1050 match v {
1051 ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(k)))) => {
1052 Value::String(stellar_strkey::ed25519::PublicKey(*k).to_string())
1053 }
1054 ScAddress::Contract(ContractId(h)) => {
1055 Value::String(stellar_strkey::Contract(h.clone().into()).to_string())
1056 }
1057 ScAddress::MuxedAccount(MuxedEd25519Account { ed25519, id }) => Value::String(
1058 stellar_strkey::ed25519::MuxedAccount {
1059 ed25519: ed25519.0,
1060 id: *id,
1061 }
1062 .to_string(),
1063 ),
1064 ScAddress::ClaimableBalance(_) => todo!("ClaimableBalance is not supported"),
1065 ScAddress::LiquidityPool(_) => todo!("LiquidityPool is not supported"),
1066 }
1067}
1068
1069fn sc_address_from_json(s: &str) -> Result<ScVal, Error> {
1070 stellar_strkey::Strkey::from_string(s)
1071 .map_err(|_| Error::InvalidValue(Some(ScType::Address)))
1072 .map(|parsed| match parsed {
1073 stellar_strkey::Strkey::PublicKeyEd25519(p) => Some(ScVal::Address(
1074 ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(p.0)))),
1075 )),
1076 stellar_strkey::Strkey::Contract(c) => {
1077 Some(ScVal::Address(ScAddress::Contract(ContractId(Hash(c.0)))))
1078 }
1079 stellar_strkey::Strkey::MuxedAccountEd25519(m) => Some(ScVal::Address(
1080 ScAddress::MuxedAccount(MuxedEd25519Account {
1081 ed25519: Uint256(m.ed25519),
1082 id: m.id,
1083 }),
1084 )),
1085 _ => None,
1086 })?
1087 .ok_or(Error::InvalidValue(Some(ScType::Address)))
1088}
1089
1090fn to_lower_hex(bytes: &[u8]) -> String {
1091 let mut res = String::with_capacity(bytes.len());
1092 for b in bytes {
1093 let _ = write!(res, "{b:02x}");
1094 }
1095 res
1096}
1097
1098impl Spec {
1099 #[must_use]
1100 pub fn arg_value_name(&self, type_: &ScType, depth: usize) -> Option<String> {
1101 match type_ {
1102 ScType::U64 => Some("u64".to_string()),
1103 ScType::I64 => Some("i64".to_string()),
1104 ScType::U128 => Some("u128".to_string()),
1105 ScType::I128 => Some("i128".to_string()),
1106 ScType::U32 => Some("u32".to_string()),
1107 ScType::I32 => Some("i32".to_string()),
1108 ScType::Bool => Some("bool".to_string()),
1109 ScType::Symbol => Some("Symbol".to_string()),
1110 ScType::Error => Some("Error".to_string()),
1111 ScType::Bytes => Some("hex_bytes".to_string()),
1112 ScType::Address => Some("Address".to_string()),
1113 ScType::Void => Some("Null".to_string()),
1114 ScType::Timepoint => Some("Timepoint".to_string()),
1115 ScType::Duration => Some("Duration".to_string()),
1116 ScType::U256 => Some("u256".to_string()),
1117 ScType::I256 => Some("i256".to_string()),
1118 ScType::String => Some("String".to_string()),
1119 ScType::MuxedAddress => Some("MuxedAddress".to_string()),
1120 ScType::Option(val) => {
1121 let ScSpecTypeOption { value_type } = val.as_ref();
1122 let inner = self.arg_value_name(value_type.as_ref(), depth + 1)?;
1123 Some(format!("Option<{inner}>"))
1124 }
1125 ScType::Vec(val) => {
1126 let ScSpecTypeVec { element_type } = val.as_ref();
1127 let inner = self.arg_value_name(element_type.as_ref(), depth + 1)?;
1128 Some(format!("Array<{inner}>"))
1129 }
1130 ScType::Result(val) => {
1131 let ScSpecTypeResult {
1132 ok_type,
1133 error_type,
1134 } = val.as_ref();
1135 let ok = self.arg_value_name(ok_type.as_ref(), depth + 1)?;
1136 let error = self.arg_value_name(error_type.as_ref(), depth + 1)?;
1137 Some(format!("Result<{ok}, {error}>"))
1138 }
1139 ScType::Tuple(val) => {
1140 let ScSpecTypeTuple { value_types } = val.as_ref();
1141 let names = value_types
1142 .iter()
1143 .map(|t| self.arg_value_name(t, depth + 1))
1144 .collect::<Option<Vec<_>>>()?
1145 .join(", ");
1146 Some(format!("Tuple<{names}>"))
1147 }
1148 ScType::Map(val) => {
1149 let ScSpecTypeMap {
1150 key_type,
1151 value_type,
1152 } = val.as_ref();
1153 let (key, val) = (
1154 self.arg_value_name(key_type.as_ref(), depth + 1)?,
1155 self.arg_value_name(value_type.as_ref(), depth + 1)?,
1156 );
1157 Some(format!("Map<{key}, {val}>"))
1158 }
1159 ScType::BytesN(t) => Some(format!("{}_hex_bytes", t.n)),
1160 ScType::Udt(ScSpecTypeUdt { name }) => {
1161 match self.find(&name.to_utf8_string_lossy()).ok()? {
1162 ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. })
1163 if fields
1164 .first()
1165 .is_some_and(|f| f.name.to_utf8_string_lossy() == "0") =>
1166 {
1167 let fields = fields
1168 .iter()
1169 .map(|t| self.arg_value_name(&t.type_, depth + 1))
1170 .collect::<Option<Vec<_>>>()?
1171 .join(", ");
1172 Some(format!("[{fields}]"))
1173 }
1174 ScSpecEntry::UdtStructV0(strukt) => self.arg_value_udt(strukt, depth),
1175 ScSpecEntry::UdtUnionV0(union) => self.arg_value_union(union, depth),
1176 ScSpecEntry::UdtEnumV0(enum_) => Some(arg_value_enum(enum_)),
1177 ScSpecEntry::FunctionV0(_)
1178 | ScSpecEntry::UdtErrorEnumV0(_)
1179 | ScSpecEntry::EventV0(_) => None,
1180 }
1181 }
1182 ScType::Val => None,
1184 }
1185 }
1186
1187 fn arg_value_udt(&self, strukt: &ScSpecUdtStructV0, depth: usize) -> Option<String> {
1188 let inner = strukt
1189 .fields
1190 .iter()
1191 .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1192 .map(|(name, type_)| {
1193 let type_ = self.arg_value_name(type_, depth + 1)?;
1194 Some(format!("{name}: {type_}"))
1195 })
1196 .collect::<Option<Vec<_>>>()?
1197 .join(", ");
1198 Some(format!("{{ {inner} }}"))
1199 }
1200
1201 fn arg_value_union(&self, union: &ScSpecUdtUnionV0, depth: usize) -> Option<String> {
1202 union
1203 .cases
1204 .iter()
1205 .map(|f| {
1206 Some(match f {
1207 ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1208 name.to_utf8_string_lossy()
1209 }
1210 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1211 name,
1212 type_,
1213 ..
1214 }) => format!(
1215 "{}({})",
1216 name.to_utf8_string_lossy(),
1217 type_
1218 .iter()
1219 .map(|type_| self.arg_value_name(type_, depth + 1))
1220 .collect::<Option<Vec<String>>>()?
1221 .join(",")
1222 ),
1223 })
1224 })
1225 .collect::<Option<Vec<_>>>()
1226 .map(|v| v.join(" | "))
1227 }
1228}
1229
1230fn arg_value_enum(enum_: &ScSpecUdtEnumV0) -> String {
1231 enum_
1232 .cases
1233 .iter()
1234 .map(|case| case.value.to_string())
1235 .join(" | ")
1236}
1237
1238impl Spec {
1240 #[must_use]
1241 pub fn example(&self, type_: &ScType) -> Option<String> {
1242 #[allow(clippy::match_same_arms)]
1243 match type_ {
1244 ScType::U64 => Some("1".to_string()),
1245 ScType::I64 => Some("1".to_string()),
1246 ScType::U128 => Some("\"1\"".to_string()),
1247 ScType::I128 => Some("\"1\"".to_string()),
1248 ScType::U32 => Some("1".to_string()),
1249 ScType::I32 => Some("1".to_string()),
1250 ScType::Bool => Some("true".to_string()),
1251 ScType::Symbol => Some("\"hello\"".to_string()),
1252 ScType::Error => Some("Error".to_string()),
1253 ScType::Bytes => Some("\"0000000000000000\"".to_string()),
1254 ScType::Address => {
1255 Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1256 }
1257 ScType::Void => Some("null".to_string()),
1258 ScType::Timepoint => Some("1743010492".to_string()),
1259 ScType::Duration => Some("1".to_string()),
1260 ScType::U256 => Some("\"1\"".to_string()),
1261 ScType::I256 => Some("\"1\"".to_string()),
1262 ScType::String => Some("\"hello world\"".to_string()),
1263 ScType::Option(val) => {
1264 let ScSpecTypeOption { value_type } = val.as_ref();
1265 self.example(value_type.as_ref())
1266 }
1267 ScType::Vec(val) => {
1268 let ScSpecTypeVec { element_type } = val.as_ref();
1269 let inner = self.example(element_type.as_ref())?;
1270 Some(format!("[ {inner} ]"))
1271 }
1272 ScType::Result(val) => {
1273 let ScSpecTypeResult {
1274 ok_type,
1275 error_type,
1276 } = val.as_ref();
1277 let ok = self.example(ok_type.as_ref())?;
1278 let error = self.example(error_type.as_ref())?;
1279 Some(format!("Result<{ok}, {error}>"))
1280 }
1281 ScType::Tuple(val) => {
1282 let ScSpecTypeTuple { value_types } = val.as_ref();
1283 let names = value_types
1284 .iter()
1285 .map(|t| self.example(t))
1286 .collect::<Option<Vec<_>>>()?
1287 .join(", ");
1288 Some(format!("[{names}]"))
1289 }
1290 ScType::Map(map) => {
1291 let ScSpecTypeMap {
1292 key_type,
1293 value_type,
1294 } = map.as_ref();
1295 let (mut key, val) = (
1296 self.example(key_type.as_ref())?,
1297 self.example(value_type.as_ref())?,
1298 );
1299 if !matches!(key_type.as_ref(), ScType::Symbol) {
1300 key = format!("\"{key}\"");
1301 }
1302 Some(format!("{{ {key}: {val} }}"))
1303 }
1304 ScType::BytesN(n) => {
1305 let n = n.n as usize;
1306 let res = "00".repeat(n);
1307 Some(format!("\"{res}\""))
1308 }
1309 ScType::Udt(ScSpecTypeUdt { name }) => {
1310 self.example_udts(name.to_utf8_string_lossy().as_ref())
1311 }
1312 ScType::MuxedAddress => {
1313 Some("\"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF\"".to_string())
1314 }
1315 ScType::Val => None,
1317 }
1318 }
1319
1320 fn example_udts(&self, name: &str) -> Option<String> {
1321 match self.find(name).ok() {
1322 Some(ScSpecEntry::UdtStructV0(strukt)) => {
1323 if !strukt.fields.is_empty() && strukt.fields[0].name.to_utf8_string_lossy() == "0"
1325 {
1326 let value_types = strukt
1327 .fields
1328 .iter()
1329 .map(|f| f.type_.clone())
1330 .collect::<Vec<_>>()
1331 .try_into()
1332 .ok()?;
1333 return self.example(&ScType::Tuple(Box::new(ScSpecTypeTuple { value_types })));
1334 }
1335 let inner = strukt
1336 .fields
1337 .iter()
1338 .map(|f| (f.name.to_utf8_string_lossy(), &f.type_))
1339 .map(|(name, type_)| {
1340 let type_ = self.example(type_)?;
1341 let name = format!(r#""{name}""#);
1342 Some(format!("{name}: {type_}"))
1343 })
1344 .collect::<Option<Vec<_>>>()?
1345 .join(", ");
1346 Some(format!(r"{{ {inner} }}"))
1347 }
1348 Some(ScSpecEntry::UdtUnionV0(union)) => self.example_union(union),
1349 Some(ScSpecEntry::UdtEnumV0(enum_)) => {
1350 enum_.cases.iter().next().map(|c| c.value.to_string())
1351 }
1352 Some(
1353 ScSpecEntry::FunctionV0(_)
1354 | ScSpecEntry::UdtErrorEnumV0(_)
1355 | ScSpecEntry::EventV0(_),
1356 )
1357 | None => None,
1358 }
1359 }
1360
1361 fn example_union(&self, union: &ScSpecUdtUnionV0) -> Option<String> {
1362 let res = union
1363 .cases
1364 .iter()
1365 .map(|case| match case {
1366 ScSpecUdtUnionCaseV0::VoidV0(ScSpecUdtUnionCaseVoidV0 { name, .. }) => {
1367 Some(format!("\"{}\"", name.to_utf8_string_lossy()))
1368 }
1369 ScSpecUdtUnionCaseV0::TupleV0(ScSpecUdtUnionCaseTupleV0 {
1370 name, type_, ..
1371 }) => {
1372 if type_.len() == 1 {
1373 let single = self.example(&type_[0])?;
1374 Some(format!("{{\"{}\":{single}}}", name.to_utf8_string_lossy()))
1375 } else {
1376 let names = type_
1377 .iter()
1378 .map(|t| self.example(t))
1379 .collect::<Option<Vec<_>>>()?
1380 .join(", ");
1381 Some(format!("{{\"{}\":[{names}]}}", name.to_utf8_string_lossy()))
1382 }
1383 }
1384 })
1385 .collect::<Option<Vec<_>>>()?;
1386 Some(res.join("|"))
1387 }
1388}
1389
1390#[cfg(test)]
1391mod tests {
1392 use super::*;
1393
1394 use stellar_xdr::curr::ScSpecTypeBytesN;
1395
1396 #[test]
1397 fn from_json_primitives_bytesn() {
1398 let b = from_json_primitives(
1402 &Value::String("beefface".to_string()),
1403 &ScType::BytesN(ScSpecTypeBytesN { n: 4 }),
1404 )
1405 .unwrap();
1406 assert_eq!(
1407 b,
1408 ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1409 );
1410
1411 let b = from_json_primitives(
1414 &Value::Number(4554.into()),
1415 &ScType::BytesN(ScSpecTypeBytesN { n: 2 }),
1416 )
1417 .unwrap();
1418 assert_eq!(
1419 b,
1420 ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1421 );
1422 }
1423
1424 #[test]
1425 fn from_json_primitives_bytes() {
1426 let b =
1428 from_json_primitives(&Value::String("beefface".to_string()), &ScType::Bytes).unwrap();
1429 assert_eq!(
1430 b,
1431 ScVal::Bytes(ScBytes(vec![0xbe, 0xef, 0xfa, 0xce].try_into().unwrap()))
1432 );
1433
1434 let b = from_json_primitives(&Value::Number(4554.into()), &ScType::Bytes).unwrap();
1437 assert_eq!(
1438 b,
1439 ScVal::Bytes(ScBytes(vec![0x45, 0x54].try_into().unwrap()))
1440 );
1441 }
1442
1443 #[test]
1444 fn test_sc_address_from_json_strkey() {
1445 match sc_address_from_json("CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4") {
1447 Ok(addr) => assert_eq!(
1448 addr,
1449 ScVal::Address(ScAddress::Contract(ContractId(Hash([0; 32]))))
1450 ),
1451 Err(e) => panic!("Unexpected error: {e}"),
1452 }
1453
1454 match sc_address_from_json("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") {
1456 Ok(addr) => assert_eq!(
1457 addr,
1458 ScVal::Address(ScAddress::Contract(ContractId::from(Hash([
1459 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7,
1460 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c,
1461 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1462 ]))))
1463 ),
1464 Err(e) => panic!("Unexpected error: {e}"),
1465 }
1466
1467 match sc_address_from_json("GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF") {
1469 Ok(addr) => assert_eq!(
1470 addr,
1471 ScVal::Address(ScAddress::Account(AccountId(
1472 PublicKey::PublicKeyTypeEd25519([0; 32].into())
1473 )))
1474 ),
1475 Err(e) => panic!("Unexpected error: {e}"),
1476 }
1477
1478 match sc_address_from_json("GA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQHES5") {
1480 Ok(addr) => assert_eq!(
1481 addr,
1482 ScVal::Address(ScAddress::Account(AccountId(
1483 PublicKey::PublicKeyTypeEd25519(
1484 [
1485 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88,
1486 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0,
1487 0xec, 0x9c, 0x07, 0x3d, 0x05, 0xc7, 0xb1, 0x03,
1488 ]
1489 .into()
1490 )
1491 )))
1492 ),
1493 Err(e) => panic!("Unexpected error: {e}"),
1494 }
1495
1496 match sc_address_from_json(
1498 "MA5XIGA5C7QTPTWXQHY6MCJRMTRZDOSHR6EFIBNDQTCQHG262N4GGGC2XZXD7G7EWH7U6",
1499 ) {
1500 Ok(addr) => {
1501 assert!(matches!(addr, ScVal::Address(ScAddress::MuxedAccount(_))));
1503 }
1504 Err(e) => panic!("Unexpected error parsing MuxedAddress: {e}"),
1505 }
1506 }
1507
1508 #[test]
1509 fn test_sc_address_json_strkey_muxed_roundtrip() {
1510 let muxed_address_str =
1511 "MA5XIGA5C7QTPTWXQHY6MCJRMTRZDOSHR6EFIBNDQTCQHG262N4GGGC2XZXD7G7EWH7U6";
1512
1513 match sc_address_from_json(muxed_address_str) {
1515 Ok(ScVal::Address(address)) => {
1516 let json_result = sc_address_to_json(&address);
1517 assert_eq!(json_result, Value::String(muxed_address_str.to_string()));
1518 }
1519 Ok(_) => panic!("Expected ScVal::Address"),
1520 Err(e) => panic!("Unexpected error: {e}"),
1521 }
1522 }
1523
1524 #[test]
1525 fn test_sc_muxed_address_from_json() {
1526 let expected_ed25519 = Uint256([
1527 0x3b, 0x74, 0x18, 0x1d, 0x17, 0xe1, 0x37, 0xce, 0xd7, 0x81, 0xf1, 0xe6, 0x09, 0x31,
1528 0x64, 0xe3, 0x91, 0xba, 0x47, 0x8f, 0x88, 0x54, 0x05, 0xa3, 0x84, 0xc5, 0x03, 0x9b,
1529 0x5e, 0xd3, 0x78, 0x63,
1530 ]);
1531 let expected_id = 1_754_924_385_537_090_737_u64;
1532
1533 let muxed_addr = stellar_strkey::ed25519::MuxedAccount {
1535 ed25519: expected_ed25519.0,
1536 id: expected_id,
1537 }
1538 .to_string();
1539
1540 match sc_address_from_json(&muxed_addr) {
1542 Ok(ScVal::Address(ScAddress::MuxedAccount(MuxedEd25519Account { ed25519, id }))) => {
1543 assert_eq!(ed25519, expected_ed25519);
1545 assert_eq!(id, expected_id);
1546 }
1547 Ok(_) => panic!("Expected ScVal::Address(ScAddress::MuxedAccount(_))"),
1548 Err(e) => panic!("Unexpected error parsing MuxedAddress: {e}"),
1549 }
1550 }
1551
1552 #[test]
1553 fn test_u128_conversions() {
1554 let val = 42u128;
1556 let json_val = Value::String(val.to_string());
1557 let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1558 assert_eq!(to_json(&scval).unwrap(), json_val);
1559
1560 let val = 0u128;
1562 let json_val = Value::String(val.to_string());
1563 let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1564 assert_eq!(to_json(&scval).unwrap(), json_val);
1565
1566 let val = u128::MAX;
1568 let json_val = Value::String(val.to_string());
1569 let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1570 assert_eq!(to_json(&scval).unwrap(), json_val);
1571
1572 let val = 340_282_366_920_938_463_463_374_607_431_768_211_455u128;
1574 let json_val = Value::String(val.to_string());
1575 let scval = from_json_primitives(&json_val, &ScType::U128).unwrap();
1576 assert_eq!(to_json(&scval).unwrap(), json_val);
1577 }
1578
1579 #[test]
1580 fn test_i128_conversions() {
1581 let val = 42i128;
1583 let json_val = Value::String(val.to_string());
1584 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1585 assert_eq!(to_json(&scval).unwrap(), json_val);
1586
1587 let val = -42i128;
1589 let json_val = Value::String(val.to_string());
1590 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1591 assert_eq!(to_json(&scval).unwrap(), json_val);
1592
1593 let val = 0i128;
1595 let json_val = Value::String(val.to_string());
1596 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1597 assert_eq!(to_json(&scval).unwrap(), json_val);
1598
1599 let val = i128::MAX;
1601 let json_val = Value::String(val.to_string());
1602 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1603 assert_eq!(to_json(&scval).unwrap(), json_val);
1604
1605 let val = i128::MIN;
1607 let json_val = Value::String(val.to_string());
1608 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1609 assert_eq!(to_json(&scval).unwrap(), json_val);
1610
1611 let val = -170_141_183_460_469_231_731_687_303_715_884_105_728i128;
1613 let json_val = Value::String(val.to_string());
1614 let scval = from_json_primitives(&json_val, &ScType::I128).unwrap();
1615 assert_eq!(to_json(&scval).unwrap(), json_val);
1616 }
1617
1618 #[test]
1619 fn test_u256_conversions() {
1620 let val = "42";
1622 let json_val = Value::String(val.to_string());
1623 let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1624 assert_eq!(to_json(&scval).unwrap(), json_val);
1625
1626 let val = "0";
1628 let json_val = Value::String(val.to_string());
1629 let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1630 assert_eq!(to_json(&scval).unwrap(), json_val);
1631
1632 let val = "115792089237316195423570985008687907853269984665640564039457584007913129639935";
1634 let json_val = Value::String(val.to_string());
1635 let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1636 assert_eq!(to_json(&scval).unwrap(), json_val);
1637
1638 let val = "0xff";
1640 let json_val = Value::String(val.to_string());
1641 let scval = from_json_primitives(&json_val, &ScType::U256).unwrap();
1642 let expected = Value::String("255".to_string());
1643 assert_eq!(to_json(&scval).unwrap(), expected);
1644
1645 let val = "0x1234567890abcdef";
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("1311768467294899695".to_string());
1650 assert_eq!(to_json(&scval).unwrap(), expected);
1651 }
1652
1653 #[test]
1654 fn test_i256_conversions() {
1655 let val = "42";
1657 let json_val = Value::String(val.to_string());
1658 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1659 assert_eq!(to_json(&scval).unwrap(), json_val);
1660
1661 let val = "-42";
1663 let json_val = Value::String(val.to_string());
1664 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1665 assert_eq!(to_json(&scval).unwrap(), json_val);
1666
1667 let val = "0";
1669 let json_val = Value::String(val.to_string());
1670 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1671 assert_eq!(to_json(&scval).unwrap(), json_val);
1672
1673 let val = "57896044618658097711785492504343953926634992332820282019728792003956564819967";
1675 let json_val = Value::String(val.to_string());
1676 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1677 assert_eq!(to_json(&scval).unwrap(), json_val);
1678
1679 let val = "-57896044618658097711785492504343953926634992332820282019728792003956564819968";
1681 let json_val = Value::String(val.to_string());
1682 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1683 assert_eq!(to_json(&scval).unwrap(), json_val);
1684
1685 let val = "0xff";
1687 let json_val = Value::String(val.to_string());
1688 let scval = from_json_primitives(&json_val, &ScType::I256).unwrap();
1689 let expected = Value::String("255".to_string());
1690 assert_eq!(to_json(&scval).unwrap(), expected);
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
1700 #[test]
1701 fn test_integer_conversion_edge_cases() {
1702 let json_val = Value::String("not_a_number".to_string());
1704 let result = from_json_primitives(&json_val, &ScType::U128);
1705 assert!(result.is_err());
1706
1707 let json_val = Value::String("not_a_number".to_string());
1709 let result = from_json_primitives(&json_val, &ScType::I128);
1710 assert!(result.is_err());
1711
1712 let json_val = Value::String("not_a_number".to_string());
1714 let result = from_json_primitives(&json_val, &ScType::U256);
1715 assert!(result.is_err());
1716
1717 let json_val = Value::String("not_a_number".to_string());
1719 let result = from_json_primitives(&json_val, &ScType::I256);
1720 assert!(result.is_err());
1721
1722 let json_val = Value::String("-42".to_string());
1724 let result = from_json_primitives(&json_val, &ScType::U128);
1725 assert!(result.is_err());
1726
1727 let json_val = Value::String("-42".to_string());
1729 let result = from_json_primitives(&json_val, &ScType::U256);
1730 assert!(result.is_err());
1731 }
1732
1733 #[test]
1734 fn test_basic_numeric_conversions() {
1735 let val = 42u32;
1737 let json_val = Value::Number(val.into());
1738 let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1739 assert_eq!(to_json(&scval).unwrap(), json_val);
1740
1741 let val = -42i32;
1743 let json_val = Value::Number(val.into());
1744 let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1745 assert_eq!(to_json(&scval).unwrap(), json_val);
1746
1747 let val = 42u64;
1749 let json_val = Value::Number(val.into());
1750 let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1751 assert_eq!(to_json(&scval).unwrap(), json_val);
1752
1753 let val = -42i64;
1755 let json_val = Value::Number(val.into());
1756 let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1757 assert_eq!(to_json(&scval).unwrap(), json_val);
1758
1759 let scval = from_json_primitives(&Value::Bool(true), &ScType::Bool).unwrap();
1761 assert_eq!(to_json(&scval).unwrap(), Value::Bool(true));
1762
1763 let scval = from_json_primitives(&Value::Bool(false), &ScType::Bool).unwrap();
1764 assert_eq!(to_json(&scval).unwrap(), Value::Bool(false));
1765 }
1766
1767 #[test]
1768 fn test_numeric_extremes() {
1769 let val = u32::MAX;
1771 let json_val = Value::Number(val.into());
1772 let scval = from_json_primitives(&json_val, &ScType::U32).unwrap();
1773 assert_eq!(to_json(&scval).unwrap(), json_val);
1774
1775 let val = i32::MIN;
1777 let json_val = Value::Number(val.into());
1778 let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1779 assert_eq!(to_json(&scval).unwrap(), json_val);
1780
1781 let val = i32::MAX;
1782 let json_val = Value::Number(val.into());
1783 let scval = from_json_primitives(&json_val, &ScType::I32).unwrap();
1784 assert_eq!(to_json(&scval).unwrap(), json_val);
1785
1786 let val = u64::MAX;
1788 let json_val = Value::Number(val.into());
1789 let scval = from_json_primitives(&json_val, &ScType::U64).unwrap();
1790 assert_eq!(to_json(&scval).unwrap(), json_val);
1791
1792 let val = i64::MIN;
1794 let json_val = Value::Number(val.into());
1795 let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1796 assert_eq!(to_json(&scval).unwrap(), json_val);
1797
1798 let val = i64::MAX;
1799 let json_val = Value::Number(val.into());
1800 let scval = from_json_primitives(&json_val, &ScType::I64).unwrap();
1801 assert_eq!(to_json(&scval).unwrap(), json_val);
1802 }
1803
1804 #[test]
1805 fn test_string_primitive_integration() {
1806 let val = "42";
1809 let scval = from_string_primitive(val, &ScType::U128).unwrap();
1810 assert_eq!(to_string(&scval).unwrap(), "\"42\"");
1811
1812 let val = "-42";
1814 let scval = from_string_primitive(val, &ScType::I128).unwrap();
1815 assert_eq!(to_string(&scval).unwrap(), "\"-42\"");
1816
1817 let val = "12345678901234567890123456789012345678901234567890";
1819 let scval = from_string_primitive(val, &ScType::U256).unwrap();
1820 assert_eq!(
1821 to_string(&scval).unwrap(),
1822 "\"12345678901234567890123456789012345678901234567890\""
1823 );
1824
1825 let val = "-12345678901234567890123456789012345678901234567890";
1827 let scval = from_string_primitive(val, &ScType::I256).unwrap();
1828 assert_eq!(
1829 to_string(&scval).unwrap(),
1830 "\"-12345678901234567890123456789012345678901234567890\""
1831 );
1832
1833 let scval = from_string_primitive("true", &ScType::Bool).unwrap();
1835 assert_eq!(to_string(&scval).unwrap(), "true");
1836
1837 let scval = from_string_primitive("false", &ScType::Bool).unwrap();
1838 assert_eq!(to_string(&scval).unwrap(), "false");
1839 }
1840}