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