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