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