contract_transcode/
transcoder.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use super::{
18    decode::Decoder,
19    encode::Encoder,
20    env_types::{
21        self,
22        CustomTypeDecoder,
23        CustomTypeEncoder,
24        EnvTypesTranscoder,
25        PathKey,
26        TypesByPath,
27    },
28    scon::Value,
29    AccountId32,
30};
31
32use anyhow::Result;
33use scale::Output;
34use scale_info::{
35    PortableRegistry,
36    TypeInfo,
37};
38use std::{
39    collections::HashMap,
40    fmt::Debug,
41};
42
43/// Encode strings to SCALE encoded output.
44/// Decode SCALE encoded input into `Value` objects.
45pub struct Transcoder {
46    env_types: EnvTypesTranscoder,
47}
48
49impl Transcoder {
50    pub fn new(env_types: EnvTypesTranscoder) -> Self {
51        Self { env_types }
52    }
53
54    pub fn encode<O>(
55        &self,
56        registry: &PortableRegistry,
57        type_id: u32,
58        value: &Value,
59        output: &mut O,
60    ) -> Result<()>
61    where
62        O: Output + Debug,
63    {
64        let encoder = Encoder::new(registry, &self.env_types);
65        encoder.encode(type_id, value, output)
66    }
67
68    pub fn decode(
69        &self,
70        registry: &PortableRegistry,
71        type_id: u32,
72        input: &mut &[u8],
73    ) -> Result<Value> {
74        let decoder = Decoder::new(registry, &self.env_types);
75        decoder.decode(type_id, input)
76    }
77}
78
79/// Construct a [`Transcoder`], allows registering custom transcoders for certain types.
80pub struct TranscoderBuilder {
81    types_by_path: TypesByPath,
82    encoders: HashMap<u32, Box<dyn CustomTypeEncoder>>,
83    decoders: HashMap<u32, Box<dyn CustomTypeDecoder>>,
84}
85
86impl TranscoderBuilder {
87    pub fn new(registry: &PortableRegistry) -> Self {
88        let types_by_path = registry
89            .types
90            .iter()
91            .map(|ty| (PathKey::from(&ty.ty.path), ty.id))
92            .collect::<TypesByPath>();
93        Self {
94            types_by_path,
95            encoders: HashMap::new(),
96            decoders: HashMap::new(),
97        }
98    }
99
100    pub fn with_default_custom_type_transcoders(self) -> Self {
101        self.register_custom_type_transcoder::<AccountId32, _>(env_types::AccountId)
102            .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::AccountId, _>(env_types::AccountId)
103            .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::Hash, _>(env_types::H256)
104            .register_custom_type_transcoder::<primitive_types::H160, _>(env_types::H160)
105            .register_custom_type_transcoder::<primitive_types::H256, _>(env_types::H256)
106            .register_custom_type_transcoder::<primitive_types::U256, _>(env_types::U256)
107    }
108
109    pub fn register_custom_type_transcoder<T, U>(self, transcoder: U) -> Self
110    where
111        T: TypeInfo + 'static,
112        U: CustomTypeEncoder + CustomTypeDecoder + Clone + 'static,
113    {
114        self.register_custom_type_encoder::<T, U>(transcoder.clone())
115            .register_custom_type_decoder::<T, U>(transcoder)
116    }
117
118    pub fn register_custom_type_encoder<T, U>(self, encoder: U) -> Self
119    where
120        T: TypeInfo + 'static,
121        U: CustomTypeEncoder + 'static,
122    {
123        let mut this = self;
124
125        let path_key = PathKey::from_type::<T>();
126        let type_id = this.types_by_path.get(&path_key);
127
128        match type_id {
129            Some(type_id) => {
130                let existing = this.encoders.insert(*type_id, Box::new(encoder));
131                tracing::debug!("Registered custom encoder for type `{:?}`", type_id);
132                if existing.is_some() {
133                    panic!(
134                        "Attempted to register encoder with existing type id {type_id:?}"
135                    );
136                }
137            }
138            None => {
139                // if the type is not present in the registry, it just means it has not
140                // been used.
141                tracing::debug!("No matching type in registry for path {:?}.", path_key);
142            }
143        }
144        this
145    }
146
147    pub fn register_custom_type_decoder<T, U>(self, encoder: U) -> Self
148    where
149        T: TypeInfo + 'static,
150        U: CustomTypeDecoder + 'static,
151    {
152        let mut this = self;
153
154        let path_key = PathKey::from_type::<T>();
155        let type_id = this.types_by_path.get(&path_key);
156
157        match type_id {
158            Some(type_id) => {
159                let existing = this.decoders.insert(*type_id, Box::new(encoder));
160                tracing::debug!("Registered custom decoder for type `{:?}`", type_id);
161                if existing.is_some() {
162                    panic!(
163                        "Attempted to register decoder with existing type id {type_id:?}"
164                    );
165                }
166            }
167            None => {
168                // if the type is not present in the registry, it just means it has not
169                // been used.
170                tracing::debug!("No matching type in registry for path {:?}.", path_key);
171            }
172        }
173        this
174    }
175
176    pub fn done(self) -> Transcoder {
177        let env_types_transcoder = EnvTypesTranscoder::new(self.encoders, self.decoders);
178        Transcoder::new(env_types_transcoder)
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::{
185        super::scon::{
186            self,
187            Hex,
188            Map,
189            Seq,
190            Tuple,
191            Value,
192        },
193        *,
194    };
195    use scale::Encode;
196    use scale_info::{
197        MetaType,
198        Registry,
199        TypeInfo,
200    };
201    use std::str::FromStr;
202
203    fn registry_with_type<T>() -> Result<(PortableRegistry, u32)>
204    where
205        T: scale_info::TypeInfo + 'static,
206    {
207        let mut registry = Registry::new();
208        let type_id = registry.register_type(&MetaType::new::<T>());
209        let registry: PortableRegistry = registry.into();
210
211        Ok((registry, type_id.id))
212    }
213
214    fn transcode_roundtrip<T>(input: &str, expected_output: Value) -> Result<()>
215    where
216        T: scale_info::TypeInfo + 'static,
217    {
218        let (registry, ty) = registry_with_type::<T>()?;
219        let transcoder = TranscoderBuilder::new(&registry)
220            .with_default_custom_type_transcoders()
221            .done();
222
223        let value = scon::parse_value(input)?;
224
225        let mut output = Vec::new();
226        transcoder.encode(&registry, ty, &value, &mut output)?;
227        let decoded = transcoder.decode(&registry, ty, &mut &output[..])?;
228        assert_eq!(expected_output, decoded, "decoding");
229        Ok(())
230    }
231
232    #[test]
233    fn transcode_bool() -> Result<()> {
234        transcode_roundtrip::<bool>("true", Value::Bool(true))?;
235        transcode_roundtrip::<bool>("false", Value::Bool(false))
236    }
237
238    #[test]
239    fn transcode_char_unsupported() -> Result<()> {
240        let (registry, ty) = registry_with_type::<char>()?;
241        let transcoder = Transcoder::new(Default::default());
242        let encoded = u32::from('c').encode();
243
244        assert!(transcoder
245            .encode(&registry, ty, &Value::Char('c'), &mut Vec::new())
246            .is_err());
247        assert!(transcoder.decode(&registry, ty, &mut &encoded[..]).is_err());
248        Ok(())
249    }
250
251    #[test]
252    fn transcode_str() -> Result<()> {
253        transcode_roundtrip::<String>("\"ink!\"", Value::String("ink!".to_string()))
254    }
255
256    #[test]
257    fn transcode_unsigned_integers() -> Result<()> {
258        transcode_roundtrip::<u8>("0", Value::UInt(0))?;
259        transcode_roundtrip::<u8>("255", Value::UInt(255))?;
260
261        transcode_roundtrip::<u16>("0", Value::UInt(0))?;
262        transcode_roundtrip::<u16>("65535", Value::UInt(65535))?;
263
264        transcode_roundtrip::<u32>("0", Value::UInt(0))?;
265        transcode_roundtrip::<u32>("4294967295", Value::UInt(4294967295))?;
266
267        transcode_roundtrip::<u64>("0", Value::UInt(0))?;
268        transcode_roundtrip::<u64>(
269            "\"18_446_744_073_709_551_615\"",
270            Value::UInt(18446744073709551615),
271        )?;
272
273        transcode_roundtrip::<u128>("0", Value::UInt(0))?;
274        transcode_roundtrip::<u128>(
275            "\"340_282_366_920_938_463_463_374_607_431_768_211_455\"",
276            Value::UInt(340282366920938463463374607431768211455),
277        )
278    }
279
280    #[test]
281    fn transcode_integers() -> Result<()> {
282        transcode_roundtrip::<i8>("-128", Value::Int(i8::MIN.into()))?;
283        transcode_roundtrip::<i8>("127", Value::Int(i8::MAX.into()))?;
284
285        transcode_roundtrip::<i16>("-32768", Value::Int(i16::MIN.into()))?;
286        transcode_roundtrip::<i16>("32767", Value::Int(i16::MAX.into()))?;
287
288        transcode_roundtrip::<i32>("-2147483648", Value::Int(i32::MIN.into()))?;
289        transcode_roundtrip::<i32>("2147483647", Value::Int(i32::MAX.into()))?;
290
291        transcode_roundtrip::<i64>("-9223372036854775808", Value::Int(i64::MIN.into()))?;
292        transcode_roundtrip::<i64>(
293            "\"9_223_372_036_854_775_807\"",
294            Value::Int(i64::MAX.into()),
295        )?;
296
297        transcode_roundtrip::<i128>(
298            "-170141183460469231731687303715884105728",
299            Value::Int(i128::MIN),
300        )?;
301        transcode_roundtrip::<i128>(
302            "\"170141183460469231731687303715884105727\"",
303            Value::Int(i128::MAX),
304        )
305    }
306
307    #[test]
308    fn transcode_byte_array() -> Result<()> {
309        transcode_roundtrip::<[u8; 2]>(
310            r#"0x0000"#,
311            Value::Seq(vec![Value::UInt(0x00), Value::UInt(0x00)].into()),
312        )?;
313        transcode_roundtrip::<[u8; 4]>(
314            r#"0xDEADBEEF"#,
315            Value::Seq(
316                vec![
317                    Value::UInt(0xDE),
318                    Value::UInt(0xAD),
319                    Value::UInt(0xBE),
320                    Value::UInt(0xEF),
321                ]
322                .into(),
323            ),
324        )?;
325        transcode_roundtrip::<[u8; 4]>(
326            r#"0xdeadbeef"#,
327            Value::Seq(
328                vec![
329                    Value::UInt(0xDE),
330                    Value::UInt(0xAD),
331                    Value::UInt(0xBE),
332                    Value::UInt(0xEF),
333                ]
334                .into(),
335            ),
336        )
337    }
338
339    #[test]
340    fn transcode_array() -> Result<()> {
341        transcode_roundtrip::<[u32; 3]>(
342            "[1, 2, 3]",
343            Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
344        )?;
345        transcode_roundtrip::<[String; 2]>(
346            "[\"hello\", \"world\"]",
347            Value::Seq(
348                vec![
349                    Value::String("hello".to_string()),
350                    Value::String("world".to_string()),
351                ]
352                .into(),
353            ),
354        )
355    }
356
357    #[test]
358    fn transcode_seq() -> Result<()> {
359        transcode_roundtrip::<Vec<u32>>(
360            "[1, 2, 3]",
361            Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
362        )?;
363        transcode_roundtrip::<Vec<String>>(
364            "[\"hello\", \"world\"]",
365            Value::Seq(
366                vec![
367                    Value::String("hello".to_string()),
368                    Value::String("world".to_string()),
369                ]
370                .into(),
371            ),
372        )
373    }
374
375    #[test]
376    fn transcode_tuple() -> Result<()> {
377        transcode_roundtrip::<(u32, String, [u8; 4])>(
378            r#"(1, "ink!", 0xDEADBEEF)"#,
379            Value::Tuple(Tuple::new(
380                None,
381                vec![
382                    Value::UInt(1),
383                    Value::String("ink!".to_string()),
384                    Value::Seq(
385                        vec![
386                            Value::UInt(0xDE),
387                            Value::UInt(0xAD),
388                            Value::UInt(0xBE),
389                            Value::UInt(0xEF),
390                        ]
391                        .into(),
392                    ),
393                ],
394            )),
395        )
396    }
397
398    #[test]
399    fn transcode_composite_struct() -> Result<()> {
400        #[allow(dead_code)]
401        #[derive(TypeInfo)]
402        struct S {
403            a: u32,
404            b: String,
405            c: [u8; 4],
406            // recursive struct ref
407            d: Vec<S>,
408        }
409
410        transcode_roundtrip::<S>(
411            r#"S(a: 1, b: "ink!", c: 0xDEADBEEF, d: [S(a: 2, b: "ink!", c: 0xDEADBEEF, d: [])])"#,
412            Value::Map(
413                vec![
414                    (Value::String("a".to_string()), Value::UInt(1)),
415                    (
416                        Value::String("b".to_string()),
417                        Value::String("ink!".to_string()),
418                    ),
419                    (
420                        Value::String("c".to_string()),
421                        Value::Seq(
422                            vec![
423                                Value::UInt(0xDE),
424                                Value::UInt(0xAD),
425                                Value::UInt(0xBE),
426                                Value::UInt(0xEF),
427                            ]
428                            .into(),
429                        ),
430                    ),
431                    (
432                        Value::String("d".to_string()),
433                        Value::Seq(
434                            vec![Value::Map(
435                                vec![
436                                    (Value::String("a".to_string()), Value::UInt(2)),
437                                    (
438                                        Value::String("b".to_string()),
439                                        Value::String("ink!".to_string()),
440                                    ),
441                                    (
442                                        Value::String("c".to_string()),
443                                        Value::Seq(
444                                            vec![
445                                                Value::UInt(0xDE),
446                                                Value::UInt(0xAD),
447                                                Value::UInt(0xBE),
448                                                Value::UInt(0xEF),
449                                            ]
450                                            .into(),
451                                        ),
452                                    ),
453                                    (
454                                        Value::String("d".to_string()),
455                                        Value::Seq(
456                                            Vec::new()
457                                                .into_iter()
458                                                .collect::<Vec<_>>()
459                                                .into(),
460                                        ),
461                                    ),
462                                ]
463                                .into_iter()
464                                .collect(),
465                            )]
466                            .into(),
467                        ),
468                    ),
469                ]
470                .into_iter()
471                .collect(),
472            ),
473        )
474    }
475
476    #[test]
477    fn transcode_composite_struct_nested() -> Result<()> {
478        #[allow(dead_code)]
479        #[derive(TypeInfo)]
480        struct S {
481            nested: Nested,
482        }
483
484        #[allow(dead_code)]
485        #[derive(TypeInfo)]
486        struct Nested(u32);
487
488        transcode_roundtrip::<S>(
489            r#"S { nested: Nested(33) }"#,
490            Value::Map(Map::new(
491                Some("S"),
492                vec![(
493                    Value::String("nested".to_string()),
494                    Value::Tuple(Tuple::new(
495                        Some("Nested"),
496                        vec![Value::UInt(33)].into_iter().collect(),
497                    )),
498                )]
499                .into_iter()
500                .collect(),
501            )),
502        )
503    }
504
505    #[test]
506    fn transcode_composite_struct_out_of_order_fields() -> Result<()> {
507        #[allow(dead_code)]
508        #[derive(TypeInfo)]
509        struct S {
510            a: u32,
511            b: String,
512            c: [u8; 4],
513        }
514
515        transcode_roundtrip::<S>(
516            r#"S(b: "ink!", a: 1,  c: 0xDEADBEEF)"#,
517            Value::Map(
518                vec![
519                    (Value::String("a".to_string()), Value::UInt(1)),
520                    (
521                        Value::String("b".to_string()),
522                        Value::String("ink!".to_string()),
523                    ),
524                    (
525                        Value::String("c".to_string()),
526                        Value::Seq(
527                            vec![
528                                Value::UInt(0xDE),
529                                Value::UInt(0xAD),
530                                Value::UInt(0xBE),
531                                Value::UInt(0xEF),
532                            ]
533                            .into(),
534                        ),
535                    ),
536                ]
537                .into_iter()
538                .collect(),
539            ),
540        )
541    }
542
543    #[test]
544    fn transcode_composite_tuple_struct() -> Result<()> {
545        #[allow(dead_code)]
546        #[derive(TypeInfo)]
547        struct S(u32, String, [u8; 4]);
548
549        transcode_roundtrip::<S>(
550            r#"S(1, "ink!", 0xDEADBEEF)"#,
551            Value::Tuple(Tuple::new(
552                Some("S"),
553                vec![
554                    Value::UInt(1),
555                    Value::String("ink!".to_string()),
556                    Value::Seq(
557                        vec![
558                            Value::UInt(0xDE),
559                            Value::UInt(0xAD),
560                            Value::UInt(0xBE),
561                            Value::UInt(0xEF),
562                        ]
563                        .into(),
564                    ),
565                ],
566            )),
567        )
568    }
569
570    #[test]
571    fn transcode_composite_single_field_struct() -> Result<()> {
572        #[allow(dead_code)]
573        #[derive(TypeInfo)]
574        struct S([u8; 4]);
575
576        transcode_roundtrip::<S>(
577            r#"0xDEADBEEF"#,
578            Value::Tuple(Tuple::new(
579                Some("S"),
580                vec![Value::Seq(
581                    vec![
582                        Value::UInt(0xDE),
583                        Value::UInt(0xAD),
584                        Value::UInt(0xBE),
585                        Value::UInt(0xEF),
586                    ]
587                    .into(),
588                )],
589            )),
590        )
591    }
592
593    #[test]
594    fn transcode_composite_single_field_tuple() -> Result<()> {
595        transcode_roundtrip::<([u8; 4],)>(
596            r#"0xDEADBEEF"#,
597            Value::Tuple(Tuple::new(
598                None,
599                vec![Value::Seq(
600                    vec![
601                        Value::UInt(0xDE),
602                        Value::UInt(0xAD),
603                        Value::UInt(0xBE),
604                        Value::UInt(0xEF),
605                    ]
606                    .into(),
607                )],
608            )),
609        )
610    }
611
612    #[test]
613    fn transcode_enum_variant_tuple() -> Result<()> {
614        #[derive(TypeInfo)]
615        #[allow(dead_code)]
616        enum E {
617            A(u32, String),
618            B { a: [u8; 4], b: Vec<E> },
619            C,
620        }
621
622        transcode_roundtrip::<E>(
623            r#"A(1, "2")"#,
624            Value::Tuple(Tuple::new(
625                Some("A"),
626                vec![Value::UInt(1), Value::String("2".into())],
627            )),
628        )
629    }
630
631    #[test]
632    fn transcode_enum_variant_map() -> Result<()> {
633        #[derive(TypeInfo)]
634        #[allow(dead_code)]
635        enum E {
636            A { a: u32, b: bool },
637        }
638
639        transcode_roundtrip::<E>(
640            r#"A { a: 33, b: false }"#,
641            Value::Map(Map::new(
642                Some("A"),
643                vec![
644                    (Value::String("a".to_string()), Value::UInt(33)),
645                    (Value::String("b".to_string()), Value::Bool(false)),
646                ]
647                .into_iter()
648                .collect(),
649            )),
650        )
651    }
652
653    #[test]
654    fn transcode_enum_variant_map_out_of_order_fields() -> Result<()> {
655        #[derive(TypeInfo)]
656        #[allow(dead_code)]
657        enum E {
658            A { a: u32, b: bool },
659        }
660
661        transcode_roundtrip::<E>(
662            r#"A { a: 33, b: false }"#,
663            Value::Map(Map::new(
664                Some("A"),
665                vec![
666                    (Value::String("a".to_string()), Value::UInt(33)),
667                    (Value::String("b".to_string()), Value::Bool(false)),
668                ]
669                .into_iter()
670                .collect(),
671            )),
672        )
673    }
674
675    #[test]
676    fn transcode_option() -> Result<()> {
677        transcode_roundtrip::<Option<u32>>(
678            r#"Some(32)"#,
679            Value::Tuple(Tuple::new(Some("Some"), vec![Value::UInt(32)])),
680        )?;
681
682        transcode_roundtrip::<Option<u32>>(
683            r#"None"#,
684            Value::Tuple(Tuple::new(Some("None"), Vec::new())),
685        )
686    }
687
688    #[test]
689    fn transcode_account_id_custom_ss58_encoding() -> Result<()> {
690        type AccountId = AccountId32;
691
692        #[allow(dead_code)]
693        #[derive(TypeInfo)]
694        struct S {
695            no_alias: AccountId32,
696            aliased: AccountId,
697        }
698
699        transcode_roundtrip::<S>(
700            r#"S(
701                no_alias: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
702                aliased: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
703             )"#,
704            Value::Map(Map::new(
705                Some("S"),
706                vec![
707                    (
708                        Value::String("no_alias".into()),
709                        Value::Literal(
710                            "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".into(),
711                        ),
712                    ),
713                    (
714                        Value::String("aliased".into()),
715                        Value::Literal(
716                            "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".into(),
717                        ),
718                    ),
719                ]
720                .into_iter()
721                .collect(),
722            )),
723        )
724    }
725
726    #[test]
727    fn transcode_account_id_custom_ss58_encoding_seq() -> Result<()> {
728        let hex_to_bytes = |hex: &str| -> Result<Value> {
729            let hex = Hex::from_str(hex)?;
730            let values = hex.bytes().iter().map(|b| Value::UInt((*b).into()));
731            Ok(Value::Seq(Seq::new(values.collect())))
732        };
733
734        transcode_roundtrip::<Vec<AccountId32>>(
735            r#"[
736                5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
737                5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
738             ]"#,
739            Value::Seq(Seq::new(
740                vec![
741                    Value::Tuple(
742                        Tuple::new(
743                            Some("AccountId32"),
744                            vec![hex_to_bytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")?]
745                        )
746                    ),
747                    Value::Tuple(
748                        Tuple::new(
749                            Some("AccountId32"),
750                            vec![hex_to_bytes("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48")?]
751                        )
752                    )
753                ]
754                    .into_iter()
755                    .collect(),
756            )),
757        )
758    }
759
760    #[test]
761    fn decode_h256_as_hex_string() -> Result<()> {
762        #[allow(dead_code)]
763        #[derive(TypeInfo)]
764        struct S {
765            hash: primitive_types::H256,
766        }
767
768        transcode_roundtrip::<S>(
769            r#"S(
770                hash: 0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de,
771             )"#,
772            Value::Map(Map::new(
773                Some("S"),
774                vec![
775                    (
776                        Value::String("hash".into()),
777                        Value::Hex(
778                            Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?,
779                        ),
780                    ),
781                ]
782                    .into_iter()
783                    .collect(),
784            )),
785        )
786    }
787
788    #[test]
789    fn transcode_compact_primitives() -> Result<()> {
790        transcode_roundtrip::<scale::Compact<u8>>(r#"33"#, Value::UInt(33))?;
791
792        transcode_roundtrip::<scale::Compact<u16>>(r#"33"#, Value::UInt(33))?;
793
794        transcode_roundtrip::<scale::Compact<u32>>(r#"33"#, Value::UInt(33))?;
795
796        transcode_roundtrip::<scale::Compact<u64>>(r#"33"#, Value::UInt(33))?;
797
798        transcode_roundtrip::<scale::Compact<u128>>(r#"33"#, Value::UInt(33))
799    }
800
801    #[test]
802    fn transcode_compact_struct() -> Result<()> {
803        #[derive(scale::Encode, scale::CompactAs, TypeInfo)]
804        struct CompactStruct(u32);
805
806        #[allow(dead_code)]
807        #[derive(scale::Encode, TypeInfo)]
808        struct S {
809            #[codec(compact)]
810            a: CompactStruct,
811        }
812
813        transcode_roundtrip::<S>(
814            r#"S { a: CompactStruct(33) }"#,
815            Value::Map(Map::new(
816                Some("S"),
817                vec![(
818                    Value::String("a".to_string()),
819                    Value::Tuple(Tuple::new(
820                        Some("CompactStruct"),
821                        vec![Value::UInt(33)],
822                    )),
823                )]
824                .into_iter()
825                .collect(),
826            )),
827        )
828    }
829
830    #[test]
831    fn transcode_hex_literal_uints() -> Result<()> {
832        #[derive(scale::Encode, TypeInfo)]
833        struct S(u8, u16, u32, u64, u128);
834
835        transcode_roundtrip::<S>(
836            r#"S (0xDE, 0xDEAD, 0xDEADBEEF, 0xDEADBEEF12345678, 0xDEADBEEF0123456789ABCDEF01234567)"#,
837            Value::Tuple(Tuple::new(
838                Some("S"),
839                vec![
840                    Value::UInt(0xDE),
841                    Value::UInt(0xDEAD),
842                    Value::UInt(0xDEADBEEF),
843                    Value::UInt(0xDEADBEEF12345678),
844                    Value::UInt(0xDEADBEEF0123456789ABCDEF01234567),
845                ]
846                .into_iter()
847                .collect(),
848            )),
849        )
850    }
851}