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    AccountId32,
19    decode::Decoder,
20    encode::Encoder,
21    env_types::{
22        self,
23        CustomTypeDecoder,
24        CustomTypeEncoder,
25        EnvTypesTranscoder,
26        PathKey,
27        TypesByPath,
28    },
29    scon::Value,
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        tracing::trace!(
157            "Registering custom decoder for type `{:?}` with path `{:?}`",
158            type_id,
159            path_key
160        );
161
162        match type_id {
163            Some(type_id) => {
164                let existing = this.decoders.insert(*type_id, Box::new(encoder));
165                tracing::debug!("Registered custom decoder for type `{:?}`", type_id);
166                if existing.is_some() {
167                    panic!(
168                        "Attempted to register decoder with existing type id {type_id:?}"
169                    );
170                }
171            }
172            None => {
173                // if the type is not present in the registry, it just means it has not
174                // been used.
175                tracing::debug!("No matching type in registry for path {:?}.", path_key);
176            }
177        }
178        this
179    }
180
181    pub fn done(self) -> Transcoder {
182        let env_types_transcoder = EnvTypesTranscoder::new(self.encoders, self.decoders);
183        Transcoder::new(env_types_transcoder)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::{
190        super::scon::{
191            self,
192            Hex,
193            Map,
194            Seq,
195            Tuple,
196            Value,
197        },
198        *,
199    };
200    use primitive_types::H160;
201    use scale::Encode;
202    use scale_info::{
203        MetaType,
204        Registry,
205        TypeInfo,
206    };
207    use std::str::FromStr;
208
209    fn registry_with_type<T>() -> Result<(PortableRegistry, u32)>
210    where
211        T: scale_info::TypeInfo + 'static,
212    {
213        let mut registry = Registry::new();
214        let type_id = registry.register_type(&MetaType::new::<T>());
215        let registry: PortableRegistry = registry.into();
216
217        Ok((registry, type_id.id))
218    }
219
220    fn transcode_roundtrip<T>(input: &str, expected_output: Value) -> Result<()>
221    where
222        T: scale_info::TypeInfo + 'static,
223    {
224        let (registry, ty) = registry_with_type::<T>()?;
225        let transcoder = TranscoderBuilder::new(&registry)
226            .with_default_custom_type_transcoders()
227            .done();
228
229        let value = scon::parse_value(input)?;
230
231        let mut output = Vec::new();
232        transcoder.encode(&registry, ty, &value, &mut output)?;
233        let decoded = transcoder.decode(&registry, ty, &mut &output[..])?;
234        assert_eq!(expected_output, decoded, "decoding");
235        Ok(())
236    }
237
238    #[test]
239    fn transcode_bool() -> Result<()> {
240        transcode_roundtrip::<bool>("true", Value::Bool(true))?;
241        transcode_roundtrip::<bool>("false", Value::Bool(false))
242    }
243
244    #[test]
245    fn transcode_char_unsupported() -> Result<()> {
246        let (registry, ty) = registry_with_type::<char>()?;
247        let transcoder = Transcoder::new(Default::default());
248        let encoded = u32::from('c').encode();
249
250        assert!(
251            transcoder
252                .encode(&registry, ty, &Value::Char('c'), &mut Vec::new())
253                .is_err()
254        );
255        assert!(transcoder.decode(&registry, ty, &mut &encoded[..]).is_err());
256        Ok(())
257    }
258
259    #[test]
260    fn transcode_str() -> Result<()> {
261        transcode_roundtrip::<String>("\"ink!\"", Value::String("ink!".to_string()))
262    }
263
264    #[test]
265    fn transcode_unsigned_integers() -> Result<()> {
266        transcode_roundtrip::<u8>("0", Value::UInt(0))?;
267        transcode_roundtrip::<u8>("255", Value::UInt(255))?;
268
269        transcode_roundtrip::<u16>("0", Value::UInt(0))?;
270        transcode_roundtrip::<u16>("65535", Value::UInt(65535))?;
271
272        transcode_roundtrip::<u32>("0", Value::UInt(0))?;
273        transcode_roundtrip::<u32>("4294967295", Value::UInt(4294967295))?;
274
275        transcode_roundtrip::<u64>("0", Value::UInt(0))?;
276        transcode_roundtrip::<u64>(
277            "\"18_446_744_073_709_551_615\"",
278            Value::UInt(18446744073709551615),
279        )?;
280
281        transcode_roundtrip::<u128>("0", Value::UInt(0))?;
282        transcode_roundtrip::<u128>(
283            "\"340_282_366_920_938_463_463_374_607_431_768_211_455\"",
284            Value::UInt(340282366920938463463374607431768211455),
285        )
286    }
287
288    #[test]
289    fn transcode_integers() -> Result<()> {
290        transcode_roundtrip::<i8>("-128", Value::Int(i8::MIN.into()))?;
291        transcode_roundtrip::<i8>("127", Value::Int(i8::MAX.into()))?;
292
293        transcode_roundtrip::<i16>("-32768", Value::Int(i16::MIN.into()))?;
294        transcode_roundtrip::<i16>("32767", Value::Int(i16::MAX.into()))?;
295
296        transcode_roundtrip::<i32>("-2147483648", Value::Int(i32::MIN.into()))?;
297        transcode_roundtrip::<i32>("2147483647", Value::Int(i32::MAX.into()))?;
298
299        transcode_roundtrip::<i64>("-9223372036854775808", Value::Int(i64::MIN.into()))?;
300        transcode_roundtrip::<i64>(
301            "\"9_223_372_036_854_775_807\"",
302            Value::Int(i64::MAX.into()),
303        )?;
304
305        transcode_roundtrip::<i128>(
306            "-170141183460469231731687303715884105728",
307            Value::Int(i128::MIN),
308        )?;
309        transcode_roundtrip::<i128>(
310            "\"170141183460469231731687303715884105727\"",
311            Value::Int(i128::MAX),
312        )
313    }
314
315    #[test]
316    fn transcode_byte_array() -> Result<()> {
317        transcode_roundtrip::<[u8; 2]>(
318            r#"0x0000"#,
319            Value::Seq(vec![Value::UInt(0x00), Value::UInt(0x00)].into()),
320        )?;
321        transcode_roundtrip::<[u8; 4]>(
322            r#"0xDEADBEEF"#,
323            Value::Seq(
324                vec![
325                    Value::UInt(0xDE),
326                    Value::UInt(0xAD),
327                    Value::UInt(0xBE),
328                    Value::UInt(0xEF),
329                ]
330                .into(),
331            ),
332        )?;
333        transcode_roundtrip::<[u8; 4]>(
334            r#"0xdeadbeef"#,
335            Value::Seq(
336                vec![
337                    Value::UInt(0xDE),
338                    Value::UInt(0xAD),
339                    Value::UInt(0xBE),
340                    Value::UInt(0xEF),
341                ]
342                .into(),
343            ),
344        )
345    }
346
347    #[test]
348    fn transcode_array() -> Result<()> {
349        transcode_roundtrip::<[u32; 3]>(
350            "[1, 2, 3]",
351            Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
352        )?;
353        transcode_roundtrip::<[String; 2]>(
354            "[\"hello\", \"world\"]",
355            Value::Seq(
356                vec![
357                    Value::String("hello".to_string()),
358                    Value::String("world".to_string()),
359                ]
360                .into(),
361            ),
362        )
363    }
364
365    #[test]
366    fn transcode_seq() -> Result<()> {
367        transcode_roundtrip::<Vec<u32>>(
368            "[1, 2, 3]",
369            Value::Seq(vec![Value::UInt(1), Value::UInt(2), Value::UInt(3)].into()),
370        )?;
371        transcode_roundtrip::<Vec<String>>(
372            "[\"hello\", \"world\"]",
373            Value::Seq(
374                vec![
375                    Value::String("hello".to_string()),
376                    Value::String("world".to_string()),
377                ]
378                .into(),
379            ),
380        )
381    }
382
383    #[test]
384    fn transcode_tuple() -> Result<()> {
385        transcode_roundtrip::<(u32, String, [u8; 4])>(
386            r#"(1, "ink!", 0xDEADBEEF)"#,
387            Value::Tuple(Tuple::new(
388                None,
389                vec![
390                    Value::UInt(1),
391                    Value::String("ink!".to_string()),
392                    Value::Seq(
393                        vec![
394                            Value::UInt(0xDE),
395                            Value::UInt(0xAD),
396                            Value::UInt(0xBE),
397                            Value::UInt(0xEF),
398                        ]
399                        .into(),
400                    ),
401                ],
402            )),
403        )
404    }
405
406    #[test]
407    fn transcode_composite_struct() -> Result<()> {
408        #[allow(dead_code)]
409        #[derive(TypeInfo)]
410        struct S {
411            a: u32,
412            b: String,
413            c: [u8; 4],
414            // recursive struct ref
415            d: Vec<S>,
416        }
417
418        transcode_roundtrip::<S>(
419            r#"S(a: 1, b: "ink!", c: 0xDEADBEEF, d: [S(a: 2, b: "ink!", c: 0xDEADBEEF, d: [])])"#,
420            Value::Map(
421                vec![
422                    (Value::String("a".to_string()), Value::UInt(1)),
423                    (
424                        Value::String("b".to_string()),
425                        Value::String("ink!".to_string()),
426                    ),
427                    (
428                        Value::String("c".to_string()),
429                        Value::Seq(
430                            vec![
431                                Value::UInt(0xDE),
432                                Value::UInt(0xAD),
433                                Value::UInt(0xBE),
434                                Value::UInt(0xEF),
435                            ]
436                            .into(),
437                        ),
438                    ),
439                    (
440                        Value::String("d".to_string()),
441                        Value::Seq(
442                            vec![Value::Map(
443                                vec![
444                                    (Value::String("a".to_string()), Value::UInt(2)),
445                                    (
446                                        Value::String("b".to_string()),
447                                        Value::String("ink!".to_string()),
448                                    ),
449                                    (
450                                        Value::String("c".to_string()),
451                                        Value::Seq(
452                                            vec![
453                                                Value::UInt(0xDE),
454                                                Value::UInt(0xAD),
455                                                Value::UInt(0xBE),
456                                                Value::UInt(0xEF),
457                                            ]
458                                            .into(),
459                                        ),
460                                    ),
461                                    (
462                                        Value::String("d".to_string()),
463                                        Value::Seq(
464                                            Vec::new()
465                                                .into_iter()
466                                                .collect::<Vec<_>>()
467                                                .into(),
468                                        ),
469                                    ),
470                                ]
471                                .into_iter()
472                                .collect(),
473                            )]
474                            .into(),
475                        ),
476                    ),
477                ]
478                .into_iter()
479                .collect(),
480            ),
481        )
482    }
483
484    #[test]
485    fn transcode_composite_struct_nested() -> Result<()> {
486        #[allow(dead_code)]
487        #[derive(TypeInfo)]
488        struct S {
489            nested: Nested,
490        }
491
492        #[allow(dead_code)]
493        #[derive(TypeInfo)]
494        struct Nested(u32);
495
496        transcode_roundtrip::<S>(
497            r#"S { nested: Nested(33) }"#,
498            Value::Map(Map::new(
499                Some("S"),
500                vec![(
501                    Value::String("nested".to_string()),
502                    Value::Tuple(Tuple::new(
503                        Some("Nested"),
504                        vec![Value::UInt(33)].into_iter().collect(),
505                    )),
506                )]
507                .into_iter()
508                .collect(),
509            )),
510        )
511    }
512
513    #[test]
514    fn transcode_composite_struct_out_of_order_fields() -> Result<()> {
515        #[allow(dead_code)]
516        #[derive(TypeInfo)]
517        struct S {
518            a: u32,
519            b: String,
520            c: [u8; 4],
521        }
522
523        transcode_roundtrip::<S>(
524            r#"S(b: "ink!", a: 1,  c: 0xDEADBEEF)"#,
525            Value::Map(
526                vec![
527                    (Value::String("a".to_string()), Value::UInt(1)),
528                    (
529                        Value::String("b".to_string()),
530                        Value::String("ink!".to_string()),
531                    ),
532                    (
533                        Value::String("c".to_string()),
534                        Value::Seq(
535                            vec![
536                                Value::UInt(0xDE),
537                                Value::UInt(0xAD),
538                                Value::UInt(0xBE),
539                                Value::UInt(0xEF),
540                            ]
541                            .into(),
542                        ),
543                    ),
544                ]
545                .into_iter()
546                .collect(),
547            ),
548        )
549    }
550
551    #[test]
552    fn transcode_composite_tuple_struct() -> Result<()> {
553        #[allow(dead_code)]
554        #[derive(TypeInfo)]
555        struct S(u32, String, [u8; 4]);
556
557        transcode_roundtrip::<S>(
558            r#"S(1, "ink!", 0xDEADBEEF)"#,
559            Value::Tuple(Tuple::new(
560                Some("S"),
561                vec![
562                    Value::UInt(1),
563                    Value::String("ink!".to_string()),
564                    Value::Seq(
565                        vec![
566                            Value::UInt(0xDE),
567                            Value::UInt(0xAD),
568                            Value::UInt(0xBE),
569                            Value::UInt(0xEF),
570                        ]
571                        .into(),
572                    ),
573                ],
574            )),
575        )
576    }
577
578    #[test]
579    fn transcode_composite_single_field_struct() -> Result<()> {
580        #[allow(dead_code)]
581        #[derive(TypeInfo)]
582        struct S([u8; 4]);
583
584        transcode_roundtrip::<S>(
585            r#"0xDEADBEEF"#,
586            Value::Tuple(Tuple::new(
587                Some("S"),
588                vec![Value::Seq(
589                    vec![
590                        Value::UInt(0xDE),
591                        Value::UInt(0xAD),
592                        Value::UInt(0xBE),
593                        Value::UInt(0xEF),
594                    ]
595                    .into(),
596                )],
597            )),
598        )
599    }
600
601    #[test]
602    fn transcode_composite_single_field_tuple() -> Result<()> {
603        transcode_roundtrip::<([u8; 4],)>(
604            r#"0xDEADBEEF"#,
605            Value::Tuple(Tuple::new(
606                None,
607                vec![Value::Seq(
608                    vec![
609                        Value::UInt(0xDE),
610                        Value::UInt(0xAD),
611                        Value::UInt(0xBE),
612                        Value::UInt(0xEF),
613                    ]
614                    .into(),
615                )],
616            )),
617        )
618    }
619
620    #[test]
621    fn transcode_enum_variant_tuple() -> Result<()> {
622        #[derive(TypeInfo)]
623        #[allow(dead_code)]
624        enum E {
625            A(u32, String),
626            B { a: [u8; 4], b: Vec<E> },
627            C,
628        }
629
630        transcode_roundtrip::<E>(
631            r#"A(1, "2")"#,
632            Value::Tuple(Tuple::new(
633                Some("A"),
634                vec![Value::UInt(1), Value::String("2".into())],
635            )),
636        )
637    }
638
639    #[test]
640    fn transcode_enum_variant_map() -> Result<()> {
641        #[derive(TypeInfo)]
642        #[allow(dead_code)]
643        enum E {
644            A { a: u32, b: bool },
645        }
646
647        transcode_roundtrip::<E>(
648            r#"A { a: 33, b: false }"#,
649            Value::Map(Map::new(
650                Some("A"),
651                vec![
652                    (Value::String("a".to_string()), Value::UInt(33)),
653                    (Value::String("b".to_string()), Value::Bool(false)),
654                ]
655                .into_iter()
656                .collect(),
657            )),
658        )
659    }
660
661    #[test]
662    fn transcode_enum_variant_map_out_of_order_fields() -> Result<()> {
663        #[derive(TypeInfo)]
664        #[allow(dead_code)]
665        enum E {
666            A { a: u32, b: bool },
667        }
668
669        transcode_roundtrip::<E>(
670            r#"A { a: 33, b: false }"#,
671            Value::Map(Map::new(
672                Some("A"),
673                vec![
674                    (Value::String("a".to_string()), Value::UInt(33)),
675                    (Value::String("b".to_string()), Value::Bool(false)),
676                ]
677                .into_iter()
678                .collect(),
679            )),
680        )
681    }
682
683    #[test]
684    fn transcode_option() -> Result<()> {
685        transcode_roundtrip::<Option<u32>>(
686            r#"Some(32)"#,
687            Value::Tuple(Tuple::new(Some("Some"), vec![Value::UInt(32)])),
688        )?;
689
690        transcode_roundtrip::<Option<u32>>(
691            r#"None"#,
692            Value::Tuple(Tuple::new(Some("None"), Vec::new())),
693        )
694    }
695
696    #[test]
697    fn transcode_account_id_custom_ss58_encoding() -> Result<()> {
698        type AccountId = AccountId32;
699
700        #[allow(dead_code)]
701        #[derive(TypeInfo)]
702        struct S {
703            no_alias: AccountId32,
704            aliased: AccountId,
705        }
706
707        transcode_roundtrip::<S>(
708            r#"S(
709                no_alias: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
710                aliased: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
711             )"#,
712            Value::Map(Map::new(
713                Some("S"),
714                vec![
715                    (
716                        Value::String("no_alias".into()),
717                        Value::Literal(
718                            "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".into(),
719                        ),
720                    ),
721                    (
722                        Value::String("aliased".into()),
723                        Value::Literal(
724                            "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".into(),
725                        ),
726                    ),
727                ]
728                .into_iter()
729                .collect(),
730            )),
731        )
732    }
733
734    #[test]
735    fn transcode_account_id_custom_ss58_encoding_seq() -> Result<()> {
736        transcode_roundtrip::<Vec<AccountId32>>(
737            r#"[
738                5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
739                5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty,
740             ]"#,
741            Value::Seq(Seq::new(vec![
742                Value::Literal("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".into()),
743                Value::Literal("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty".into()),
744            ])),
745        )
746    }
747
748    #[test]
749    fn decode_h256_as_hex_string() -> Result<()> {
750        #[allow(dead_code)]
751        #[derive(TypeInfo)]
752        struct S {
753            hash: primitive_types::H256,
754        }
755
756        transcode_roundtrip::<S>(
757            r#"S(
758                hash: 0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de,
759             )"#,
760            Value::Map(Map::new(
761                Some("S"),
762                vec![
763                    (
764                        Value::String("hash".into()),
765                        Value::Hex(
766                            Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?,
767                        ),
768                    ),
769                ]
770                    .into_iter()
771                    .collect(),
772            )),
773        )
774    }
775
776    #[test]
777    fn transcode_compact_primitives() -> Result<()> {
778        transcode_roundtrip::<scale::Compact<u8>>(r#"33"#, Value::UInt(33))?;
779
780        transcode_roundtrip::<scale::Compact<u16>>(r#"33"#, Value::UInt(33))?;
781
782        transcode_roundtrip::<scale::Compact<u32>>(r#"33"#, Value::UInt(33))?;
783
784        transcode_roundtrip::<scale::Compact<u64>>(r#"33"#, Value::UInt(33))?;
785
786        transcode_roundtrip::<scale::Compact<u128>>(r#"33"#, Value::UInt(33))
787    }
788
789    #[test]
790    fn transcode_compact_struct() -> Result<()> {
791        #[derive(scale::Encode, scale::CompactAs, TypeInfo)]
792        struct CompactStruct(u32);
793
794        #[allow(dead_code)]
795        #[derive(scale::Encode, TypeInfo)]
796        struct S {
797            #[codec(compact)]
798            a: CompactStruct,
799        }
800
801        transcode_roundtrip::<S>(
802            r#"S { a: CompactStruct(33) }"#,
803            Value::Map(Map::new(
804                Some("S"),
805                vec![(
806                    Value::String("a".to_string()),
807                    Value::Tuple(Tuple::new(
808                        Some("CompactStruct"),
809                        vec![Value::UInt(33)],
810                    )),
811                )]
812                .into_iter()
813                .collect(),
814            )),
815        )
816    }
817
818    #[test]
819    fn transcode_hex_literal_uints() -> Result<()> {
820        #[derive(scale::Encode, TypeInfo)]
821        struct S(u8, u16, u32, u64, u128);
822
823        transcode_roundtrip::<S>(
824            r#"S (0xDE, 0xDEAD, 0xDEADBEEF, 0xDEADBEEF12345678, 0xDEADBEEF0123456789ABCDEF01234567)"#,
825            Value::Tuple(Tuple::new(
826                Some("S"),
827                vec![
828                    Value::UInt(0xDE),
829                    Value::UInt(0xDEAD),
830                    Value::UInt(0xDEADBEEF),
831                    Value::UInt(0xDEADBEEF12345678),
832                    Value::UInt(0xDEADBEEF0123456789ABCDEF01234567),
833                ]
834                .into_iter()
835                .collect(),
836            )),
837        )
838    }
839
840    #[test]
841    fn transcode_h160_vec() -> Result<()> {
842        transcode_roundtrip::<Vec<H160>>(
843            r#"[
844                0x9621dde636de098b43efb0fa9b61facfe328f99d,
845                0x41dccbd49b26c50d34355ed86ff0fa9e489d1e01
846            ]"#,
847            Value::Seq(Seq::new(vec![
848                Value::Hex(Hex::from_str("0x9621dde636de098b43efb0fa9b61facfe328f99d")?),
849                Value::Hex(Hex::from_str("0x41dccbd49b26c50d34355ed86ff0fa9e489d1e01")?),
850            ])),
851        )
852    }
853}