sui_graphql_rpc/types/
move_value.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use async_graphql::*;
5use move_core_types::{
6    account_address::AccountAddress,
7    annotated_value as A, ident_str,
8    identifier::{IdentStr, Identifier},
9    language_storage::{StructTag, TypeTag},
10};
11use serde::{Deserialize, Serialize};
12use sui_types::object::bounded_visitor::BoundedVisitor;
13
14use crate::data::package_resolver::PackageResolver;
15use crate::{error::Error, types::json::Json, types::move_type::unexpected_signer_error};
16
17use super::{base64::Base64, big_int::BigInt, move_type::MoveType, sui_address::SuiAddress};
18
19const STD: AccountAddress = AccountAddress::ONE;
20const SUI: AccountAddress = AccountAddress::TWO;
21
22const MOD_ASCII: &IdentStr = ident_str!("ascii");
23const MOD_OBJECT: &IdentStr = ident_str!("object");
24const MOD_OPTION: &IdentStr = ident_str!("option");
25const MOD_STRING: &IdentStr = ident_str!("string");
26
27const TYP_ID: &IdentStr = ident_str!("ID");
28const TYP_OPTION: &IdentStr = ident_str!("Option");
29const TYP_STRING: &IdentStr = ident_str!("String");
30const TYP_UID: &IdentStr = ident_str!("UID");
31
32#[derive(SimpleObject)]
33#[graphql(complex)]
34pub(crate) struct MoveValue {
35    /// The value's Move type.
36    #[graphql(name = "type")]
37    type_: MoveType,
38    /// The BCS representation of this value, Base64 encoded.
39    bcs: Base64,
40}
41
42scalar!(
43    MoveData,
44    "MoveData",
45    "The contents of a Move Value, corresponding to the following recursive type:
46
47type MoveData =
48    { Address: SuiAddress }
49  | { UID:     SuiAddress }
50  | { ID:      SuiAddress }
51  | { Bool:    bool }
52  | { Number:  BigInt }
53  | { String:  string }
54  | { Vector:  [MoveData] }
55  | { Option:   MoveData? }
56  | { Struct:  [{ name: string , value: MoveData }] }
57  | { Variant: {
58      name: string,
59      fields: [{ name: string, value: MoveData }],
60  }"
61);
62
63#[derive(Serialize, Deserialize, Debug)]
64pub(crate) enum MoveData {
65    Address(SuiAddress),
66    #[serde(rename = "UID")]
67    Uid(SuiAddress),
68    #[serde(rename = "ID")]
69    Id(SuiAddress),
70    Bool(bool),
71    Number(BigInt),
72    String(String),
73    Vector(Vec<MoveData>),
74    Option(Option<Box<MoveData>>),
75    Struct(Vec<MoveField>),
76    Variant(MoveVariant),
77}
78
79#[derive(Serialize, Deserialize, Debug)]
80pub(crate) struct MoveVariant {
81    name: String,
82    fields: Vec<MoveField>,
83}
84
85#[derive(Serialize, Deserialize, Debug)]
86pub(crate) struct MoveField {
87    name: String,
88    value: MoveData,
89}
90
91/// An instance of a Move type.
92#[ComplexObject]
93impl MoveValue {
94    /// Structured contents of a Move value.
95    async fn data(&self, ctx: &Context<'_>) -> Result<MoveData> {
96        let resolver: &PackageResolver = ctx
97            .data()
98            .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
99            .extend()?;
100
101        let Some(layout) = self.type_.layout_impl(resolver).await.extend()? else {
102            return Err(Error::Internal(
103                "Move value must have valid layout".to_string(),
104            ))
105            .extend();
106        };
107
108        // Factor out into its own non-GraphQL, non-async function for better testability
109        self.data_impl(layout).extend()
110    }
111
112    /// Representation of a Move value in JSON, where:
113    ///
114    /// - Addresses, IDs, and UIDs are represented in canonical form, as JSON strings.
115    /// - Bools are represented by JSON boolean literals.
116    /// - u8, u16, and u32 are represented as JSON numbers.
117    /// - u64, u128, and u256 are represented as JSON strings.
118    /// - Vectors are represented by JSON arrays.
119    /// - Structs are represented by JSON objects.
120    /// - Empty optional values are represented by `null`.
121    ///
122    /// This form is offered as a less verbose convenience in cases where the layout of the type is
123    /// known by the client.
124    async fn json(&self, ctx: &Context<'_>) -> Result<Json> {
125        let resolver: &PackageResolver = ctx
126            .data()
127            .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
128            .extend()?;
129
130        let Some(layout) = self.type_.layout_impl(resolver).await.extend()? else {
131            return Err(Error::Internal(
132                "Move value must have valid layout".to_string(),
133            ))
134            .extend();
135        };
136
137        // Factor out into its own non-GraphQL, non-async function for better testability
138        self.json_impl(layout).extend()
139    }
140}
141
142impl MoveValue {
143    pub fn new(tag: TypeTag, bcs: Base64) -> Self {
144        let type_ = MoveType::from(tag);
145        Self { type_, bcs }
146    }
147
148    fn value_impl(&self, layout: A::MoveTypeLayout) -> Result<A::MoveValue, Error> {
149        // TODO (annotated-visitor): deserializing directly using a custom visitor.
150        BoundedVisitor::deserialize_value(&self.bcs.0[..], &layout).map_err(|_| {
151            let type_tag: TypeTag = (&layout).into();
152            Error::Internal(format!(
153                "Failed to deserialize Move value for type: {}",
154                type_tag
155            ))
156        })
157    }
158
159    fn data_impl(&self, layout: A::MoveTypeLayout) -> Result<MoveData, Error> {
160        MoveData::try_from(self.value_impl(layout)?)
161    }
162
163    fn json_impl(&self, layout: A::MoveTypeLayout) -> Result<Json, Error> {
164        Ok(try_to_json_value(self.value_impl(layout)?)?.into())
165    }
166}
167
168impl TryFrom<A::MoveValue> for MoveData {
169    type Error = Error;
170
171    fn try_from(value: A::MoveValue) -> Result<Self, Error> {
172        use A::MoveValue as V;
173
174        Ok(match value {
175            V::U8(n) => Self::Number(BigInt::from(n)),
176            V::U16(n) => Self::Number(BigInt::from(n)),
177            V::U32(n) => Self::Number(BigInt::from(n)),
178            V::U64(n) => Self::Number(BigInt::from(n)),
179            V::U128(n) => Self::Number(BigInt::from(n)),
180            V::U256(n) => Self::Number(BigInt::from(n)),
181
182            V::Bool(b) => Self::Bool(b),
183            V::Address(a) => Self::Address(a.into()),
184
185            V::Vector(v) => Self::Vector(
186                v.into_iter()
187                    .map(MoveData::try_from)
188                    .collect::<Result<Vec<_>, _>>()?,
189            ),
190
191            V::Struct(s) => {
192                let A::MoveStruct { type_, fields } = s;
193                if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
194                    // 0x1::option::Option
195                    Self::Option(match extract_option(&type_, fields)? {
196                        Some(value) => Some(Box::new(MoveData::try_from(value)?)),
197                        None => None,
198                    })
199                } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
200                    || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
201                {
202                    // 0x1::ascii::String, 0x1::string::String
203                    Self::String(extract_string(&type_, fields)?)
204                } else if is_type(&type_, &SUI, MOD_OBJECT, TYP_UID) {
205                    // 0x2::object::UID
206                    Self::Uid(extract_uid(&type_, fields)?.into())
207                } else if is_type(&type_, &SUI, MOD_OBJECT, TYP_ID) {
208                    // 0x2::object::ID
209                    Self::Id(extract_id(&type_, fields)?.into())
210                } else {
211                    // Arbitrary structs
212                    let fields: Result<Vec<_>, _> =
213                        fields.into_iter().map(MoveField::try_from).collect();
214                    Self::Struct(fields?)
215                }
216            }
217
218            V::Variant(A::MoveVariant {
219                type_: _,
220                variant_name,
221                tag: _,
222                fields,
223            }) => {
224                let fields = fields
225                    .into_iter()
226                    .map(MoveField::try_from)
227                    .collect::<Result<_, _>>()?;
228                Self::Variant(MoveVariant {
229                    name: variant_name.to_string(),
230                    fields,
231                })
232            }
233
234            // Sui does not support `signer` as a type.
235            V::Signer(_) => return Err(unexpected_signer_error()),
236        })
237    }
238}
239
240impl TryFrom<(Identifier, A::MoveValue)> for MoveField {
241    type Error = Error;
242
243    fn try_from((ident, value): (Identifier, A::MoveValue)) -> Result<Self, Error> {
244        Ok(MoveField {
245            name: ident.to_string(),
246            value: MoveData::try_from(value)?,
247        })
248    }
249}
250
251fn try_to_json_value(value: A::MoveValue) -> Result<Value, Error> {
252    use A::MoveValue as V;
253    Ok(match value {
254        V::U8(n) => Value::Number(n.into()),
255        V::U16(n) => Value::Number(n.into()),
256        V::U32(n) => Value::Number(n.into()),
257        V::U64(n) => Value::String(n.to_string()),
258        V::U128(n) => Value::String(n.to_string()),
259        V::U256(n) => Value::String(n.to_string()),
260
261        V::Bool(b) => Value::Boolean(b),
262        V::Address(a) => Value::String(a.to_canonical_string(/* with_prefix */ true)),
263
264        V::Vector(xs) => Value::List(
265            xs.into_iter()
266                .map(try_to_json_value)
267                .collect::<Result<_, _>>()?,
268        ),
269
270        V::Struct(s) => {
271            let A::MoveStruct { type_, fields } = s;
272            if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
273                // 0x1::option::Option
274                match extract_option(&type_, fields)? {
275                    Some(value) => try_to_json_value(value)?,
276                    None => Value::Null,
277                }
278            } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
279                || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
280            {
281                // 0x1::ascii::String, 0x1::string::String
282                Value::String(extract_string(&type_, fields)?)
283            } else if is_type(&type_, &SUI, MOD_OBJECT, TYP_UID) {
284                // 0x2::object::UID
285                Value::String(
286                    extract_uid(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
287                )
288            } else if is_type(&type_, &SUI, MOD_OBJECT, TYP_ID) {
289                // 0x2::object::ID
290                Value::String(
291                    extract_id(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
292                )
293            } else {
294                // Arbitrary structs
295                Value::Object(
296                    fields
297                        .into_iter()
298                        .map(|(name, value)| {
299                            Ok((Name::new(name.to_string()), try_to_json_value(value)?))
300                        })
301                        .collect::<Result<_, Error>>()?,
302                )
303            }
304        }
305
306        V::Variant(A::MoveVariant {
307            type_: _,
308            variant_name,
309            tag: _,
310            fields,
311        }) => {
312            let fields = fields
313                .into_iter()
314                .map(|(name, value)| Ok((Name::new(name.to_string()), try_to_json_value(value)?)))
315                .collect::<Result<_, Error>>()?;
316            Value::Object(
317                vec![(Name::new(variant_name.to_string()), Value::Object(fields))]
318                    .into_iter()
319                    .collect(),
320            )
321        }
322        // Sui does not support `signer` as a type.
323        V::Signer(_) => return Err(unexpected_signer_error()),
324    })
325}
326
327fn is_type(tag: &StructTag, address: &AccountAddress, module: &IdentStr, name: &IdentStr) -> bool {
328    &tag.address == address
329        && tag.module.as_ident_str() == module
330        && tag.name.as_ident_str() == name
331}
332
333macro_rules! extract_field {
334    ($type:expr, $fields:expr, $name:ident) => {{
335        let _name = ident_str!(stringify!($name));
336        let _type = $type;
337        if let Some(value) = ($fields)
338            .into_iter()
339            .find_map(|(name, value)| (&*name == _name).then_some(value))
340        {
341            value
342        } else {
343            return Err(Error::Internal(format!(
344                "Couldn't find expected field '{_name}' of {_type}."
345            )));
346        }
347    }};
348}
349
350/// Extracts a vector of bytes from `value`, assuming it's a `MoveValue::Vector` where all the
351/// values are `MoveValue::U8`s.
352fn extract_bytes(value: A::MoveValue) -> Result<Vec<u8>, Error> {
353    use A::MoveValue as V;
354    let V::Vector(elements) = value else {
355        return Err(Error::Internal("Expected a vector.".to_string()));
356    };
357
358    let mut bytes = Vec::with_capacity(elements.len());
359    for element in elements {
360        let V::U8(byte) = element else {
361            return Err(Error::Internal("Expected a byte.".to_string()));
362        };
363        bytes.push(byte)
364    }
365
366    Ok(bytes)
367}
368
369/// Extracts a Rust String from the contents of a Move Struct assuming that struct matches the
370/// contents of Move String:
371///
372/// ```notrust
373///     { bytes: vector<u8> }
374/// ```
375///
376/// Which is conformed to by both `std::ascii::String` and `std::string::String`.
377fn 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        // Provide a sample of the string in question.
387        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
397/// Extracts an address from the contents of a Move Struct, assuming the struct matches the
398/// following shape:
399///
400/// ```notrust
401///     { bytes: address }
402/// ```
403///
404/// Which matches `0x2::object::ID`.
405fn 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
419/// Extracts an address from the contents of a Move Struct, assuming the struct matches the
420/// following shape:
421///
422/// ```notrust
423///     { id: 0x2::object::ID { bytes: address } }
424/// ```
425///
426/// Which matches `0x2::object::UID`.
427fn 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_, &SUI, 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
448/// Extracts a value from the contents of a Move Struct, assuming the struct matches the following
449/// shape:
450///
451/// ```notrust
452///     { vec: vector<T> }
453/// ```
454///
455/// Where `vec` contains at most one element.  This matches the shape of `0x1::option::Option<T>`.
456fn 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) -> SuiAddress {
506        SuiAddress::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        // The format for type from its `Display` impl does not technically match the format that
513        // the RPC expects from the data layer (where a type's package should be canonicalized), but
514        // it will suffice.
515        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::from(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::from(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!["Ok(Address(SuiAddress([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])))"];
709        expect.assert_eq(&format!("{v:?}"));
710    }
711
712    #[test]
713    fn address_json() {
714        let v = json(L::Address, address("0x42")).unwrap();
715        let expect =
716            expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
717        expect.assert_eq(&format!("{v}"));
718    }
719
720    #[test]
721    fn uid_data() {
722        let l = struct_layout!("0x2::object::UID" {
723            "id": struct_layout!("0x2::object::ID" {
724                "bytes": L::Address,
725            })
726        });
727
728        let v = data(l, address("0x42"));
729        let expect = expect!["Ok(Uid(SuiAddress([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])))"];
730        expect.assert_eq(&format!("{v:?}"));
731    }
732
733    #[test]
734    fn uid_json() {
735        let l = struct_layout!("0x2::object::UID" {
736            "id": struct_layout!("0x2::object::ID" {
737                "bytes": L::Address,
738            })
739        });
740
741        let v = json(l, address("0x42")).unwrap();
742        let expect =
743            expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
744        expect.assert_eq(&format!("{v}"));
745    }
746
747    #[test]
748    fn compound_data() {
749        let l = struct_layout!("0x42::foo::Bar" {
750            "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
751            "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
752                "quy": L::U16,
753                "quz": struct_layout!("0x1::option::Option" {
754                    "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
755                        "bytes": vector_layout!(L::U8),
756                    }))
757                }),
758                "frob": L::Address,
759            })),
760        });
761
762        let v = data(
763            l,
764            (
765                vec![] as Vec<Vec<u8>>,
766                vec![
767                    (44u16, vec!["Hello, world!"], address("0x45")),
768                    (46u16, vec![], address("0x47")),
769                ],
770            ),
771        );
772
773        let expect = expect![[r#"
774            Ok(
775                Struct(
776                    [
777                        MoveField {
778                            name: "baz",
779                            value: Option(
780                                None,
781                            ),
782                        },
783                        MoveField {
784                            name: "qux",
785                            value: Vector(
786                                [
787                                    Struct(
788                                        [
789                                            MoveField {
790                                                name: "quy",
791                                                value: Number(
792                                                    BigInt(
793                                                        "44",
794                                                    ),
795                                                ),
796                                            },
797                                            MoveField {
798                                                name: "quz",
799                                                value: Option(
800                                                    Some(
801                                                        String(
802                                                            "Hello, world!",
803                                                        ),
804                                                    ),
805                                                ),
806                                            },
807                                            MoveField {
808                                                name: "frob",
809                                                value: Address(
810                                                    SuiAddress(
811                                                        [
812                                                            0,
813                                                            0,
814                                                            0,
815                                                            0,
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                                                            69,
844                                                        ],
845                                                    ),
846                                                ),
847                                            },
848                                        ],
849                                    ),
850                                    Struct(
851                                        [
852                                            MoveField {
853                                                name: "quy",
854                                                value: Number(
855                                                    BigInt(
856                                                        "46",
857                                                    ),
858                                                ),
859                                            },
860                                            MoveField {
861                                                name: "quz",
862                                                value: Option(
863                                                    None,
864                                                ),
865                                            },
866                                            MoveField {
867                                                name: "frob",
868                                                value: Address(
869                                                    SuiAddress(
870                                                        [
871                                                            0,
872                                                            0,
873                                                            0,
874                                                            0,
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                                                            71,
903                                                        ],
904                                                    ),
905                                                ),
906                                            },
907                                        ],
908                                    ),
909                                ],
910                            ),
911                        },
912                    ],
913                ),
914            )"#]];
915        expect.assert_eq(&format!("{v:#?}"));
916    }
917
918    #[test]
919    fn compound_json() {
920        let l = struct_layout!("0x42::foo::Bar" {
921            "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
922            "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
923                "quy": L::U16,
924                "quz": struct_layout!("0x1::option::Option" {
925                    "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
926                        "bytes": vector_layout!(L::U8),
927                    }))
928                }),
929                "frob": L::Address,
930            })),
931        });
932
933        let v = json(
934            l,
935            (
936                vec![] as Vec<Vec<u8>>,
937                vec![
938                    (44u16, vec!["Hello, world!"], address("0x45")),
939                    (46u16, vec![], address("0x47")),
940                ],
941            ),
942        )
943        .unwrap();
944
945        let expect = expect![[
946            r#"{baz: null,qux: [{quy: 44,quz: "Hello, world!",frob: "0x0000000000000000000000000000000000000000000000000000000000000045"},{quy: 46,quz: null,frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"#
947        ]];
948        expect.assert_eq(&format!("{v}"));
949    }
950
951    #[test]
952    fn signer_value() {
953        let v = data(L::Signer, address("0x42"));
954        let expect = expect![[r#"
955            Err(
956                Internal(
957                    "Unexpected value of type: signer.",
958                ),
959            )"#]];
960        expect.assert_eq(&format!("{v:#?}"));
961    }
962
963    #[test]
964    fn signer_json() {
965        let err = json(L::Signer, address("0x42")).unwrap_err();
966        let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
967        expect.assert_eq(&format!("{err:?}"));
968    }
969
970    #[test]
971    fn signer_nested_data() {
972        let v = data(
973            vector_layout!(L::Signer),
974            vec![address("0x42"), address("0x43")],
975        );
976        let expect = expect![[r#"
977            Err(
978                Internal(
979                    "Unexpected value of type: signer.",
980                ),
981            )"#]];
982        expect.assert_eq(&format!("{v:#?}"));
983    }
984
985    #[test]
986    fn signer_nested_json() {
987        let err = json(
988            vector_layout!(L::Signer),
989            vec![address("0x42"), address("0x43")],
990        )
991        .unwrap_err();
992
993        let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
994        expect.assert_eq(&format!("{err:?}"));
995    }
996}