iota_graphql_rpc/types/
move_value.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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    /// The value's Move type.
44    #[graphql(name = "type")]
45    type_: MoveType,
46    /// The BCS representation of this value, Base64 encoded.
47    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/// An instance of a Move type.
100#[ComplexObject]
101impl MoveValue {
102    /// Structured contents of a Move value.
103    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        // Factor out into its own non-GraphQL, non-async function for better
117        // testability
118        self.data_impl(layout).extend()
119    }
120
121    /// Representation of a Move value in JSON, where:
122    ///
123    /// - Addresses, IDs, and UIDs are represented in canonical form, as JSON
124    ///   strings.
125    /// - Bools are represented by JSON boolean literals.
126    /// - u8, u16, and u32 are represented as JSON numbers.
127    /// - u64, u128, and u256 are represented as JSON strings.
128    /// - Vectors are represented by JSON arrays.
129    /// - Structs are represented by JSON objects.
130    /// - Empty optional values are represented by `null`.
131    ///
132    /// This form is offered as a less verbose convenience in cases where the
133    /// layout of the type is known by the client.
134    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        // Factor out into its own non-GraphQL, non-async function for better
148        // testability
149        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        // TODO (annotated-visitor): deserializing directly using a custom visitor.
161        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                    // 0x1::option::Option
205                    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                    // 0x1::ascii::String, 0x1::string::String
213                    Self::String(extract_string(&type_, fields)?)
214                } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
215                    // 0x2::object::UID
216                    Self::Uid(extract_uid(&type_, fields)?.into())
217                } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
218                    // 0x2::object::ID
219                    Self::Id(extract_id(&type_, fields)?.into())
220                } else {
221                    // Arbitrary structs
222                    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            // IOTA does not support `signer` as a type.
245            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(/* with_prefix */ 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                // 0x1::option::Option
284                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                // 0x1::ascii::String, 0x1::string::String
292                Value::String(extract_string(&type_, fields)?)
293            } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
294                // 0x2::object::UID
295                Value::String(
296                    extract_uid(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
297                )
298            } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
299                // 0x2::object::ID
300                Value::String(
301                    extract_id(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
302                )
303            } else {
304                // Arbitrary structs
305                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        // IOTA does not support `signer` as a type.
333        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
360/// Extracts a vector of bytes from `value`, assuming it's a `MoveValue::Vector`
361/// where all the values are `MoveValue::U8`s.
362fn 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
379/// Extracts a Rust String from the contents of a Move Struct assuming that
380/// struct matches the contents of Move String:
381///
382/// ```notrust
383///     { bytes: vector<u8> }
384/// ```
385///
386/// Which is conformed to by both `std::ascii::String` and
387/// `std::string::String`.
388fn 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        // Provide a sample of the string in question.
398        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
408/// Extracts an address from the contents of a Move Struct, assuming the struct
409/// matches the following shape:
410///
411/// ```notrust
412///     { bytes: address }
413/// ```
414///
415/// Which matches `0x2::object::ID`.
416fn 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
430/// Extracts an address from the contents of a Move Struct, assuming the struct
431/// matches the following shape:
432///
433/// ```notrust
434///     { id: 0x2::object::ID { bytes: address } }
435/// ```
436///
437/// Which matches `0x2::object::UID`.
438fn 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
459/// Extracts a value from the contents of a Move Struct, assuming the struct
460/// matches the following shape:
461///
462/// ```notrust
463///     { vec: vector<T> }
464/// ```
465///
466/// Where `vec` contains at most one element.  This matches the shape of
467/// `0x1::option::Option<T>`.
468fn 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        // The format for type from its `Display` impl does not technically match the
525        // format that the RPC expects from the data layer (where a type's
526        // package should be canonicalized), but it will suffice.
527        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}