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: {}",
153 type_tag
154 ))
155 })
156 }
157
158 fn data_impl(&self, layout: A::MoveTypeLayout) -> Result<MoveData, Error> {
159 MoveData::try_from(self.value_impl(layout)?)
160 }
161
162 fn json_impl(&self, layout: A::MoveTypeLayout) -> Result<Json, Error> {
163 Ok(try_to_json_value(self.value_impl(layout)?)?.into())
164 }
165}
166
167impl TryFrom<A::MoveValue> for MoveData {
168 type Error = Error;
169
170 fn try_from(value: A::MoveValue) -> Result<Self, Error> {
171 use A::MoveValue as V;
172
173 Ok(match value {
174 V::U8(n) => Self::Number(BigInt::from(n)),
175 V::U16(n) => Self::Number(BigInt::from(n)),
176 V::U32(n) => Self::Number(BigInt::from(n)),
177 V::U64(n) => Self::Number(BigInt::from(n)),
178 V::U128(n) => Self::Number(BigInt::from(n)),
179 V::U256(n) => Self::Number(BigInt::from(n)),
180
181 V::Bool(b) => Self::Bool(b),
182 V::Address(a) => Self::Address(a.into()),
183
184 V::Vector(v) => Self::Vector(
185 v.into_iter()
186 .map(MoveData::try_from)
187 .collect::<Result<Vec<_>, _>>()?,
188 ),
189
190 V::Struct(s) => {
191 let A::MoveStruct { type_, fields } = s;
192 if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
193 Self::Option(match extract_option(&type_, fields)? {
195 Some(value) => Some(Box::new(MoveData::try_from(value)?)),
196 None => None,
197 })
198 } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
199 || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
200 {
201 Self::String(extract_string(&type_, fields)?)
203 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
204 Self::Uid(extract_uid(&type_, fields)?.into())
206 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
207 Self::Id(extract_id(&type_, fields)?.into())
209 } else {
210 let fields: Result<Vec<_>, _> =
212 fields.into_iter().map(MoveField::try_from).collect();
213 Self::Struct(fields?)
214 }
215 }
216
217 V::Variant(A::MoveVariant {
218 type_: _,
219 variant_name,
220 tag: _,
221 fields,
222 }) => {
223 let fields = fields
224 .into_iter()
225 .map(MoveField::try_from)
226 .collect::<Result<_, _>>()?;
227 Self::Variant(MoveVariant {
228 name: variant_name.to_string(),
229 fields,
230 })
231 }
232
233 V::Signer(_) => return Err(unexpected_signer_error()),
235 })
236 }
237}
238
239impl TryFrom<(Identifier, A::MoveValue)> for MoveField {
240 type Error = Error;
241
242 fn try_from((ident, value): (Identifier, A::MoveValue)) -> Result<Self, Error> {
243 Ok(MoveField {
244 name: ident.to_string(),
245 value: MoveData::try_from(value)?,
246 })
247 }
248}
249
250fn try_to_json_value(value: A::MoveValue) -> Result<Value, Error> {
251 use A::MoveValue as V;
252 Ok(match value {
253 V::U8(n) => Value::Number(n.into()),
254 V::U16(n) => Value::Number(n.into()),
255 V::U32(n) => Value::Number(n.into()),
256 V::U64(n) => Value::String(n.to_string()),
257 V::U128(n) => Value::String(n.to_string()),
258 V::U256(n) => Value::String(n.to_string()),
259
260 V::Bool(b) => Value::Boolean(b),
261 V::Address(a) => Value::String(a.to_canonical_string(true)),
262
263 V::Vector(xs) => Value::List(
264 xs.into_iter()
265 .map(try_to_json_value)
266 .collect::<Result<_, _>>()?,
267 ),
268
269 V::Struct(s) => {
270 let A::MoveStruct { type_, fields } = s;
271 if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
272 match extract_option(&type_, fields)? {
274 Some(value) => try_to_json_value(value)?,
275 None => Value::Null,
276 }
277 } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
278 || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
279 {
280 Value::String(extract_string(&type_, fields)?)
282 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
283 Value::String(
285 extract_uid(&type_, fields)?.to_canonical_string(true),
286 )
287 } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
288 Value::String(
290 extract_id(&type_, fields)?.to_canonical_string(true),
291 )
292 } else {
293 Value::Object(
295 fields
296 .into_iter()
297 .map(|(name, value)| {
298 Ok((Name::new(name.to_string()), try_to_json_value(value)?))
299 })
300 .collect::<Result<_, Error>>()?,
301 )
302 }
303 }
304
305 V::Variant(A::MoveVariant {
306 type_: _,
307 variant_name,
308 tag: _,
309 fields,
310 }) => {
311 let fields = fields
312 .into_iter()
313 .map(|(name, value)| Ok((Name::new(name.to_string()), try_to_json_value(value)?)))
314 .collect::<Result<_, Error>>()?;
315 Value::Object(
316 vec![(Name::new(variant_name.to_string()), Value::Object(fields))]
317 .into_iter()
318 .collect(),
319 )
320 }
321 V::Signer(_) => return Err(unexpected_signer_error()),
323 })
324}
325
326fn is_type(tag: &StructTag, address: &AccountAddress, module: &IdentStr, name: &IdentStr) -> bool {
327 &tag.address == address
328 && tag.module.as_ident_str() == module
329 && tag.name.as_ident_str() == name
330}
331
332macro_rules! extract_field {
333 ($type:expr, $fields:expr, $name:ident) => {{
334 let _name = ident_str!(stringify!($name));
335 let _type = $type;
336 if let Some(value) = ($fields)
337 .into_iter()
338 .find_map(|(name, value)| (&*name == _name).then_some(value))
339 {
340 value
341 } else {
342 return Err(Error::Internal(format!(
343 "Couldn't find expected field '{_name}' of {_type}."
344 )));
345 }
346 }};
347}
348
349fn extract_bytes(value: A::MoveValue) -> Result<Vec<u8>, Error> {
352 use A::MoveValue as V;
353 let V::Vector(elements) = value else {
354 return Err(Error::Internal("Expected a vector.".to_string()));
355 };
356
357 let mut bytes = Vec::with_capacity(elements.len());
358 for element in elements {
359 let V::U8(byte) = element else {
360 return Err(Error::Internal("Expected a byte.".to_string()));
361 };
362 bytes.push(byte)
363 }
364
365 Ok(bytes)
366}
367
368fn extract_string(
378 type_: &StructTag,
379 fields: Vec<(Identifier, A::MoveValue)>,
380) -> Result<String, Error> {
381 let bytes = extract_bytes(extract_field!(type_, fields, bytes))?;
382 String::from_utf8(bytes).map_err(|e| {
383 const PREFIX: usize = 30;
384 let bytes = e.as_bytes();
385
386 let sample = if bytes.len() < PREFIX {
388 String::from_utf8_lossy(bytes)
389 } else {
390 String::from_utf8_lossy(&bytes[..PREFIX - 3]) + "..."
391 };
392
393 Error::Internal(format!("{e} in {sample:?}"))
394 })
395}
396
397fn extract_id(
406 type_: &StructTag,
407 fields: Vec<(Identifier, A::MoveValue)>,
408) -> Result<AccountAddress, Error> {
409 use A::MoveValue as V;
410 let V::Address(addr) = extract_field!(type_, fields, bytes) else {
411 return Err(Error::Internal(
412 "Expected ID.bytes to have type address.".to_string(),
413 ));
414 };
415
416 Ok(addr)
417}
418
419fn extract_uid(
428 type_: &StructTag,
429 fields: Vec<(Identifier, A::MoveValue)>,
430) -> Result<AccountAddress, Error> {
431 use A::MoveValue as V;
432 let V::Struct(s) = extract_field!(type_, fields, id) else {
433 return Err(Error::Internal(
434 "Expected UID.id to be a struct".to_string(),
435 ));
436 };
437
438 let A::MoveStruct { type_, fields } = s;
439 if !is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
440 return Err(Error::Internal(
441 "Expected UID.id to have type ID.".to_string(),
442 ));
443 }
444
445 extract_id(&type_, fields)
446}
447
448fn extract_option(
458 type_: &StructTag,
459 fields: Vec<(Identifier, A::MoveValue)>,
460) -> Result<Option<A::MoveValue>, Error> {
461 let A::MoveValue::Vector(mut elements) = extract_field!(type_, fields, vec) else {
462 return Err(Error::Internal(
463 "Expected Option.vec to be a vector.".to_string(),
464 ));
465 };
466
467 if elements.len() > 1 {
468 return Err(Error::Internal(
469 "Expected Option.vec to contain at most one element.".to_string(),
470 ));
471 };
472
473 Ok(elements.pop())
474}
475
476#[cfg(test)]
477mod tests {
478 use std::str::FromStr;
479
480 use expect_test::expect;
481 use move_core_types::{
482 annotated_value::{self as A, MoveFieldLayout, MoveStructLayout as S, MoveTypeLayout as L},
483 u256::U256,
484 };
485
486 use super::*;
487
488 macro_rules! struct_layout {
489 ($type:literal { $($name:literal : $layout:expr),* $(,)?}) => {
490 A::MoveTypeLayout::Struct(Box::new(S {
491 type_: StructTag::from_str($type).expect("Failed to parse struct"),
492 fields: vec![$(MoveFieldLayout {
493 name: ident_str!($name).to_owned(),
494 layout: $layout,
495 }),*]
496 }))
497 }
498 }
499
500 macro_rules! vector_layout {
501 ($inner:expr) => {
502 A::MoveTypeLayout::Vector(Box::new($inner))
503 };
504 }
505
506 fn address(a: &str) -> IotaAddress {
507 IotaAddress::from_str(a).unwrap()
508 }
509
510 fn data<T: Serialize>(layout: A::MoveTypeLayout, data: T) -> Result<MoveData, Error> {
511 let tag: TypeTag = (&layout).into();
512
513 data_with_tag(format!("{}", tag), layout, data)
517 }
518
519 fn data_with_tag<T: Serialize>(
520 tag: impl Into<String>,
521 layout: A::MoveTypeLayout,
522 data: T,
523 ) -> Result<MoveData, Error> {
524 let tag = TypeTag::from_str(tag.into().as_str()).unwrap();
525 let type_ = MoveType::new(tag);
526 let bcs = Base64(bcs::to_bytes(&data).unwrap());
527 MoveValue { type_, bcs }.data_impl(layout)
528 }
529
530 fn json<T: Serialize>(layout: A::MoveTypeLayout, data: T) -> Result<Json, Error> {
531 let tag: TypeTag = (&layout).into();
532 let type_ = MoveType::new(tag);
533 let bcs = Base64(bcs::to_bytes(&data).unwrap());
534 MoveValue { type_, bcs }.json_impl(layout)
535 }
536
537 #[test]
538 fn bool_data() {
539 let v = data(L::Bool, true);
540 let expect = expect!["Ok(Bool(true))"];
541 expect.assert_eq(&format!("{v:?}"));
542 }
543
544 #[test]
545 fn bool_json() {
546 let v = json(L::Bool, true).unwrap();
547 let expect = expect!["true"];
548 expect.assert_eq(&format!("{v}"));
549 }
550
551 #[test]
552 fn u8_data() {
553 let v = data(L::U8, 42u8);
554 let expect = expect![[r#"Ok(Number(BigInt("42")))"#]];
555 expect.assert_eq(&format!("{v:?}"));
556 }
557
558 #[test]
559 fn u8_json() {
560 let v = json(L::U8, 42u8).unwrap();
561 let expect = expect!["42"];
562 expect.assert_eq(&format!("{v}"));
563 }
564
565 #[test]
566 fn u16_data() {
567 let v = data(L::U16, 424u16);
568 let expect = expect![[r#"Ok(Number(BigInt("424")))"#]];
569 expect.assert_eq(&format!("{v:?}"));
570 }
571
572 #[test]
573 fn u16_json() {
574 let v = json(L::U16, 424u16).unwrap();
575 let expect = expect!["424"];
576 expect.assert_eq(&format!("{v}"));
577 }
578
579 #[test]
580 fn u32_data() {
581 let v = data(L::U32, 424_242u32);
582 let expect = expect![[r#"Ok(Number(BigInt("424242")))"#]];
583 expect.assert_eq(&format!("{v:?}"));
584 }
585
586 #[test]
587 fn u32_json() {
588 let v = json(L::U32, 424_242u32).unwrap();
589 let expect = expect!["424242"];
590 expect.assert_eq(&format!("{v}"));
591 }
592
593 #[test]
594 fn u64_data() {
595 let v = data(L::U64, 42_424_242_424u64);
596 let expect = expect![[r#"Ok(Number(BigInt("42424242424")))"#]];
597 expect.assert_eq(&format!("{v:?}"));
598 }
599
600 #[test]
601 fn u64_json() {
602 let v = json(L::U64, 42_424_242_424u64).unwrap();
603 let expect = expect![[r#""42424242424""#]];
604 expect.assert_eq(&format!("{v}"));
605 }
606
607 #[test]
608 fn u128_data() {
609 let v = data(L::U128, 424_242_424_242_424_242_424u128);
610 let expect = expect![[r#"Ok(Number(BigInt("424242424242424242424")))"#]];
611 expect.assert_eq(&format!("{v:?}"));
612 }
613
614 #[test]
615 fn u128_json() {
616 let v = json(L::U128, 424_242_424_242_424_242_424u128).unwrap();
617 let expect = expect![[r#""424242424242424242424""#]];
618 expect.assert_eq(&format!("{v}"));
619 }
620
621 #[test]
622 fn u256_data() {
623 let v = data(
624 L::U256,
625 U256::from_str("42424242424242424242424242424242424242424").unwrap(),
626 );
627 let expect =
628 expect![[r#"Ok(Number(BigInt("42424242424242424242424242424242424242424")))"#]];
629 expect.assert_eq(&format!("{v:?}"));
630 }
631
632 #[test]
633 fn u256_json() {
634 let v = json(
635 L::U256,
636 U256::from_str("42424242424242424242424242424242424242424").unwrap(),
637 )
638 .unwrap();
639 let expect = expect![[r#""42424242424242424242424242424242424242424""#]];
640 expect.assert_eq(&format!("{v}"));
641 }
642
643 #[test]
644 fn ascii_string_data() {
645 let l = struct_layout!("0x1::ascii::String" {
646 "bytes": vector_layout!(L::U8)
647 });
648
649 let v = data(l, "The quick brown fox");
650 let expect = expect![[r#"Ok(String("The quick brown fox"))"#]];
651 expect.assert_eq(&format!("{v:?}"));
652 }
653
654 #[test]
655 fn ascii_string_json() {
656 let l = struct_layout!("0x1::ascii::String" {
657 "bytes": vector_layout!(L::U8)
658 });
659
660 let v = json(l, "The quick brown fox").unwrap();
661 let expect = expect![[r#""The quick brown fox""#]];
662 expect.assert_eq(&format!("{v}"));
663 }
664
665 #[test]
666 fn utf8_string_data() {
667 let l = struct_layout!("0x1::string::String" {
668 "bytes": vector_layout!(L::U8)
669 });
670
671 let v = data(l, "jumped over the lazy dog.");
672 let expect = expect![[r#"Ok(String("jumped over the lazy dog."))"#]];
673 expect.assert_eq(&format!("{v:?}"));
674 }
675
676 #[test]
677 fn utf8_string_json() {
678 let l = struct_layout!("0x1::string::String" {
679 "bytes": vector_layout!(L::U8)
680 });
681
682 let v = json(l, "jumped over the lazy dog.").unwrap();
683 let expect = expect![[r#""jumped over the lazy dog.""#]];
684 expect.assert_eq(&format!("{v}"));
685 }
686
687 #[test]
688 fn string_encoding_error() {
689 let l = struct_layout!("0x1::string::String" {
690 "bytes": vector_layout!(L::U8)
691 });
692
693 let mut bytes = "Lorem ipsum dolor sit amet consectetur".as_bytes().to_vec();
694 bytes[5] = 0xff;
695
696 let v = data(l, bytes);
697 let expect = expect![[r#"
698 Err(
699 Internal(
700 "invalid utf-8 sequence of 1 bytes from index 5 in \"Lorem�ipsum dolor sit amet ...\"",
701 ),
702 )"#]];
703 expect.assert_eq(&format!("{v:#?}"));
704 }
705
706 #[test]
707 fn address_data() {
708 let v = data(L::Address, address("0x42"));
709 let expect = expect![
710 "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])))"
711 ];
712 expect.assert_eq(&format!("{v:?}"));
713 }
714
715 #[test]
716 fn address_json() {
717 let v = json(L::Address, address("0x42")).unwrap();
718 let expect =
719 expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
720 expect.assert_eq(&format!("{v}"));
721 }
722
723 #[test]
724 fn uid_data() {
725 let l = struct_layout!("0x2::object::UID" {
726 "id": struct_layout!("0x2::object::ID" {
727 "bytes": L::Address,
728 })
729 });
730
731 let v = data(l, address("0x42"));
732 let expect = expect![
733 "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])))"
734 ];
735 expect.assert_eq(&format!("{v:?}"));
736 }
737
738 #[test]
739 fn uid_json() {
740 let l = struct_layout!("0x2::object::UID" {
741 "id": struct_layout!("0x2::object::ID" {
742 "bytes": L::Address,
743 })
744 });
745
746 let v = json(l, address("0x42")).unwrap();
747 let expect =
748 expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
749 expect.assert_eq(&format!("{v}"));
750 }
751
752 #[test]
753 fn compound_data() {
754 let l = struct_layout!("0x42::foo::Bar" {
755 "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
756 "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
757 "quy": L::U16,
758 "quz": struct_layout!("0x1::option::Option" {
759 "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
760 "bytes": vector_layout!(L::U8),
761 }))
762 }),
763 "frob": L::Address,
764 })),
765 });
766
767 let v = data(
768 l,
769 (
770 vec![] as Vec<Vec<u8>>,
771 vec![
772 (44u16, vec!["Hello, world!"], address("0x45")),
773 (46u16, vec![], address("0x47")),
774 ],
775 ),
776 );
777
778 let expect = expect![[r#"
779 Ok(
780 Struct(
781 [
782 MoveField {
783 name: "baz",
784 value: Option(
785 None,
786 ),
787 },
788 MoveField {
789 name: "qux",
790 value: Vector(
791 [
792 Struct(
793 [
794 MoveField {
795 name: "quy",
796 value: Number(
797 BigInt(
798 "44",
799 ),
800 ),
801 },
802 MoveField {
803 name: "quz",
804 value: Option(
805 Some(
806 String(
807 "Hello, world!",
808 ),
809 ),
810 ),
811 },
812 MoveField {
813 name: "frob",
814 value: Address(
815 IotaAddress(
816 [
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 0,
848 69,
849 ],
850 ),
851 ),
852 },
853 ],
854 ),
855 Struct(
856 [
857 MoveField {
858 name: "quy",
859 value: Number(
860 BigInt(
861 "46",
862 ),
863 ),
864 },
865 MoveField {
866 name: "quz",
867 value: Option(
868 None,
869 ),
870 },
871 MoveField {
872 name: "frob",
873 value: Address(
874 IotaAddress(
875 [
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 0,
907 71,
908 ],
909 ),
910 ),
911 },
912 ],
913 ),
914 ],
915 ),
916 },
917 ],
918 ),
919 )"#]];
920 expect.assert_eq(&format!("{v:#?}"));
921 }
922
923 #[test]
924 fn compound_json() {
925 let l = struct_layout!("0x42::foo::Bar" {
926 "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
927 "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
928 "quy": L::U16,
929 "quz": struct_layout!("0x1::option::Option" {
930 "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
931 "bytes": vector_layout!(L::U8),
932 }))
933 }),
934 "frob": L::Address,
935 })),
936 });
937
938 let v = json(
939 l,
940 (
941 vec![] as Vec<Vec<u8>>,
942 vec![
943 (44u16, vec!["Hello, world!"], address("0x45")),
944 (46u16, vec![], address("0x47")),
945 ],
946 ),
947 )
948 .unwrap();
949
950 let expect = expect![[
951 r#"{baz: null, qux: [{quy: 44, quz: "Hello, world!", frob: "0x0000000000000000000000000000000000000000000000000000000000000045"}, {quy: 46, quz: null, frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"#
952 ]];
953 expect.assert_eq(&format!("{v}"));
954 }
955
956 #[test]
957 fn signer_value() {
958 let v = data(L::Signer, address("0x42"));
959 let expect = expect![[r#"
960 Err(
961 Internal(
962 "Unexpected value of type: signer.",
963 ),
964 )"#]];
965 expect.assert_eq(&format!("{v:#?}"));
966 }
967
968 #[test]
969 fn signer_json() {
970 let err = json(L::Signer, address("0x42")).unwrap_err();
971 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
972 expect.assert_eq(&format!("{err:?}"));
973 }
974
975 #[test]
976 fn signer_nested_data() {
977 let v = data(
978 vector_layout!(L::Signer),
979 vec![address("0x42"), address("0x43")],
980 );
981 let expect = expect![[r#"
982 Err(
983 Internal(
984 "Unexpected value of type: signer.",
985 ),
986 )"#]];
987 expect.assert_eq(&format!("{v:#?}"));
988 }
989
990 #[test]
991 fn signer_nested_json() {
992 let err = json(
993 vector_layout!(L::Signer),
994 vec![address("0x42"), address("0x43")],
995 )
996 .unwrap_err();
997
998 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
999 expect.assert_eq(&format!("{err:?}"));
1000 }
1001}