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