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