Skip to main content

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