1use async_graphql::*;
6use iota_types::object::bounded_visitor::BoundedVisitor;
7use move_core_types::{
8 account_address::AccountAddress,
9 annotated_value as A, ident_str,
10 identifier::{IdentStr, Identifier},
11 language_storage::{StructTag, TypeTag},
12};
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 data::package_resolver::PackageResolver,
17 error::Error,
18 types::{
19 base64::Base64,
20 big_int::BigInt,
21 iota_address::IotaAddress,
22 json::Json,
23 move_type::{MoveType, unexpected_signer_error},
24 },
25};
26
27const STD: AccountAddress = AccountAddress::ONE;
28const IOTA: AccountAddress = AccountAddress::TWO;
29
30const MOD_ASCII: &IdentStr = ident_str!("ascii");
31const MOD_OBJECT: &IdentStr = ident_str!("object");
32const MOD_OPTION: &IdentStr = ident_str!("option");
33const MOD_STRING: &IdentStr = ident_str!("string");
34
35const TYP_ID: &IdentStr = ident_str!("ID");
36const TYP_OPTION: &IdentStr = ident_str!("Option");
37const TYP_STRING: &IdentStr = ident_str!("String");
38const TYP_UID: &IdentStr = ident_str!("UID");
39
40#[derive(SimpleObject)]
41#[graphql(complex)]
42pub(crate) struct MoveValue {
43 #[graphql(name = "type")]
45 type_: MoveType,
46 bcs: Base64,
48}
49
50scalar!(
51 MoveData,
52 "MoveData",
53 "The contents of a Move Value, corresponding to the following recursive type:
54
55type MoveData =
56 { Address: IotaAddress }
57 | { UID: IotaAddress }
58 | { ID: IotaAddress }
59 | { Bool: bool }
60 | { Number: BigInt }
61 | { String: string }
62 | { Vector: [MoveData] }
63 | { Option: MoveData? }
64 | { Struct: [{ name: string , value: MoveData }] }
65 | { Variant: {
66 name: string,
67 fields: [{ name: string, value: MoveData }],
68 }"
69);
70
71#[derive(Serialize, Deserialize, Debug)]
72pub(crate) enum MoveData {
73 Address(IotaAddress),
74 #[serde(rename = "UID")]
75 Uid(IotaAddress),
76 #[serde(rename = "ID")]
77 Id(IotaAddress),
78 Bool(bool),
79 Number(BigInt),
80 String(String),
81 Vector(Vec<MoveData>),
82 Option(Option<Box<MoveData>>),
83 Struct(Vec<MoveField>),
84 Variant(MoveVariant),
85}
86
87#[derive(Serialize, Deserialize, Debug)]
88pub(crate) struct MoveVariant {
89 name: String,
90 fields: Vec<MoveField>,
91}
92
93#[derive(Serialize, Deserialize, Debug)]
94pub(crate) struct MoveField {
95 name: String,
96 value: MoveData,
97}
98
99#[ComplexObject]
101impl MoveValue {
102 async fn data(&self, ctx: &Context<'_>) -> Result<MoveData> {
104 let resolver: &PackageResolver = ctx
105 .data()
106 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
107 .extend()?;
108
109 self.data_impl(self.type_.layout_impl(resolver).await.extend()?)
112 .extend()
113 }
114
115 async fn json(&self, ctx: &Context<'_>) -> Result<Json> {
129 let resolver: &PackageResolver = ctx
130 .data()
131 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
132 .extend()?;
133
134 self.json_impl(self.type_.layout_impl(resolver).await.extend()?)
137 .extend()
138 }
139}
140
141impl MoveValue {
142 pub fn new(tag: TypeTag, bcs: Base64) -> Self {
143 let type_ = MoveType::new(tag);
144 Self { type_, bcs }
145 }
146
147 fn value_impl(&self, layout: A::MoveTypeLayout) -> Result<A::MoveValue, Error> {
148 BoundedVisitor::deserialize_value(&self.bcs.0[..], &layout).map_err(|_| {
150 let type_tag: TypeTag = (&layout).into();
151 Error::Internal(format!(
152 "Failed to deserialize Move value for type: {type_tag}"
153 ))
154 })
155 }
156
157 fn data_impl(&self, layout: A::MoveTypeLayout) -> Result<MoveData, Error> {
158 MoveData::try_from(self.value_impl(layout)?)
159 }
160
161 fn json_impl(&self, layout: A::MoveTypeLayout) -> Result<Json, Error> {
162 Ok(try_to_json_value(self.value_impl(layout)?)?.into())
163 }
164}
165
166impl TryFrom<A::MoveValue> for MoveData {
167 type Error = Error;
168
169 fn try_from(value: A::MoveValue) -> Result<Self, Error> {
170 use A::MoveValue as V;
171
172 Ok(match value {
173 V::U8(n) => Self::Number(BigInt::from(n)),
174 V::U16(n) => Self::Number(BigInt::from(n)),
175 V::U32(n) => Self::Number(BigInt::from(n)),
176 V::U64(n) => Self::Number(BigInt::from(n)),
177 V::U128(n) => Self::Number(BigInt::from(n)),
178 V::U256(n) => Self::Number(BigInt::from(n)),
179
180 V::Bool(b) => Self::Bool(b),
181 V::Address(a) => Self::Address(a.into()),
182
183 V::Vector(v) => Self::Vector(
184 v.into_iter()
185 .map(MoveData::try_from)
186 .collect::<Result<Vec<_>, _>>()?,
187 ),
188
189 V::Struct(s) => {
190 let A::MoveStruct { type_, fields } = s;
191 if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
192 Self::Option(match extract_option(&type_, fields)? {
194 Some(value) => Some(Box::new(MoveData::try_from(value)?)),
195 None => None,
196 })
197 } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
198 || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
199 {
200 Self::String(extract_string(&type_, fields)?)
202 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
203 Self::Uid(extract_uid(&type_, fields)?.into())
205 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
206 Self::Id(extract_id(&type_, fields)?.into())
208 } else {
209 let fields: Result<Vec<_>, _> =
211 fields.into_iter().map(MoveField::try_from).collect();
212 Self::Struct(fields?)
213 }
214 }
215
216 V::Variant(A::MoveVariant {
217 type_: _,
218 variant_name,
219 tag: _,
220 fields,
221 }) => {
222 let fields = fields
223 .into_iter()
224 .map(MoveField::try_from)
225 .collect::<Result<_, _>>()?;
226 Self::Variant(MoveVariant {
227 name: variant_name.to_string(),
228 fields,
229 })
230 }
231
232 V::Signer(_) => return Err(unexpected_signer_error()),
234 })
235 }
236}
237
238impl TryFrom<(Identifier, A::MoveValue)> for MoveField {
239 type Error = Error;
240
241 fn try_from((ident, value): (Identifier, A::MoveValue)) -> Result<Self, Error> {
242 Ok(MoveField {
243 name: ident.to_string(),
244 value: MoveData::try_from(value)?,
245 })
246 }
247}
248
249fn try_to_json_value(value: A::MoveValue) -> Result<Value, Error> {
250 use A::MoveValue as V;
251 Ok(match value {
252 V::U8(n) => Value::Number(n.into()),
253 V::U16(n) => Value::Number(n.into()),
254 V::U32(n) => Value::Number(n.into()),
255 V::U64(n) => Value::String(n.to_string()),
256 V::U128(n) => Value::String(n.to_string()),
257 V::U256(n) => Value::String(n.to_string()),
258
259 V::Bool(b) => Value::Boolean(b),
260 V::Address(a) => Value::String(a.to_canonical_string(true)),
261
262 V::Vector(xs) => Value::List(
263 xs.into_iter()
264 .map(try_to_json_value)
265 .collect::<Result<_, _>>()?,
266 ),
267
268 V::Struct(s) => {
269 let A::MoveStruct { type_, fields } = s;
270 if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
271 match extract_option(&type_, fields)? {
273 Some(value) => try_to_json_value(value)?,
274 None => Value::Null,
275 }
276 } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
277 || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
278 {
279 Value::String(extract_string(&type_, fields)?)
281 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
282 Value::String(
284 extract_uid(&type_, fields)?.to_canonical_string(true),
285 )
286 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
287 Value::String(
289 extract_id(&type_, fields)?.to_canonical_string(true),
290 )
291 } else {
292 Value::Object(
294 fields
295 .into_iter()
296 .map(|(name, value)| {
297 Ok((Name::new(name.to_string()), try_to_json_value(value)?))
298 })
299 .collect::<Result<_, Error>>()?,
300 )
301 }
302 }
303
304 V::Variant(A::MoveVariant {
305 type_: _,
306 variant_name,
307 tag: _,
308 fields,
309 }) => {
310 let fields = fields
311 .into_iter()
312 .map(|(name, value)| Ok((Name::new(name.to_string()), try_to_json_value(value)?)))
313 .collect::<Result<_, Error>>()?;
314 Value::Object(
315 vec![(Name::new(variant_name.to_string()), Value::Object(fields))]
316 .into_iter()
317 .collect(),
318 )
319 }
320 V::Signer(_) => return Err(unexpected_signer_error()),
322 })
323}
324
325fn is_type(tag: &StructTag, address: &AccountAddress, module: &IdentStr, name: &IdentStr) -> bool {
326 &tag.address == address
327 && tag.module.as_ident_str() == module
328 && tag.name.as_ident_str() == name
329}
330
331macro_rules! extract_field {
332 ($type:expr, $fields:expr, $name:ident) => {{
333 let _name = ident_str!(stringify!($name));
334 let _type = $type;
335 if let Some(value) = ($fields)
336 .into_iter()
337 .find_map(|(name, value)| (&*name == _name).then_some(value))
338 {
339 value
340 } else {
341 return Err(Error::Internal(format!(
342 "Couldn't find expected field '{_name}' of {_type}."
343 )));
344 }
345 }};
346}
347
348fn extract_bytes(value: A::MoveValue) -> Result<Vec<u8>, Error> {
351 use A::MoveValue as V;
352 let V::Vector(elements) = value else {
353 return Err(Error::Internal("Expected a vector.".to_string()));
354 };
355
356 let mut bytes = Vec::with_capacity(elements.len());
357 for element in elements {
358 let V::U8(byte) = element else {
359 return Err(Error::Internal("Expected a byte.".to_string()));
360 };
361 bytes.push(byte)
362 }
363
364 Ok(bytes)
365}
366
367fn extract_string(
377 type_: &StructTag,
378 fields: Vec<(Identifier, A::MoveValue)>,
379) -> Result<String, Error> {
380 let bytes = extract_bytes(extract_field!(type_, fields, bytes))?;
381 String::from_utf8(bytes).map_err(|e| {
382 const PREFIX: usize = 30;
383 let bytes = e.as_bytes();
384
385 let sample = if bytes.len() < PREFIX {
387 String::from_utf8_lossy(bytes)
388 } else {
389 String::from_utf8_lossy(&bytes[..PREFIX - 3]) + "..."
390 };
391
392 Error::Internal(format!("{e} in {sample:?}"))
393 })
394}
395
396fn extract_id(
405 type_: &StructTag,
406 fields: Vec<(Identifier, A::MoveValue)>,
407) -> Result<AccountAddress, Error> {
408 use A::MoveValue as V;
409 let V::Address(addr) = extract_field!(type_, fields, bytes) else {
410 return Err(Error::Internal(
411 "Expected ID.bytes to have type address.".to_string(),
412 ));
413 };
414
415 Ok(addr)
416}
417
418fn extract_uid(
427 type_: &StructTag,
428 fields: Vec<(Identifier, A::MoveValue)>,
429) -> Result<AccountAddress, Error> {
430 use A::MoveValue as V;
431 let V::Struct(s) = extract_field!(type_, fields, id) else {
432 return Err(Error::Internal(
433 "Expected UID.id to be a struct".to_string(),
434 ));
435 };
436
437 let A::MoveStruct { type_, fields } = s;
438 if !is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
439 return Err(Error::Internal(
440 "Expected UID.id to have type ID.".to_string(),
441 ));
442 }
443
444 extract_id(&type_, fields)
445}
446
447fn extract_option(
457 type_: &StructTag,
458 fields: Vec<(Identifier, A::MoveValue)>,
459) -> Result<Option<A::MoveValue>, Error> {
460 let A::MoveValue::Vector(mut elements) = extract_field!(type_, fields, vec) else {
461 return Err(Error::Internal(
462 "Expected Option.vec to be a vector.".to_string(),
463 ));
464 };
465
466 if elements.len() > 1 {
467 return Err(Error::Internal(
468 "Expected Option.vec to contain at most one element.".to_string(),
469 ));
470 };
471
472 Ok(elements.pop())
473}
474
475#[cfg(test)]
476mod tests {
477 use std::str::FromStr;
478
479 use expect_test::expect;
480 use move_core_types::{
481 annotated_value::{self as A, MoveFieldLayout, MoveStructLayout as S, MoveTypeLayout as L},
482 u256::U256,
483 };
484
485 use super::*;
486
487 macro_rules! struct_layout {
488 ($type:literal { $($name:literal : $layout:expr),* $(,)?}) => {
489 A::MoveTypeLayout::Struct(Box::new(S {
490 type_: StructTag::from_str($type).expect("Failed to parse struct"),
491 fields: vec![$(MoveFieldLayout {
492 name: ident_str!($name).to_owned(),
493 layout: $layout,
494 }),*]
495 }))
496 }
497 }
498
499 macro_rules! vector_layout {
500 ($inner:expr) => {
501 A::MoveTypeLayout::Vector(Box::new($inner))
502 };
503 }
504
505 fn address(a: &str) -> IotaAddress {
506 IotaAddress::from_str(a).unwrap()
507 }
508
509 fn data<T: Serialize>(layout: A::MoveTypeLayout, data: T) -> Result<MoveData, Error> {
510 let tag: TypeTag = (&layout).into();
511
512 data_with_tag(format!("{tag}"), layout, data)
516 }
517
518 fn data_with_tag<T: Serialize>(
519 tag: impl Into<String>,
520 layout: A::MoveTypeLayout,
521 data: T,
522 ) -> Result<MoveData, Error> {
523 let tag = TypeTag::from_str(tag.into().as_str()).unwrap();
524 let type_ = MoveType::new(tag);
525 let bcs = Base64(bcs::to_bytes(&data).unwrap());
526 MoveValue { type_, bcs }.data_impl(layout)
527 }
528
529 fn json<T: Serialize>(layout: A::MoveTypeLayout, data: T) -> Result<Json, Error> {
530 let tag: TypeTag = (&layout).into();
531 let type_ = MoveType::new(tag);
532 let bcs = Base64(bcs::to_bytes(&data).unwrap());
533 MoveValue { type_, bcs }.json_impl(layout)
534 }
535
536 #[test]
537 fn bool_data() {
538 let v = data(L::Bool, true);
539 let expect = expect!["Ok(Bool(true))"];
540 expect.assert_eq(&format!("{v:?}"));
541 }
542
543 #[test]
544 fn bool_json() {
545 let v = json(L::Bool, true).unwrap();
546 let expect = expect!["true"];
547 expect.assert_eq(&format!("{v}"));
548 }
549
550 #[test]
551 fn u8_data() {
552 let v = data(L::U8, 42u8);
553 let expect = expect![[r#"Ok(Number(BigInt("42")))"#]];
554 expect.assert_eq(&format!("{v:?}"));
555 }
556
557 #[test]
558 fn u8_json() {
559 let v = json(L::U8, 42u8).unwrap();
560 let expect = expect!["42"];
561 expect.assert_eq(&format!("{v}"));
562 }
563
564 #[test]
565 fn u16_data() {
566 let v = data(L::U16, 424u16);
567 let expect = expect![[r#"Ok(Number(BigInt("424")))"#]];
568 expect.assert_eq(&format!("{v:?}"));
569 }
570
571 #[test]
572 fn u16_json() {
573 let v = json(L::U16, 424u16).unwrap();
574 let expect = expect!["424"];
575 expect.assert_eq(&format!("{v}"));
576 }
577
578 #[test]
579 fn u32_data() {
580 let v = data(L::U32, 424_242u32);
581 let expect = expect![[r#"Ok(Number(BigInt("424242")))"#]];
582 expect.assert_eq(&format!("{v:?}"));
583 }
584
585 #[test]
586 fn u32_json() {
587 let v = json(L::U32, 424_242u32).unwrap();
588 let expect = expect!["424242"];
589 expect.assert_eq(&format!("{v}"));
590 }
591
592 #[test]
593 fn u64_data() {
594 let v = data(L::U64, 42_424_242_424u64);
595 let expect = expect![[r#"Ok(Number(BigInt("42424242424")))"#]];
596 expect.assert_eq(&format!("{v:?}"));
597 }
598
599 #[test]
600 fn u64_json() {
601 let v = json(L::U64, 42_424_242_424u64).unwrap();
602 let expect = expect![[r#""42424242424""#]];
603 expect.assert_eq(&format!("{v}"));
604 }
605
606 #[test]
607 fn u128_data() {
608 let v = data(L::U128, 424_242_424_242_424_242_424u128);
609 let expect = expect![[r#"Ok(Number(BigInt("424242424242424242424")))"#]];
610 expect.assert_eq(&format!("{v:?}"));
611 }
612
613 #[test]
614 fn u128_json() {
615 let v = json(L::U128, 424_242_424_242_424_242_424u128).unwrap();
616 let expect = expect![[r#""424242424242424242424""#]];
617 expect.assert_eq(&format!("{v}"));
618 }
619
620 #[test]
621 fn u256_data() {
622 let v = data(
623 L::U256,
624 U256::from_str("42424242424242424242424242424242424242424").unwrap(),
625 );
626 let expect =
627 expect![[r#"Ok(Number(BigInt("42424242424242424242424242424242424242424")))"#]];
628 expect.assert_eq(&format!("{v:?}"));
629 }
630
631 #[test]
632 fn u256_json() {
633 let v = json(
634 L::U256,
635 U256::from_str("42424242424242424242424242424242424242424").unwrap(),
636 )
637 .unwrap();
638 let expect = expect![[r#""42424242424242424242424242424242424242424""#]];
639 expect.assert_eq(&format!("{v}"));
640 }
641
642 #[test]
643 fn ascii_string_data() {
644 let l = struct_layout!("0x1::ascii::String" {
645 "bytes": vector_layout!(L::U8)
646 });
647
648 let v = data(l, "The quick brown fox");
649 let expect = expect![[r#"Ok(String("The quick brown fox"))"#]];
650 expect.assert_eq(&format!("{v:?}"));
651 }
652
653 #[test]
654 fn ascii_string_json() {
655 let l = struct_layout!("0x1::ascii::String" {
656 "bytes": vector_layout!(L::U8)
657 });
658
659 let v = json(l, "The quick brown fox").unwrap();
660 let expect = expect![[r#""The quick brown fox""#]];
661 expect.assert_eq(&format!("{v}"));
662 }
663
664 #[test]
665 fn utf8_string_data() {
666 let l = struct_layout!("0x1::string::String" {
667 "bytes": vector_layout!(L::U8)
668 });
669
670 let v = data(l, "jumped over the lazy dog.");
671 let expect = expect![[r#"Ok(String("jumped over the lazy dog."))"#]];
672 expect.assert_eq(&format!("{v:?}"));
673 }
674
675 #[test]
676 fn utf8_string_json() {
677 let l = struct_layout!("0x1::string::String" {
678 "bytes": vector_layout!(L::U8)
679 });
680
681 let v = json(l, "jumped over the lazy dog.").unwrap();
682 let expect = expect![[r#""jumped over the lazy dog.""#]];
683 expect.assert_eq(&format!("{v}"));
684 }
685
686 #[test]
687 fn string_encoding_error() {
688 let l = struct_layout!("0x1::string::String" {
689 "bytes": vector_layout!(L::U8)
690 });
691
692 let mut bytes = "Lorem ipsum dolor sit amet consectetur".as_bytes().to_vec();
693 bytes[5] = 0xff;
694
695 let v = data(l, bytes);
696 let expect = expect![[r#"
697 Err(
698 Internal(
699 "invalid utf-8 sequence of 1 bytes from index 5 in \"Lorem�ipsum dolor sit amet ...\"",
700 ),
701 )"#]];
702 expect.assert_eq(&format!("{v:#?}"));
703 }
704
705 #[test]
706 fn address_data() {
707 let v = data(L::Address, address("0x42"));
708 let expect = expect![
709 "Ok(Address(IotaAddress([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66])))"
710 ];
711 expect.assert_eq(&format!("{v:?}"));
712 }
713
714 #[test]
715 fn address_json() {
716 let v = json(L::Address, address("0x42")).unwrap();
717 let expect =
718 expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
719 expect.assert_eq(&format!("{v}"));
720 }
721
722 #[test]
723 fn uid_data() {
724 let l = struct_layout!("0x2::object::UID" {
725 "id": struct_layout!("0x2::object::ID" {
726 "bytes": L::Address,
727 })
728 });
729
730 let v = data(l, address("0x42"));
731 let expect = expect![
732 "Ok(Uid(IotaAddress([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66])))"
733 ];
734 expect.assert_eq(&format!("{v:?}"));
735 }
736
737 #[test]
738 fn uid_json() {
739 let l = struct_layout!("0x2::object::UID" {
740 "id": struct_layout!("0x2::object::ID" {
741 "bytes": L::Address,
742 })
743 });
744
745 let v = json(l, address("0x42")).unwrap();
746 let expect =
747 expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
748 expect.assert_eq(&format!("{v}"));
749 }
750
751 #[test]
752 fn compound_data() {
753 let l = struct_layout!("0x42::foo::Bar" {
754 "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
755 "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
756 "quy": L::U16,
757 "quz": struct_layout!("0x1::option::Option" {
758 "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
759 "bytes": vector_layout!(L::U8),
760 }))
761 }),
762 "frob": L::Address,
763 })),
764 });
765
766 let v = data(
767 l,
768 (
769 vec![] as Vec<Vec<u8>>,
770 vec![
771 (44u16, vec!["Hello, world!"], address("0x45")),
772 (46u16, vec![], address("0x47")),
773 ],
774 ),
775 );
776
777 let expect = expect![[r#"
778 Ok(
779 Struct(
780 [
781 MoveField {
782 name: "baz",
783 value: Option(
784 None,
785 ),
786 },
787 MoveField {
788 name: "qux",
789 value: Vector(
790 [
791 Struct(
792 [
793 MoveField {
794 name: "quy",
795 value: Number(
796 BigInt(
797 "44",
798 ),
799 ),
800 },
801 MoveField {
802 name: "quz",
803 value: Option(
804 Some(
805 String(
806 "Hello, world!",
807 ),
808 ),
809 ),
810 },
811 MoveField {
812 name: "frob",
813 value: Address(
814 IotaAddress(
815 [
816 0,
817 0,
818 0,
819 0,
820 0,
821 0,
822 0,
823 0,
824 0,
825 0,
826 0,
827 0,
828 0,
829 0,
830 0,
831 0,
832 0,
833 0,
834 0,
835 0,
836 0,
837 0,
838 0,
839 0,
840 0,
841 0,
842 0,
843 0,
844 0,
845 0,
846 0,
847 69,
848 ],
849 ),
850 ),
851 },
852 ],
853 ),
854 Struct(
855 [
856 MoveField {
857 name: "quy",
858 value: Number(
859 BigInt(
860 "46",
861 ),
862 ),
863 },
864 MoveField {
865 name: "quz",
866 value: Option(
867 None,
868 ),
869 },
870 MoveField {
871 name: "frob",
872 value: Address(
873 IotaAddress(
874 [
875 0,
876 0,
877 0,
878 0,
879 0,
880 0,
881 0,
882 0,
883 0,
884 0,
885 0,
886 0,
887 0,
888 0,
889 0,
890 0,
891 0,
892 0,
893 0,
894 0,
895 0,
896 0,
897 0,
898 0,
899 0,
900 0,
901 0,
902 0,
903 0,
904 0,
905 0,
906 71,
907 ],
908 ),
909 ),
910 },
911 ],
912 ),
913 ],
914 ),
915 },
916 ],
917 ),
918 )"#]];
919 expect.assert_eq(&format!("{v:#?}"));
920 }
921
922 #[test]
923 fn compound_json() {
924 let l = struct_layout!("0x42::foo::Bar" {
925 "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
926 "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
927 "quy": L::U16,
928 "quz": struct_layout!("0x1::option::Option" {
929 "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
930 "bytes": vector_layout!(L::U8),
931 }))
932 }),
933 "frob": L::Address,
934 })),
935 });
936
937 let v = json(
938 l,
939 (
940 vec![] as Vec<Vec<u8>>,
941 vec![
942 (44u16, vec!["Hello, world!"], address("0x45")),
943 (46u16, vec![], address("0x47")),
944 ],
945 ),
946 )
947 .unwrap();
948
949 let expect = expect![[
950 r#"{baz: null, qux: [{quy: 44, quz: "Hello, world!", frob: "0x0000000000000000000000000000000000000000000000000000000000000045"}, {quy: 46, quz: null, frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"#
951 ]];
952 expect.assert_eq(&format!("{v}"));
953 }
954
955 #[test]
956 fn signer_value() {
957 let v = data(L::Signer, address("0x42"));
958 let expect = expect![[r#"
959 Err(
960 Internal(
961 "Unexpected value of type: signer.",
962 ),
963 )"#]];
964 expect.assert_eq(&format!("{v:#?}"));
965 }
966
967 #[test]
968 fn signer_json() {
969 let err = json(L::Signer, address("0x42")).unwrap_err();
970 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
971 expect.assert_eq(&format!("{err:?}"));
972 }
973
974 #[test]
975 fn signer_nested_data() {
976 let v = data(
977 vector_layout!(L::Signer),
978 vec![address("0x42"), address("0x43")],
979 );
980 let expect = expect![[r#"
981 Err(
982 Internal(
983 "Unexpected value of type: signer.",
984 ),
985 )"#]];
986 expect.assert_eq(&format!("{v:#?}"));
987 }
988
989 #[test]
990 fn signer_nested_json() {
991 let err = json(
992 vector_layout!(L::Signer),
993 vec![address("0x42"), address("0x43")],
994 )
995 .unwrap_err();
996
997 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
998 expect.assert_eq(&format!("{err:?}"));
999 }
1000}