contract_transcode/
lib.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
17//! For interacting with contracts from the command line, arguments need to be
18//! "transcoded" from the string representation to the SCALE encoded representation.
19//!
20//! e.g. `"false" -> 0x00`
21//!
22//! And for displaying SCALE encoded data from events and RPC responses, it must be
23//! "transcoded" in the other direction from the SCALE encoded representation to a human
24//! readable string.
25//!
26//! e.g. `0x00 -> "false"`
27//!
28//! Transcoding depends on [`scale-info`](https://github.com/paritytech/scale-info/) metadata in
29//! order to dynamically determine the expected types.
30//!
31//! # Encoding
32//!
33//! First the string is parsed into an intermediate [`Value`]:
34//!
35//! `"false" -> Value::Bool(false)`
36//!
37//! This value is then matched with the metadata for the expected type in that context.
38//! e.g. the [flipper](https://github.com/use-ink/ink/blob/master/examples/flipper/lib.rs) contract
39//! accepts a `bool` argument to its `new` constructor, which will be reflected in the
40//! contract metadata as [`scale_info::TypeDefPrimitive::Bool`].
41//!
42//! ```no_compile
43//! #[ink(constructor)]
44//! pub fn new(init_value: bool) -> Self {
45//!     Self { value: init_value }
46//! }
47//! ```
48//!
49//! The parsed `Value::Bool(false)` argument value is then matched with the
50//! [`scale_info::TypeDefPrimitive::Bool`] type metadata, and then the value can be safely
51//! encoded as a `bool`, resulting in `0x00`, which can then be appended as data to the
52//! message to invoke the constructor.
53//!
54//! # Decoding
55//!
56//! First the type of the SCALE encoded data is determined from the metadata. e.g. the
57//! return type of a message when it is invoked as a "dry run" over RPC:
58//!
59//! ```no_compile
60//! #[ink(message)]
61//! pub fn get(&self) -> bool {
62//!     self.value
63//! }
64//! ```
65//!
66//! The metadata will define the return type as [`scale_info::TypeDefPrimitive::Bool`], so
67//! that when the raw data is received it can be decoded into the correct [`Value`], which
68//! is then converted to a string for displaying to the user:
69//!
70//! `0x00 -> Value::Bool(false) -> "false"`
71//!
72//! # SCALE Object Notation (SCON)
73//!
74//! Complex types can be represented as strings using `SCON` for human-computer
75//! interaction. It is intended to be similar to Rust syntax for instantiating types. e.g.
76//!
77//! `Foo { a: false, b: [0, 1, 2], c: "bar", d: (0, 1) }`
78//!
79//! This string could be parsed into a [`Value::Map`] and together with
80//! [`scale_info::TypeDefComposite`] metadata could be transcoded into SCALE encoded
81//! bytes.
82//!
83//! As with the example for the primitive `bool` above, this works in the other direction
84//! for decoding SCALE encoded bytes and converting them into a human readable string.
85//!
86//! # Example
87//! ```no_run
88//! # use contract_metadata::ContractMetadata;
89//! # use contract_transcode::ContractMessageTranscoder;
90//! # use std::{path::Path, fs::File};
91//! let metadata_path = Path::new("/path/to/contract.json");
92//! let transcoder = ContractMessageTranscoder::load(metadata_path).unwrap();
93//!
94//! let constructor = "new";
95//! let args = ["foo", "bar"];
96//! let data = transcoder.encode(&constructor, &args).unwrap();
97//!
98//! println!("Encoded constructor data {:?}", data);
99//! ```
100
101mod account_id;
102mod decode;
103mod encode;
104pub mod env_types;
105mod scon;
106mod transcoder;
107mod util;
108
109pub use self::{
110    account_id::AccountId32,
111    scon::{
112        Hex,
113        Map,
114        Tuple,
115        Value,
116    },
117    transcoder::{
118        Transcoder,
119        TranscoderBuilder,
120    },
121};
122
123use anyhow::{
124    Context,
125    Result,
126};
127pub use ink_metadata;
128use ink_metadata::{
129    ConstructorSpec,
130    InkProject,
131    MessageSpec,
132};
133use itertools::Itertools;
134use scale::{
135    Compact,
136    Decode,
137    Input,
138};
139use scale_info::{
140    form::{
141        Form,
142        PortableForm,
143    },
144    Field,
145};
146use std::{
147    cmp::Ordering,
148    fmt::Debug,
149    path::Path,
150};
151
152/// Encode strings to SCALE encoded smart contract calls.
153/// Decode SCALE encoded smart contract events and return values into `Value` objects.
154pub struct ContractMessageTranscoder {
155    metadata: InkProject,
156    transcoder: Transcoder,
157}
158
159/// Find strings from an iterable of `possible_values` similar to a given value `v`
160/// Returns a Vec of all possible values that exceed a similarity threshold
161/// sorted by ascending similarity, most similar comes last
162/// Extracted from https://github.com/clap-rs/clap/blob/v4.3.4/clap_builder/src/parser/features/suggestions.rs#L11-L26
163fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
164where
165    T: AsRef<str>,
166    I: IntoIterator<Item = T>,
167{
168    let mut candidates: Vec<(f64, String)> = possible_values
169        .into_iter()
170        .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
171        .filter(|(confidence, _)| *confidence > 0.7)
172        .collect();
173    candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
174    candidates.into_iter().map(|(_, pv)| pv).collect()
175}
176
177impl ContractMessageTranscoder {
178    pub fn new(metadata: InkProject) -> Self {
179        let transcoder = TranscoderBuilder::new(metadata.registry())
180            .register_custom_type_transcoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::AccountId, _>(env_types::AccountId)
181            .register_custom_type_decoder::<<ink_env::DefaultEnvironment as ink_env::Environment>::Hash, _>(env_types::Hash)
182            .done();
183        Self {
184            metadata,
185            transcoder,
186        }
187    }
188
189    /// Attempt to create a [`ContractMessageTranscoder`] from the metadata file at the
190    /// given path.
191    pub fn load<P>(metadata_path: P) -> Result<Self>
192    where
193        P: AsRef<Path>,
194    {
195        let path = metadata_path.as_ref();
196        let metadata: contract_metadata::ContractMetadata =
197            contract_metadata::ContractMetadata::load(&metadata_path)?;
198        let ink_metadata = serde_json::from_value(serde_json::Value::Object(
199            metadata.abi,
200        ))
201        .context(format!(
202            "Failed to deserialize ink project metadata from file {}",
203            path.display()
204        ))?;
205
206        Ok(Self::new(ink_metadata))
207    }
208
209    pub fn encode<I, S>(&self, name: &str, args: I) -> Result<Vec<u8>>
210    where
211        I: IntoIterator<Item = S>,
212        S: AsRef<str> + Debug,
213    {
214        let (selector, spec_args) = match (
215            self.find_constructor_spec(name),
216            self.find_message_spec(name),
217        ) {
218            (Some(c), None) => (c.selector(), c.args()),
219            (None, Some(m)) => (m.selector(), m.args()),
220            (Some(_), Some(_)) => {
221                return Err(anyhow::anyhow!(
222                "Invalid metadata: both a constructor and message found with name '{}'",
223                name
224            ))
225            }
226            (None, None) => {
227                let constructors = self.constructors().map(|c| c.label());
228                let messages = self.messages().map(|c| c.label());
229                let possible_values: Vec<_> = constructors.chain(messages).collect();
230                let help_txt = did_you_mean(name, possible_values.clone())
231                    .first()
232                    .map(|suggestion| format!("Did you mean '{}'?", suggestion))
233                    .unwrap_or_else(|| {
234                        format!("Should be one of: {}", possible_values.iter().join(", "))
235                    });
236
237                return Err(anyhow::anyhow!(
238                    "No constructor or message with the name '{name}' found.\n{help_txt}",
239                ))
240            }
241        };
242
243        let args: Vec<_> = args.into_iter().collect();
244        if spec_args.len() != args.len() {
245            anyhow::bail!(
246                "Invalid number of input arguments: expected {}, {} provided",
247                spec_args.len(),
248                args.len()
249            )
250        }
251
252        let mut encoded = selector.to_bytes().to_vec();
253        for (spec, arg) in spec_args.iter().zip(args) {
254            let value = scon::parse_value(arg.as_ref())?;
255            self.transcoder.encode(
256                self.metadata.registry(),
257                spec.ty().ty().id,
258                &value,
259                &mut encoded,
260            )?;
261        }
262        Ok(encoded)
263    }
264
265    pub fn decode(&self, type_id: u32, input: &mut &[u8]) -> Result<Value> {
266        self.transcoder
267            .decode(self.metadata.registry(), type_id, input)
268    }
269
270    pub fn metadata(&self) -> &InkProject {
271        &self.metadata
272    }
273
274    fn constructors(&self) -> impl Iterator<Item = &ConstructorSpec<PortableForm>> {
275        self.metadata.spec().constructors().iter()
276    }
277
278    fn messages(&self) -> impl Iterator<Item = &MessageSpec<PortableForm>> {
279        self.metadata.spec().messages().iter()
280    }
281
282    fn find_message_spec(&self, name: &str) -> Option<&MessageSpec<PortableForm>> {
283        self.messages().find(|msg| msg.label() == &name.to_string())
284    }
285
286    fn find_constructor_spec(
287        &self,
288        name: &str,
289    ) -> Option<&ConstructorSpec<PortableForm>> {
290        self.constructors()
291            .find(|msg| msg.label() == &name.to_string())
292    }
293
294    pub fn decode_contract_event<Hash>(
295        &self,
296        event_sig_topic: &Hash,
297        data: &mut &[u8],
298    ) -> Result<Value>
299    where
300        Hash: AsRef<[u8]>,
301    {
302        // data is an encoded `Vec<u8>` so is prepended with its length `Compact<u32>`,
303        // which we ignore because the structure of the event data is known for
304        // decoding.
305        let _len = <Compact<u32>>::decode(data)?;
306        let event_spec = self
307            .metadata
308            .spec()
309            .events()
310            .iter()
311            .find(|event| {
312                if let Some(sig_topic) = event.signature_topic() {
313                    sig_topic.as_bytes() == event_sig_topic.as_ref()
314                } else {
315                    false
316                }
317            })
318            .ok_or_else(|| {
319                anyhow::anyhow!(
320                    "Event with signature topic {} not found in contract metadata",
321                    hex::encode(event_sig_topic)
322                )
323            })?;
324        tracing::debug!("Decoding contract event '{}'", event_spec.label());
325
326        let mut args = Vec::new();
327        for arg in event_spec.args() {
328            let name = arg.label().to_string();
329            let value = self.decode(arg.ty().ty().id, data)?;
330            args.push((Value::String(name), value));
331        }
332
333        Self::validate_length(data, event_spec.label(), &args)?;
334
335        let name = event_spec.label().to_string();
336        let map = Map::new(Some(&name), args.into_iter().collect());
337
338        Ok(Value::Map(map))
339    }
340
341    pub fn decode_contract_message(&self, data: &mut &[u8]) -> Result<Value> {
342        let mut msg_selector = [0u8; 4];
343        data.read(&mut msg_selector)?;
344        let msg_spec = self
345            .messages()
346            .find(|x| msg_selector == x.selector().to_bytes())
347            .ok_or_else(|| {
348                anyhow::anyhow!(
349                    "Message with selector {} not found in contract metadata",
350                    hex::encode_upper(msg_selector)
351                )
352            })?;
353        tracing::debug!("Decoding contract message '{}'", msg_spec.label());
354
355        let mut args = Vec::new();
356        for arg in msg_spec.args() {
357            let name = arg.label().to_string();
358            let value = self.decode(arg.ty().ty().id, data)?;
359            args.push((Value::String(name), value));
360        }
361
362        Self::validate_length(data, msg_spec.label(), &args)?;
363
364        let name = msg_spec.label().to_string();
365        let map = Map::new(Some(&name), args.into_iter().collect());
366
367        Ok(Value::Map(map))
368    }
369
370    pub fn decode_contract_constructor(&self, data: &mut &[u8]) -> Result<Value> {
371        let mut msg_selector = [0u8; 4];
372        data.read(&mut msg_selector)?;
373        let msg_spec = self
374            .constructors()
375            .find(|x| msg_selector == x.selector().to_bytes())
376            .ok_or_else(|| {
377                anyhow::anyhow!(
378                    "Constructor with selector {} not found in contract metadata",
379                    hex::encode_upper(msg_selector)
380                )
381            })?;
382        tracing::debug!("Decoding contract constructor '{}'", msg_spec.label());
383
384        let mut args = Vec::new();
385        for arg in msg_spec.args() {
386            let name = arg.label().to_string();
387            let value = self.decode(arg.ty().ty().id, data)?;
388            args.push((Value::String(name), value));
389        }
390
391        Self::validate_length(data, msg_spec.label(), &args)?;
392
393        let name = msg_spec.label().to_string();
394        let map = Map::new(Some(&name), args.into_iter().collect());
395
396        Ok(Value::Map(map))
397    }
398
399    pub fn decode_constructor_return(
400        &self,
401        name: &str,
402        data: &mut &[u8],
403    ) -> Result<Value> {
404        let ctor_spec = self.find_constructor_spec(name).ok_or_else(|| {
405            anyhow::anyhow!("Failed to find constructor spec with name '{}'", name)
406        })?;
407        let return_ty = ctor_spec.return_type().ret_type();
408        self.decode(return_ty.ty().id, data)
409    }
410
411    pub fn decode_message_return(&self, name: &str, data: &mut &[u8]) -> Result<Value> {
412        let msg_spec = self.find_message_spec(name).ok_or_else(|| {
413            anyhow::anyhow!("Failed to find message spec with name '{}'", name)
414        })?;
415        let return_ty = msg_spec.return_type().ret_type();
416        self.decode(return_ty.ty().id, data)
417    }
418
419    /// Checks if buffer empty, otherwise returns am error
420    fn validate_length(data: &[u8], label: &str, args: &[(Value, Value)]) -> Result<()> {
421        if !data.is_empty() {
422            let arg_list_string: String =
423                args.iter().fold(format!("`{label}`"), |init, arg| {
424                    format!("{}, `{}`", init, arg.0)
425                });
426            let encoded_bytes = hex::encode_upper(data);
427            return Err(anyhow::anyhow!(
428                "input length was longer than expected by {} byte(s).\nManaged to decode {} but `{}` bytes were left unread",
429                data.len(),
430                arg_list_string,
431                encoded_bytes
432            ));
433        }
434        Ok(())
435    }
436}
437
438impl TryFrom<contract_metadata::ContractMetadata> for ContractMessageTranscoder {
439    type Error = anyhow::Error;
440
441    fn try_from(
442        metadata: contract_metadata::ContractMetadata,
443    ) -> Result<Self, Self::Error> {
444        Ok(Self::new(serde_json::from_value(
445            serde_json::Value::Object(metadata.abi),
446        )?))
447    }
448}
449
450#[derive(Debug)]
451pub enum CompositeTypeFields {
452    Named(Vec<CompositeTypeNamedField>),
453    Unnamed(Vec<Field<PortableForm>>),
454    NoFields,
455}
456
457#[derive(Debug)]
458pub struct CompositeTypeNamedField {
459    name: <PortableForm as Form>::String,
460    field: Field<PortableForm>,
461}
462
463impl CompositeTypeNamedField {
464    pub fn name(&self) -> &str {
465        &self.name
466    }
467
468    pub fn field(&self) -> &Field<PortableForm> {
469        &self.field
470    }
471}
472
473impl CompositeTypeFields {
474    pub fn from_fields(fields: &[Field<PortableForm>]) -> Result<Self> {
475        if fields.iter().next().is_none() {
476            Ok(Self::NoFields)
477        } else if fields.iter().all(|f| f.name.is_some()) {
478            let fields = fields
479                .iter()
480                .map(|field| {
481                    CompositeTypeNamedField {
482                        name: field
483                            .name
484                            .as_ref()
485                            .expect("All fields have a name; qed")
486                            .to_owned(),
487                        field: field.clone(),
488                    }
489                })
490                .collect();
491            Ok(Self::Named(fields))
492        } else if fields.iter().all(|f| f.name.is_none()) {
493            Ok(Self::Unnamed(fields.to_vec()))
494        } else {
495            Err(anyhow::anyhow!(
496                "Struct fields should either be all named or all unnamed"
497            ))
498        }
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505    use ink_env::{
506        DefaultEnvironment,
507        Environment,
508    };
509    use primitive_types::H256;
510    use scale::Encode;
511    use scon::Value;
512    use std::str::FromStr;
513
514    use crate::scon::Hex;
515
516    #[allow(clippy::extra_unused_lifetimes, unexpected_cfgs, non_local_definitions)]
517    #[ink::contract]
518    pub mod transcode {
519        #[ink(storage)]
520        pub struct Transcode {
521            value: bool,
522        }
523
524        #[ink(event)]
525        pub struct Event1 {
526            #[ink(topic)]
527            name: Hash,
528            #[ink(topic)]
529            from: AccountId,
530        }
531
532        impl Transcode {
533            #[ink(constructor)]
534            pub fn new(init_value: bool) -> Self {
535                Self { value: init_value }
536            }
537
538            #[ink(constructor)]
539            pub fn default() -> Self {
540                Self::new(Default::default())
541            }
542
543            #[ink(message)]
544            pub fn flip(&mut self) {
545                self.value = !self.value;
546            }
547
548            #[ink(message)]
549            pub fn get(&self) -> bool {
550                self.value
551            }
552
553            #[ink(message)]
554            pub fn set_account_id(&self, account_id: AccountId) {
555                let _ = account_id;
556            }
557
558            #[ink(message)]
559            pub fn set_account_ids_vec(&self, account_ids: Vec<AccountId>) {
560                let _ = account_ids;
561            }
562
563            #[ink(message)]
564            pub fn primitive_vec_args(&self, args: Vec<u32>) {
565                let _ = args;
566            }
567
568            #[ink(message)]
569            pub fn uint_args(
570                &self,
571                _u8: u8,
572                _u16: u16,
573                _u32: u32,
574                _u64: u64,
575                _u128: u128,
576            ) {
577            }
578
579            #[ink(message)]
580            pub fn uint_array_args(&self, arr: [u8; 4]) {
581                let _ = arr;
582            }
583        }
584    }
585
586    fn generate_metadata() -> InkProject {
587        extern "Rust" {
588            fn __ink_generate_metadata() -> InkProject;
589        }
590
591        unsafe { __ink_generate_metadata() }
592    }
593
594    #[test]
595    fn encode_single_primitive_arg() -> Result<()> {
596        let metadata = generate_metadata();
597        let transcoder = ContractMessageTranscoder::new(metadata);
598
599        let encoded = transcoder.encode("new", ["true"])?;
600        // encoded args follow the 4 byte selector
601        let encoded_args = &encoded[4..];
602
603        assert_eq!(true.encode(), encoded_args);
604        Ok(())
605    }
606
607    #[test]
608    fn encode_misspelled_arg() {
609        let metadata = generate_metadata();
610        let transcoder = ContractMessageTranscoder::new(metadata);
611        assert_eq!(
612            transcoder.encode("fip", ["true"]).unwrap_err().to_string(),
613            "No constructor or message with the name 'fip' found.\nDid you mean 'flip'?"
614        );
615    }
616
617    #[test]
618    fn encode_mismatching_args_length() {
619        let metadata = generate_metadata();
620        let transcoder = ContractMessageTranscoder::new(metadata);
621
622        let result: Result<Vec<u8>> = transcoder.encode("new", Vec::<&str>::new());
623        assert!(result.is_err(), "Should return an error");
624        assert_eq!(
625            result.unwrap_err().to_string(),
626            "Invalid number of input arguments: expected 1, 0 provided"
627        );
628
629        let result: Result<Vec<u8>> = transcoder.encode("new", ["true", "false"]);
630        assert!(result.is_err(), "Should return an error");
631        assert_eq!(
632            result.unwrap_err().to_string(),
633            "Invalid number of input arguments: expected 1, 2 provided"
634        );
635    }
636
637    #[test]
638    fn encode_account_id_custom_ss58_encoding() -> Result<()> {
639        let metadata = generate_metadata();
640        let transcoder = ContractMessageTranscoder::new(metadata);
641
642        let encoded = transcoder.encode(
643            "set_account_id",
644            ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
645        )?;
646
647        // encoded args follow the 4 byte selector
648        let encoded_args = &encoded[4..];
649
650        let expected =
651            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
652                .unwrap();
653        assert_eq!(expected.encode(), encoded_args);
654        Ok(())
655    }
656
657    #[test]
658    fn encode_account_ids_vec_args() -> Result<()> {
659        let metadata = generate_metadata();
660        let transcoder = ContractMessageTranscoder::new(metadata);
661
662        let encoded = transcoder.encode(
663            "set_account_ids_vec",
664            ["[5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty]"],
665        )?;
666
667        // encoded args follow the 4 byte selector
668        let encoded_args = &encoded[4..];
669
670        let expected = vec![
671            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
672                .unwrap(),
673            AccountId32::from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")
674                .unwrap(),
675        ];
676        assert_eq!(expected.encode(), encoded_args);
677        Ok(())
678    }
679
680    #[test]
681    fn encode_primitive_vec_args() -> Result<()> {
682        let metadata = generate_metadata();
683        let transcoder = ContractMessageTranscoder::new(metadata);
684
685        let encoded = transcoder.encode("primitive_vec_args", ["[1, 2]"])?;
686
687        // encoded args follow the 4 byte selector
688        let encoded_args = &encoded[4..];
689
690        let expected = vec![1, 2];
691        assert_eq!(expected.encode(), encoded_args);
692        Ok(())
693    }
694
695    #[test]
696    fn encode_uint_hex_literals() -> Result<()> {
697        let metadata = generate_metadata();
698        let transcoder = ContractMessageTranscoder::new(metadata);
699
700        let encoded = transcoder.encode(
701            "uint_args",
702            [
703                "0x00",
704                "0xDEAD",
705                "0xDEADBEEF",
706                "0xDEADBEEF12345678",
707                "0xDEADBEEF0123456789ABCDEF01234567",
708            ],
709        )?;
710
711        // encoded args follow the 4 byte selector
712        let encoded_args = &encoded[4..];
713
714        let expected = (
715            0x00u8,
716            0xDEADu16,
717            0xDEADBEEFu32,
718            0xDEADBEEF12345678u64,
719            0xDEADBEEF0123456789ABCDEF01234567u128,
720        );
721        assert_eq!(expected.encode(), encoded_args);
722        Ok(())
723    }
724
725    #[test]
726    fn encode_uint_arr_hex_literals() -> Result<()> {
727        let metadata = generate_metadata();
728        let transcoder = ContractMessageTranscoder::new(metadata);
729
730        let encoded =
731            transcoder.encode("uint_array_args", ["[0xDE, 0xAD, 0xBE, 0xEF]"])?;
732
733        // encoded args follow the 4 byte selector
734        let encoded_args = &encoded[4..];
735
736        let expected: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
737        assert_eq!(expected.encode(), encoded_args);
738        Ok(())
739    }
740
741    #[test]
742    fn decode_primitive_return() {
743        let metadata = generate_metadata();
744        let transcoder = ContractMessageTranscoder::new(metadata);
745
746        let encoded = Result::<bool, ink::primitives::LangError>::Ok(true).encode();
747        let decoded = transcoder
748            .decode_message_return("get", &mut &encoded[..])
749            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
750
751        let expected = Value::Tuple(Tuple::new(
752            "Ok".into(),
753            [Value::Bool(true)].into_iter().collect(),
754        ));
755        assert_eq!(expected, decoded);
756    }
757
758    #[test]
759    fn decode_lang_error() {
760        use ink::primitives::LangError;
761
762        let metadata = generate_metadata();
763        let transcoder = ContractMessageTranscoder::new(metadata);
764
765        let encoded =
766            Result::<bool, LangError>::Err(LangError::CouldNotReadInput).encode();
767        let decoded = transcoder
768            .decode_message_return("get", &mut &encoded[..])
769            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
770
771        let expected = Value::Tuple(Tuple::new(
772            "Err".into(),
773            [Value::Tuple(Tuple::new(
774                Some("CouldNotReadInput"),
775                Vec::new(),
776            ))]
777            .to_vec(),
778        ));
779        assert_eq!(expected, decoded);
780    }
781
782    #[test]
783    fn decode_contract_event() -> Result<()> {
784        let metadata = generate_metadata();
785        let transcoder = ContractMessageTranscoder::new(metadata);
786
787        let signature_topic: <DefaultEnvironment as Environment>::Hash =
788            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
789                .unwrap()
790                .into();
791        // raw encoded event
792        let encoded = ([0u32; 8], [1u32; 8]).encode();
793        // encode again as a Vec<u8> which has a len prefix.
794        let encoded_bytes = encoded.encode();
795        let _ = transcoder
796            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
797
798        Ok(())
799    }
800
801    #[test]
802    fn decode_hash_as_hex_encoded_string() -> Result<()> {
803        let metadata = generate_metadata();
804        let transcoder = ContractMessageTranscoder::new(metadata);
805
806        let hash = [
807            52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240,
808            83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222,
809        ];
810        let signature_topic: <DefaultEnvironment as Environment>::Hash =
811            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
812                .unwrap()
813                .into();
814        // raw encoded event with event index prefix
815        let encoded = (hash, [0u32; 8]).encode();
816        // encode again as a Vec<u8> which has a len prefix.
817        let encoded_bytes = encoded.encode();
818        let decoded = transcoder
819            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
820
821        if let Value::Map(ref map) = decoded {
822            let name_field = &map[&Value::String("name".into())];
823            if let Value::Hex(hex) = name_field {
824                assert_eq!(&Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?, hex);
825                Ok(())
826            } else {
827                Err(anyhow::anyhow!(
828                    "Expected a name field hash encoded as Hex value, was {:?}",
829                    name_field
830                ))
831            }
832        } else {
833            Err(anyhow::anyhow!(
834                "Expected a Value::Map for the decoded event"
835            ))
836        }
837    }
838
839    #[test]
840    fn decode_contract_message() -> Result<()> {
841        let metadata = generate_metadata();
842        let transcoder = ContractMessageTranscoder::new(metadata);
843
844        let encoded_bytes = hex::decode("633aa551").unwrap();
845        let _ = transcoder.decode_contract_message(&mut &encoded_bytes[..])?;
846
847        Ok(())
848    }
849
850    #[test]
851    #[should_panic(
852        expected = "input length was longer than expected by 1 byte(s).\nManaged to decode `flip` but `00` bytes were left unread"
853    )]
854    fn fail_decode_input_with_extra_bytes() {
855        let metadata = generate_metadata();
856        let transcoder = ContractMessageTranscoder::new(metadata);
857
858        let encoded_bytes = hex::decode("633aa55100").unwrap();
859        let _ = transcoder
860            .decode_contract_message(&mut &encoded_bytes[..])
861            .unwrap();
862    }
863
864    #[test]
865    #[should_panic(
866        expected = "input length was longer than expected by 2 byte(s).\nManaged to decode `Event1`, `name`, `from` but `0C10` bytes were left unread"
867    )]
868    fn fail_decode_contract_event_with_extra_bytes() {
869        let metadata = generate_metadata();
870        let transcoder = ContractMessageTranscoder::new(metadata);
871
872        let signature_topic: H256 =
873            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
874                .unwrap()
875                .into();
876        // raw encoded event with event index prefix
877        let encoded = ([0u32; 8], [1u32; 8], [12u8, 16u8]).encode();
878        // encode again as a Vec<u8> which has a len prefix.
879        let encoded_bytes = encoded.encode();
880        let _ = transcoder
881            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])
882            .unwrap();
883    }
884}