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        // Factor out into its own non-GraphQL, non-async function for better
110        // testability
111        self.data_impl(self.type_.layout_impl(resolver).await.extend()?)
112            .extend()
113    }
114
115    /// Representation of a Move value in JSON, where:
116    ///
117    /// - Addresses, IDs, and UIDs are represented in canonical form, as JSON
118    ///   strings.
119    /// - Bools are represented by JSON boolean literals.
120    /// - u8, u16, and u32 are represented as JSON numbers.
121    /// - u64, u128, and u256 are represented as JSON strings.
122    /// - Vectors are represented by JSON arrays.
123    /// - Structs are represented by JSON objects.
124    /// - Empty optional values are represented by `null`.
125    ///
126    /// This form is offered as a less verbose convenience in cases where the
127    /// layout of the type is known by the client.
128    async fn json(&self, ctx: &Context<'_>) -> Result<Json> {
129        let resolver: &PackageResolver = ctx
130            .data()
131            .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
132            .extend()?;
133
134        // Factor out into its own non-GraphQL, non-async function for better
135        // testability
136        self.json_impl(self.type_.layout_impl(resolver).await.extend()?)
137            .extend()
138    }
139}
140
141impl MoveValue {
142    pub fn new(tag: TypeTag, bcs: Base64) -> Self {
143        let type_ = MoveType::new(tag);
144        Self { type_, bcs }
145    }
146
147    fn value_impl(&self, layout: A::MoveTypeLayout) -> Result<A::MoveValue, Error> {
148        // TODO (annotated-visitor): deserializing directly using a custom visitor.
149        BoundedVisitor::deserialize_value(&self.bcs.0[..], &layout).map_err(|_| {
150            let type_tag: TypeTag = (&layout).into();
151            Error::Internal(format!(
152                "Failed to deserialize Move value for type: {type_tag}"
153            ))
154        })
155    }
156
157    fn data_impl(&self, layout: A::MoveTypeLayout) -> Result<MoveData, Error> {
158        MoveData::try_from(self.value_impl(layout)?)
159    }
160
161    fn json_impl(&self, layout: A::MoveTypeLayout) -> Result<Json, Error> {
162        Ok(try_to_json_value(self.value_impl(layout)?)?.into())
163    }
164}
165
166impl TryFrom<A::MoveValue> for MoveData {
167    type Error = Error;
168
169    fn try_from(value: A::MoveValue) -> Result<Self, Error> {
170        use A::MoveValue as V;
171
172        Ok(match value {
173            V::U8(n) => Self::Number(BigInt::from(n)),
174            V::U16(n) => Self::Number(BigInt::from(n)),
175            V::U32(n) => Self::Number(BigInt::from(n)),
176            V::U64(n) => Self::Number(BigInt::from(n)),
177            V::U128(n) => Self::Number(BigInt::from(n)),
178            V::U256(n) => Self::Number(BigInt::from(n)),
179
180            V::Bool(b) => Self::Bool(b),
181            V::Address(a) => Self::Address(a.into()),
182
183            V::Vector(v) => Self::Vector(
184                v.into_iter()
185                    .map(MoveData::try_from)
186                    .collect::<Result<Vec<_>, _>>()?,
187            ),
188
189            V::Struct(s) => {
190                let A::MoveStruct { type_, fields } = s;
191                if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
192                    // 0x1::option::Option
193                    Self::Option(match extract_option(&type_, fields)? {
194                        Some(value) => Some(Box::new(MoveData::try_from(value)?)),
195                        None => None,
196                    })
197                } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
198                    || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
199                {
200                    // 0x1::ascii::String, 0x1::string::String
201                    Self::String(extract_string(&type_, fields)?)
202                } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
203                    // 0x2::object::UID
204                    Self::Uid(extract_uid(&type_, fields)?.into())
205                } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
206                    // 0x2::object::ID
207                    Self::Id(extract_id(&type_, fields)?.into())
208                } else {
209                    // Arbitrary structs
210                    let fields: Result<Vec<_>, _> =
211                        fields.into_iter().map(MoveField::try_from).collect();
212                    Self::Struct(fields?)
213                }
214            }
215
216            V::Variant(A::MoveVariant {
217                type_: _,
218                variant_name,
219                tag: _,
220                fields,
221            }) => {
222                let fields = fields
223                    .into_iter()
224                    .map(MoveField::try_from)
225                    .collect::<Result<_, _>>()?;
226                Self::Variant(MoveVariant {
227                    name: variant_name.to_string(),
228                    fields,
229                })
230            }
231
232            // IOTA does not support `signer` as a type.
233            V::Signer(_) => return Err(unexpected_signer_error()),
234        })
235    }
236}
237
238impl TryFrom<(Identifier, A::MoveValue)> for MoveField {
239    type Error = Error;
240
241    fn try_from((ident, value): (Identifier, A::MoveValue)) -> Result<Self, Error> {
242        Ok(MoveField {
243            name: ident.to_string(),
244            value: MoveData::try_from(value)?,
245        })
246    }
247}
248
249fn try_to_json_value(value: A::MoveValue) -> Result<Value, Error> {
250    use A::MoveValue as V;
251    Ok(match value {
252        V::U8(n) => Value::Number(n.into()),
253        V::U16(n) => Value::Number(n.into()),
254        V::U32(n) => Value::Number(n.into()),
255        V::U64(n) => Value::String(n.to_string()),
256        V::U128(n) => Value::String(n.to_string()),
257        V::U256(n) => Value::String(n.to_string()),
258
259        V::Bool(b) => Value::Boolean(b),
260        V::Address(a) => Value::String(a.to_canonical_string(/* with_prefix */ true)),
261
262        V::Vector(xs) => Value::List(
263            xs.into_iter()
264                .map(try_to_json_value)
265                .collect::<Result<_, _>>()?,
266        ),
267
268        V::Struct(s) => {
269            let A::MoveStruct { type_, fields } = s;
270            if is_type(&type_, &STD, MOD_OPTION, TYP_OPTION) {
271                // 0x1::option::Option
272                match extract_option(&type_, fields)? {
273                    Some(value) => try_to_json_value(value)?,
274                    None => Value::Null,
275                }
276            } else if is_type(&type_, &STD, MOD_ASCII, TYP_STRING)
277                || is_type(&type_, &STD, MOD_STRING, TYP_STRING)
278            {
279                // 0x1::ascii::String, 0x1::string::String
280                Value::String(extract_string(&type_, fields)?)
281            } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_UID) {
282                // 0x2::object::UID
283                Value::String(
284                    extract_uid(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
285                )
286            } else if is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
287                // 0x2::object::ID
288                Value::String(
289                    extract_id(&type_, fields)?.to_canonical_string(/* with_prefix */ true),
290                )
291            } else {
292                // Arbitrary structs
293                Value::Object(
294                    fields
295                        .into_iter()
296                        .map(|(name, value)| {
297                            Ok((Name::new(name.to_string()), try_to_json_value(value)?))
298                        })
299                        .collect::<Result<_, Error>>()?,
300                )
301            }
302        }
303
304        V::Variant(A::MoveVariant {
305            type_: _,
306            variant_name,
307            tag: _,
308            fields,
309        }) => {
310            let fields = fields
311                .into_iter()
312                .map(|(name, value)| Ok((Name::new(name.to_string()), try_to_json_value(value)?)))
313                .collect::<Result<_, Error>>()?;
314            Value::Object(
315                vec![(Name::new(variant_name.to_string()), Value::Object(fields))]
316                    .into_iter()
317                    .collect(),
318            )
319        }
320        // IOTA does not support `signer` as a type.
321        V::Signer(_) => return Err(unexpected_signer_error()),
322    })
323}
324
325fn is_type(tag: &StructTag, address: &AccountAddress, module: &IdentStr, name: &IdentStr) -> bool {
326    &tag.address == address
327        && tag.module.as_ident_str() == module
328        && tag.name.as_ident_str() == name
329}
330
331macro_rules! extract_field {
332    ($type:expr, $fields:expr, $name:ident) => {{
333        let _name = ident_str!(stringify!($name));
334        let _type = $type;
335        if let Some(value) = ($fields)
336            .into_iter()
337            .find_map(|(name, value)| (&*name == _name).then_some(value))
338        {
339            value
340        } else {
341            return Err(Error::Internal(format!(
342                "Couldn't find expected field '{_name}' of {_type}."
343            )));
344        }
345    }};
346}
347
348/// Extracts a vector of bytes from `value`, assuming it's a `MoveValue::Vector`
349/// where all the values are `MoveValue::U8`s.
350fn extract_bytes(value: A::MoveValue) -> Result<Vec<u8>, Error> {
351    use A::MoveValue as V;
352    let V::Vector(elements) = value else {
353        return Err(Error::Internal("Expected a vector.".to_string()));
354    };
355
356    let mut bytes = Vec::with_capacity(elements.len());
357    for element in elements {
358        let V::U8(byte) = element else {
359            return Err(Error::Internal("Expected a byte.".to_string()));
360        };
361        bytes.push(byte)
362    }
363
364    Ok(bytes)
365}
366
367/// Extracts a Rust String from the contents of a Move Struct assuming that
368/// struct matches the contents of Move String:
369///
370/// ```notrust
371///     { bytes: vector<u8> }
372/// ```
373///
374/// Which is conformed to by both `std::ascii::String` and
375/// `std::string::String`.
376fn extract_string(
377    type_: &StructTag,
378    fields: Vec<(Identifier, A::MoveValue)>,
379) -> Result<String, Error> {
380    let bytes = extract_bytes(extract_field!(type_, fields, bytes))?;
381    String::from_utf8(bytes).map_err(|e| {
382        const PREFIX: usize = 30;
383        let bytes = e.as_bytes();
384
385        // Provide a sample of the string in question.
386        let sample = if bytes.len() < PREFIX {
387            String::from_utf8_lossy(bytes)
388        } else {
389            String::from_utf8_lossy(&bytes[..PREFIX - 3]) + "..."
390        };
391
392        Error::Internal(format!("{e} in {sample:?}"))
393    })
394}
395
396/// Extracts an address from the contents of a Move Struct, assuming the struct
397/// matches the following shape:
398///
399/// ```notrust
400///     { bytes: address }
401/// ```
402///
403/// Which matches `0x2::object::ID`.
404fn extract_id(
405    type_: &StructTag,
406    fields: Vec<(Identifier, A::MoveValue)>,
407) -> Result<AccountAddress, Error> {
408    use A::MoveValue as V;
409    let V::Address(addr) = extract_field!(type_, fields, bytes) else {
410        return Err(Error::Internal(
411            "Expected ID.bytes to have type address.".to_string(),
412        ));
413    };
414
415    Ok(addr)
416}
417
418/// Extracts an address from the contents of a Move Struct, assuming the struct
419/// matches the following shape:
420///
421/// ```notrust
422///     { id: 0x2::object::ID { bytes: address } }
423/// ```
424///
425/// Which matches `0x2::object::UID`.
426fn extract_uid(
427    type_: &StructTag,
428    fields: Vec<(Identifier, A::MoveValue)>,
429) -> Result<AccountAddress, Error> {
430    use A::MoveValue as V;
431    let V::Struct(s) = extract_field!(type_, fields, id) else {
432        return Err(Error::Internal(
433            "Expected UID.id to be a struct".to_string(),
434        ));
435    };
436
437    let A::MoveStruct { type_, fields } = s;
438    if !is_type(&type_, &IOTA, MOD_OBJECT, TYP_ID) {
439        return Err(Error::Internal(
440            "Expected UID.id to have type ID.".to_string(),
441        ));
442    }
443
444    extract_id(&type_, fields)
445}
446
447/// Extracts a value from the contents of a Move Struct, assuming the struct
448/// matches the following shape:
449///
450/// ```notrust
451///     { vec: vector<T> }
452/// ```
453///
454/// Where `vec` contains at most one element.  This matches the shape of
455/// `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) -> IotaAddress {
506        IotaAddress::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
513        // format that the RPC expects from the data layer (where a type's
514        // package should be canonicalized), but 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::new(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::new(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![
709            "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])))"
710        ];
711        expect.assert_eq(&format!("{v:?}"));
712    }
713
714    #[test]
715    fn address_json() {
716        let v = json(L::Address, address("0x42")).unwrap();
717        let expect =
718            expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
719        expect.assert_eq(&format!("{v}"));
720    }
721
722    #[test]
723    fn uid_data() {
724        let l = struct_layout!("0x2::object::UID" {
725            "id": struct_layout!("0x2::object::ID" {
726                "bytes": L::Address,
727            })
728        });
729
730        let v = data(l, address("0x42"));
731        let expect = expect![
732            "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])))"
733        ];
734        expect.assert_eq(&format!("{v:?}"));
735    }
736
737    #[test]
738    fn uid_json() {
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 = json(l, address("0x42")).unwrap();
746        let expect =
747            expect![[r#""0x0000000000000000000000000000000000000000000000000000000000000042""#]];
748        expect.assert_eq(&format!("{v}"));
749    }
750
751    #[test]
752    fn compound_data() {
753        let l = struct_layout!("0x42::foo::Bar" {
754            "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
755            "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
756                "quy": L::U16,
757                "quz": struct_layout!("0x1::option::Option" {
758                    "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
759                        "bytes": vector_layout!(L::U8),
760                    }))
761                }),
762                "frob": L::Address,
763            })),
764        });
765
766        let v = data(
767            l,
768            (
769                vec![] as Vec<Vec<u8>>,
770                vec![
771                    (44u16, vec!["Hello, world!"], address("0x45")),
772                    (46u16, vec![], address("0x47")),
773                ],
774            ),
775        );
776
777        let expect = expect![[r#"
778            Ok(
779                Struct(
780                    [
781                        MoveField {
782                            name: "baz",
783                            value: Option(
784                                None,
785                            ),
786                        },
787                        MoveField {
788                            name: "qux",
789                            value: Vector(
790                                [
791                                    Struct(
792                                        [
793                                            MoveField {
794                                                name: "quy",
795                                                value: Number(
796                                                    BigInt(
797                                                        "44",
798                                                    ),
799                                                ),
800                                            },
801                                            MoveField {
802                                                name: "quz",
803                                                value: Option(
804                                                    Some(
805                                                        String(
806                                                            "Hello, world!",
807                                                        ),
808                                                    ),
809                                                ),
810                                            },
811                                            MoveField {
812                                                name: "frob",
813                                                value: Address(
814                                                    IotaAddress(
815                                                        [
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                                                            0,
844                                                            0,
845                                                            0,
846                                                            0,
847                                                            69,
848                                                        ],
849                                                    ),
850                                                ),
851                                            },
852                                        ],
853                                    ),
854                                    Struct(
855                                        [
856                                            MoveField {
857                                                name: "quy",
858                                                value: Number(
859                                                    BigInt(
860                                                        "46",
861                                                    ),
862                                                ),
863                                            },
864                                            MoveField {
865                                                name: "quz",
866                                                value: Option(
867                                                    None,
868                                                ),
869                                            },
870                                            MoveField {
871                                                name: "frob",
872                                                value: Address(
873                                                    IotaAddress(
874                                                        [
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                                                            0,
903                                                            0,
904                                                            0,
905                                                            0,
906                                                            71,
907                                                        ],
908                                                    ),
909                                                ),
910                                            },
911                                        ],
912                                    ),
913                                ],
914                            ),
915                        },
916                    ],
917                ),
918            )"#]];
919        expect.assert_eq(&format!("{v:#?}"));
920    }
921
922    #[test]
923    fn compound_json() {
924        let l = struct_layout!("0x42::foo::Bar" {
925            "baz": struct_layout!("0x1::option::Option" { "vec": vector_layout!(L::U8) }),
926            "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
927                "quy": L::U16,
928                "quz": struct_layout!("0x1::option::Option" {
929                    "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
930                        "bytes": vector_layout!(L::U8),
931                    }))
932                }),
933                "frob": L::Address,
934            })),
935        });
936
937        let v = json(
938            l,
939            (
940                vec![] as Vec<Vec<u8>>,
941                vec![
942                    (44u16, vec!["Hello, world!"], address("0x45")),
943                    (46u16, vec![], address("0x47")),
944                ],
945            ),
946        )
947        .unwrap();
948
949        let expect = expect![[
950            r#"{baz: null, qux: [{quy: 44, quz: "Hello, world!", frob: "0x0000000000000000000000000000000000000000000000000000000000000045"}, {quy: 46, quz: null, frob: "0x0000000000000000000000000000000000000000000000000000000000000047"}]}"#
951        ]];
952        expect.assert_eq(&format!("{v}"));
953    }
954
955    #[test]
956    fn signer_value() {
957        let v = data(L::Signer, address("0x42"));
958        let expect = expect![[r#"
959            Err(
960                Internal(
961                    "Unexpected value of type: signer.",
962                ),
963            )"#]];
964        expect.assert_eq(&format!("{v:#?}"));
965    }
966
967    #[test]
968    fn signer_json() {
969        let err = json(L::Signer, address("0x42")).unwrap_err();
970        let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
971        expect.assert_eq(&format!("{err:?}"));
972    }
973
974    #[test]
975    fn signer_nested_data() {
976        let v = data(
977            vector_layout!(L::Signer),
978            vec![address("0x42"), address("0x43")],
979        );
980        let expect = expect![[r#"
981            Err(
982                Internal(
983                    "Unexpected value of type: signer.",
984                ),
985            )"#]];
986        expect.assert_eq(&format!("{v:#?}"));
987    }
988
989    #[test]
990    fn signer_nested_json() {
991        let err = json(
992            vector_layout!(L::Signer),
993            vec![address("0x42"), address("0x43")],
994        )
995        .unwrap_err();
996
997        let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
998        expect.assert_eq(&format!("{err:?}"));
999    }
1000}