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
24//! human-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 regex::Regex;
135use scale::{
136    Compact,
137    Decode,
138    Input,
139};
140use scale_info::{
141    form::{
142        Form,
143        PortableForm,
144    },
145    Field,
146};
147use std::{
148    cmp::Ordering,
149    fmt::Debug,
150    path::Path,
151};
152
153/// Encode strings to SCALE encoded smart contract calls.
154/// Decode SCALE encoded smart contract events and return values into `Value` objects.
155pub struct ContractMessageTranscoder {
156    metadata: InkProject,
157    transcoder: Transcoder,
158}
159
160/// Find strings from an iterable of `possible_values` similar to a given value `v`
161/// Returns a Vec of all possible values that exceed a similarity threshold
162/// sorted by ascending similarity, most similar comes last
163/// Extracted from https://github.com/clap-rs/clap/blob/v4.3.4/clap_builder/src/parser/features/suggestions.rs#L11-L26
164fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
165where
166    T: AsRef<str>,
167    I: IntoIterator<Item = T>,
168{
169    let mut candidates: Vec<(f64, String)> = possible_values
170        .into_iter()
171        .map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
172        .filter(|(confidence, _)| *confidence > 0.7)
173        .collect();
174    candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
175    candidates.into_iter().map(|(_, pv)| pv).collect()
176}
177
178impl ContractMessageTranscoder {
179    pub fn new(metadata: InkProject) -> Self {
180        let transcoder = TranscoderBuilder::new(metadata.registry())
181            .with_default_custom_type_transcoders()
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            assert_not_shortened_hex(arg.as_ref());
255            let value = scon::parse_value(arg.as_ref())?;
256            self.transcoder.encode(
257                self.metadata.registry(),
258                spec.ty().ty().id,
259                &value,
260                &mut encoded,
261            )?;
262        }
263        Ok(encoded)
264    }
265
266    pub fn decode(&self, type_id: u32, input: &mut &[u8]) -> Result<Value> {
267        self.transcoder
268            .decode(self.metadata.registry(), type_id, input)
269    }
270
271    pub fn metadata(&self) -> &InkProject {
272        &self.metadata
273    }
274
275    fn constructors(&self) -> impl Iterator<Item = &ConstructorSpec<PortableForm>> {
276        self.metadata.spec().constructors().iter()
277    }
278
279    fn messages(&self) -> impl Iterator<Item = &MessageSpec<PortableForm>> {
280        self.metadata.spec().messages().iter()
281    }
282
283    fn find_message_spec(&self, name: &str) -> Option<&MessageSpec<PortableForm>> {
284        self.messages().find(|msg| msg.label() == &name.to_string())
285    }
286
287    fn find_constructor_spec(
288        &self,
289        name: &str,
290    ) -> Option<&ConstructorSpec<PortableForm>> {
291        self.constructors()
292            .find(|msg| msg.label() == &name.to_string())
293    }
294
295    pub fn decode_contract_event<Hash>(
296        &self,
297        event_sig_topic: &Hash,
298        data: &mut &[u8],
299    ) -> Result<Value>
300    where
301        Hash: AsRef<[u8]>,
302    {
303        // data is an encoded `Vec<u8>` so is prepended with its length `Compact<u32>`,
304        // which we ignore because the structure of the event data is known for
305        // decoding.
306        let _len = <Compact<u32>>::decode(data)?;
307        let event_spec = self
308            .metadata
309            .spec()
310            .events()
311            .iter()
312            .find(|event| {
313                if let Some(sig_topic) = event.signature_topic() {
314                    sig_topic.as_bytes() == event_sig_topic.as_ref()
315                } else {
316                    false
317                }
318            })
319            .ok_or_else(|| {
320                anyhow::anyhow!(
321                    "Event with signature topic {} not found in contract metadata",
322                    hex::encode(event_sig_topic)
323                )
324            })?;
325        tracing::debug!("Decoding contract event '{}'", event_spec.label());
326
327        let mut args = Vec::new();
328        for arg in event_spec.args() {
329            let name = arg.label().to_string();
330            let value = self.decode(arg.ty().ty().id, data)?;
331            args.push((Value::String(name), value));
332        }
333
334        Self::validate_length(data, event_spec.label(), &args)?;
335
336        let name = event_spec.label().to_string();
337        let map = Map::new(Some(&name), args.into_iter().collect());
338
339        Ok(Value::Map(map))
340    }
341
342    pub fn decode_contract_message(&self, data: &mut &[u8]) -> Result<Value> {
343        let mut msg_selector = [0u8; 4];
344        data.read(&mut msg_selector)?;
345        let msg_spec = self
346            .messages()
347            .find(|x| msg_selector == x.selector().to_bytes())
348            .ok_or_else(|| {
349                anyhow::anyhow!(
350                    "Message with selector {} not found in contract metadata",
351                    hex::encode_upper(msg_selector)
352                )
353            })?;
354        tracing::debug!("Decoding contract message '{}'", msg_spec.label());
355
356        let mut args = Vec::new();
357        for arg in msg_spec.args() {
358            let name = arg.label().to_string();
359            let value = self.decode(arg.ty().ty().id, data)?;
360            args.push((Value::String(name), value));
361        }
362
363        Self::validate_length(data, msg_spec.label(), &args)?;
364
365        let name = msg_spec.label().to_string();
366        let map = Map::new(Some(&name), args.into_iter().collect());
367
368        Ok(Value::Map(map))
369    }
370
371    pub fn decode_contract_constructor(&self, data: &mut &[u8]) -> Result<Value> {
372        let mut msg_selector = [0u8; 4];
373        data.read(&mut msg_selector)?;
374        let msg_spec = self
375            .constructors()
376            .find(|x| msg_selector == x.selector().to_bytes())
377            .ok_or_else(|| {
378                anyhow::anyhow!(
379                    "Constructor with selector {} not found in contract metadata",
380                    hex::encode_upper(msg_selector)
381                )
382            })?;
383        tracing::debug!("Decoding contract constructor '{}'", msg_spec.label());
384
385        let mut args = Vec::new();
386        for arg in msg_spec.args() {
387            let name = arg.label().to_string();
388            let value = self.decode(arg.ty().ty().id, data)?;
389            args.push((Value::String(name), value));
390        }
391
392        Self::validate_length(data, msg_spec.label(), &args)?;
393
394        let name = msg_spec.label().to_string();
395        let map = Map::new(Some(&name), args.into_iter().collect());
396
397        Ok(Value::Map(map))
398    }
399
400    pub fn decode_constructor_return(
401        &self,
402        name: &str,
403        data: &mut &[u8],
404    ) -> Result<Value> {
405        let ctor_spec = self.find_constructor_spec(name).ok_or_else(|| {
406            anyhow::anyhow!("Failed to find constructor spec with name '{}'", name)
407        })?;
408        let return_ty = ctor_spec.return_type().ret_type();
409        self.decode(return_ty.ty().id, data)
410    }
411
412    pub fn decode_message_return(&self, name: &str, data: &mut &[u8]) -> Result<Value> {
413        let msg_spec = self.find_message_spec(name).ok_or_else(|| {
414            anyhow::anyhow!("Failed to find message spec with name '{}'", name)
415        })?;
416        let return_ty = msg_spec.return_type().ret_type();
417        self.decode(return_ty.ty().id, data)
418    }
419
420    /// Checks if buffer empty, otherwise returns am error
421    fn validate_length(data: &[u8], label: &str, args: &[(Value, Value)]) -> Result<()> {
422        if !data.is_empty() {
423            let arg_list_string: String =
424                args.iter().fold(format!("`{label}`"), |init, arg| {
425                    format!("{}, `{}`", init, arg.0)
426                });
427            let encoded_bytes = hex::encode_upper(data);
428            return Err(anyhow::anyhow!(
429                "input length was longer than expected by {} byte(s).\nManaged to decode {} but `{}` bytes were left unread",
430                data.len(),
431                arg_list_string,
432                encoded_bytes
433            ));
434        }
435        Ok(())
436    }
437}
438
439// Assert that `arg` is not in a shortened format a la `0xbc3f…f58a`.
440fn assert_not_shortened_hex(arg: &str) {
441    let re = Regex::new(r"^0x[a-fA-F0-9]+…[a-fA-F0-9]+$").unwrap();
442    if re.is_match(arg) {
443        panic!("Error: You are attempting to transcode a shortened hex value: `{:?}`.\n\
444                This would result in a different return value than the un-shortened hex value.\n\
445                You likely called `to_string()` on e.g. `H160` and got a shortened output.", arg);
446    }
447}
448
449impl TryFrom<contract_metadata::ContractMetadata> for ContractMessageTranscoder {
450    type Error = anyhow::Error;
451
452    fn try_from(
453        metadata: contract_metadata::ContractMetadata,
454    ) -> Result<Self, Self::Error> {
455        Ok(Self::new(serde_json::from_value(
456            serde_json::Value::Object(metadata.abi),
457        )?))
458    }
459}
460
461#[derive(Debug)]
462pub enum CompositeTypeFields {
463    Named(Vec<CompositeTypeNamedField>),
464    Unnamed(Vec<Field<PortableForm>>),
465    NoFields,
466}
467
468#[derive(Debug)]
469pub struct CompositeTypeNamedField {
470    name: <PortableForm as Form>::String,
471    field: Field<PortableForm>,
472}
473
474impl CompositeTypeNamedField {
475    pub fn name(&self) -> &str {
476        &self.name
477    }
478
479    pub fn field(&self) -> &Field<PortableForm> {
480        &self.field
481    }
482}
483
484impl CompositeTypeFields {
485    pub fn from_fields(fields: &[Field<PortableForm>]) -> Result<Self> {
486        if fields.iter().next().is_none() {
487            Ok(Self::NoFields)
488        } else if fields.iter().all(|f| f.name.is_some()) {
489            let fields = fields
490                .iter()
491                .map(|field| {
492                    CompositeTypeNamedField {
493                        name: field
494                            .name
495                            .as_ref()
496                            .expect("All fields have a name; qed")
497                            .to_owned(),
498                        field: field.clone(),
499                    }
500                })
501                .collect();
502            Ok(Self::Named(fields))
503        } else if fields.iter().all(|f| f.name.is_none()) {
504            Ok(Self::Unnamed(fields.to_vec()))
505        } else {
506            Err(anyhow::anyhow!(
507                "Struct fields should either be all named or all unnamed"
508            ))
509        }
510    }
511}
512
513#[cfg(test)]
514mod tests {
515    use super::*;
516    use crate::scon::Hex;
517    use ink_env::{
518        DefaultEnvironment,
519        Environment,
520    };
521    use primitive_types::H256;
522    use scale::Encode;
523    use scon::Value;
524    use std::str::FromStr;
525
526    #[allow(clippy::extra_unused_lifetimes, unexpected_cfgs, non_local_definitions)]
527    #[ink::contract]
528    pub mod transcode {
529        #[ink(storage)]
530        pub struct Transcode {
531            value: bool,
532        }
533
534        #[ink(event)]
535        pub struct Event1 {
536            #[ink(topic)]
537            name: Hash,
538            #[ink(topic)]
539            from: AccountId,
540        }
541
542        impl Transcode {
543            #[ink(constructor)]
544            pub fn new(init_value: bool) -> Self {
545                Self { value: init_value }
546            }
547
548            #[ink(constructor)]
549            pub fn default() -> Self {
550                Self::new(Default::default())
551            }
552
553            #[ink(message)]
554            pub fn flip(&mut self) {
555                self.value = !self.value;
556            }
557
558            #[ink(message)]
559            pub fn get(&self) -> bool {
560                self.value
561            }
562
563            #[ink(message)]
564            pub fn get_complex(
565                &self,
566            ) -> (u32, ink::H160, ink::H256, ink::U256, AccountId) {
567                (
568                    32u32,
569                    self.env().address(),
570                    self.env().own_code_hash().unwrap(),
571                    //self.env().transferred_value()
572                    ink::U256::one(),
573                    AccountId::from([0x17; 32]),
574                )
575            }
576            //pub fn get_complex(&mut self) -> (ink::H160, Hash, ink::H256, ink::U256) {
577            //(self.env().address(), [0xABu8; 32].into(),
578            /*
579            pub fn get_complex(&self) -> (ink::H160, ink::H256, ink::U256) {
580                (self.env().address(),
581                self.env().own_code_hash().unwrap(), self.env().transferred_value())
582            }
583             */
584
585            #[ink(message)]
586            pub fn set_account_id(&self, account_id: AccountId) {
587                let _ = account_id;
588            }
589
590            #[ink(message)]
591            pub fn set_account_ids_vec(&self, account_ids: Vec<AccountId>) {
592                let _ = account_ids;
593            }
594
595            #[ink(message)]
596            pub fn primitive_vec_args(&self, args: Vec<u32>) {
597                let _ = args;
598            }
599
600            #[ink(message)]
601            pub fn uint_args(
602                &self,
603                _u8: u8,
604                _u16: u16,
605                _u32: u32,
606                _u64: u64,
607                _u128: u128,
608            ) {
609            }
610
611            #[ink(message)]
612            pub fn uint_array_args(&self, arr: [u8; 4]) {
613                let _ = arr;
614            }
615
616            #[ink(message)]
617            pub fn h160(&self, addr: ink::H160) {
618                let _ = addr;
619            }
620        }
621    }
622
623    fn generate_metadata() -> InkProject {
624        extern "Rust" {
625            fn __ink_generate_metadata() -> InkProject;
626        }
627
628        unsafe { __ink_generate_metadata() }
629    }
630
631    #[test]
632    fn encode_single_primitive_arg() -> Result<()> {
633        let metadata = generate_metadata();
634        let transcoder = ContractMessageTranscoder::new(metadata);
635
636        let encoded = transcoder.encode("new", ["true"])?;
637        // encoded args follow the 4 byte selector
638        let encoded_args = &encoded[4..];
639
640        assert_eq!(true.encode(), encoded_args);
641        Ok(())
642    }
643
644    #[test]
645    fn encode_misspelled_arg() {
646        let metadata = generate_metadata();
647        let transcoder = ContractMessageTranscoder::new(metadata);
648        assert_eq!(
649            transcoder.encode("fip", ["true"]).unwrap_err().to_string(),
650            "No constructor or message with the name 'fip' found.\nDid you mean 'flip'?"
651        );
652    }
653
654    #[test]
655    fn encode_mismatching_args_length() {
656        let metadata = generate_metadata();
657        let transcoder = ContractMessageTranscoder::new(metadata);
658
659        let result: Result<Vec<u8>> = transcoder.encode("new", Vec::<&str>::new());
660        assert!(result.is_err(), "Should return an error");
661        assert_eq!(
662            result.unwrap_err().to_string(),
663            "Invalid number of input arguments: expected 1, 0 provided"
664        );
665
666        let result: Result<Vec<u8>> = transcoder.encode("new", ["true", "false"]);
667        assert!(result.is_err(), "Should return an error");
668        assert_eq!(
669            result.unwrap_err().to_string(),
670            "Invalid number of input arguments: expected 1, 2 provided"
671        );
672    }
673
674    #[test]
675    fn encode_account_id_custom_ss58_encoding() -> Result<()> {
676        let metadata = generate_metadata();
677        let transcoder = ContractMessageTranscoder::new(metadata);
678
679        let encoded = transcoder.encode(
680            "set_account_id",
681            ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
682        )?;
683
684        // encoded args follow the 4 byte selector
685        let encoded_args = &encoded[4..];
686
687        let expected =
688            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
689                .unwrap();
690        assert_eq!(expected.encode(), encoded_args);
691        Ok(())
692    }
693
694    #[test]
695    fn encode_account_ids_vec_args() -> Result<()> {
696        let metadata = generate_metadata();
697        let transcoder = ContractMessageTranscoder::new(metadata);
698
699        let encoded = transcoder.encode(
700            "set_account_ids_vec",
701            ["[5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty]"],
702        )?;
703
704        // encoded args follow the 4 byte selector
705        let encoded_args = &encoded[4..];
706
707        let expected = vec![
708            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
709                .unwrap(),
710            AccountId32::from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")
711                .unwrap(),
712        ];
713        assert_eq!(expected.encode(), encoded_args);
714        Ok(())
715    }
716
717    #[test]
718    fn encode_primitive_vec_args() -> Result<()> {
719        let metadata = generate_metadata();
720        let transcoder = ContractMessageTranscoder::new(metadata);
721
722        let encoded = transcoder.encode("primitive_vec_args", ["[1, 2]"])?;
723
724        // encoded args follow the 4 byte selector
725        let encoded_args = &encoded[4..];
726
727        let expected = vec![1, 2];
728        assert_eq!(expected.encode(), encoded_args);
729        Ok(())
730    }
731
732    #[test]
733    fn encode_uint_hex_literals() -> Result<()> {
734        let metadata = generate_metadata();
735        let transcoder = ContractMessageTranscoder::new(metadata);
736
737        let encoded = transcoder.encode(
738            "uint_args",
739            [
740                "0x00",
741                "0xDEAD",
742                "0xDEADBEEF",
743                "0xDEADBEEF12345678",
744                "0xDEADBEEF0123456789ABCDEF01234567",
745            ],
746        )?;
747
748        // encoded args follow the 4 byte selector
749        let encoded_args = &encoded[4..];
750
751        let expected = (
752            0x00u8,
753            0xDEADu16,
754            0xDEADBEEFu32,
755            0xDEADBEEF12345678u64,
756            0xDEADBEEF0123456789ABCDEF01234567u128,
757        );
758        assert_eq!(expected.encode(), encoded_args);
759        Ok(())
760    }
761
762    #[test]
763    fn encode_uint_arr_hex_literals() -> Result<()> {
764        let metadata = generate_metadata();
765        let transcoder = ContractMessageTranscoder::new(metadata);
766
767        let encoded =
768            transcoder.encode("uint_array_args", ["[0xDE, 0xAD, 0xBE, 0xEF]"])?;
769
770        // encoded args follow the 4 byte selector
771        let encoded_args = &encoded[4..];
772
773        let expected: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
774        assert_eq!(expected.encode(), encoded_args);
775        Ok(())
776    }
777
778    #[test]
779    #[should_panic(
780        expected = "Error: You are attempting to transcode a shortened hex value: `\"0xbc3f…f58a\"`"
781    )]
782    fn encode_must_panic_on_shortened_hex() {
783        let metadata = generate_metadata();
784        let transcoder = ContractMessageTranscoder::new(metadata);
785
786        let _encoded = transcoder.encode("h160", ["0xbc3f…f58a"]);
787    }
788
789    #[test]
790    fn decode_complex_return() {
791        let metadata = generate_metadata();
792        let transcoder = ContractMessageTranscoder::new(metadata);
793
794        let addr: ink::H160 = ink::H160::from([0x42; 20]);
795        let account_id: AccountId32 = AccountId32::from([0x13; 32]);
796        let _hash: [u8; 32] = [0xAB; 32];
797        let h256: ink::H256 = ink::H256::from([0x17; 32]);
798        // todo let value: ink::U256 = ink::U256::MAX;
799        let value: ink::U256 = ink::U256::one();
800
801        let encoded = Result::<
802            (u32, ink::H160, ink::H256, ink::U256, AccountId32),
803            ink::primitives::LangError,
804        >::Ok((32, addr, h256, value, account_id))
805        .encode();
806
807        let decoded = transcoder
808            .decode_message_return("get_complex", &mut &encoded[..])
809            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
810
811        let expected = Value::Tuple(Tuple::new(
812            "Ok".into(),
813            [
814                Value::Tuple(Tuple::new(
815                    None,
816                    [
817                        Value::UInt(32),
818                        Value::Hex(Hex::from_str("0x4242424242424242424242424242424242424242").unwrap()),
819                        Value::Hex(Hex::from_str("0x1717171717171717171717171717171717171717171717171717171717171717").unwrap()),
820                        Value::Literal("1".to_string()),
821                        Value::Literal("5CViS5pKamF1VbJ9tmQKPNDpLpJaBCfpPw2m49UzQ8zgiDGT".to_string()),
822                    ]
823                    .into_iter().collect()
824                ))
825            ]
826            .into_iter().collect(),
827        ));
828        assert_eq!(expected, decoded);
829    }
830
831    #[test]
832    fn decode_primitive_return() {
833        let metadata = generate_metadata();
834        let transcoder = ContractMessageTranscoder::new(metadata);
835
836        let encoded = Result::<bool, ink::primitives::LangError>::Ok(true).encode();
837        let decoded = transcoder
838            .decode_message_return("get", &mut &encoded[..])
839            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
840
841        let expected = Value::Tuple(Tuple::new(
842            "Ok".into(),
843            [Value::Bool(true)].into_iter().collect(),
844        ));
845        assert_eq!(expected, decoded);
846    }
847
848    #[test]
849    fn decode_lang_error() {
850        use ink::primitives::LangError;
851
852        let metadata = generate_metadata();
853        let transcoder = ContractMessageTranscoder::new(metadata);
854
855        let encoded =
856            Result::<bool, LangError>::Err(LangError::CouldNotReadInput).encode();
857        let decoded = transcoder
858            .decode_message_return("get", &mut &encoded[..])
859            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
860
861        let expected = Value::Tuple(Tuple::new(
862            "Err".into(),
863            [Value::Tuple(Tuple::new(
864                Some("CouldNotReadInput"),
865                Vec::new(),
866            ))]
867            .to_vec(),
868        ));
869        assert_eq!(expected, decoded);
870    }
871
872    #[test]
873    fn decode_contract_event() -> Result<()> {
874        let metadata = generate_metadata();
875        let transcoder = ContractMessageTranscoder::new(metadata);
876
877        let signature_topic: <DefaultEnvironment as Environment>::Hash =
878            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
879                .unwrap()
880                .into();
881        // raw encoded event
882        let encoded = ([0u32; 8], [1u32; 8]).encode();
883        // encode again as a Vec<u8> which has a len prefix.
884        let encoded_bytes = encoded.encode();
885        let _ = transcoder
886            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
887
888        // todo assert is missing
889
890        Ok(())
891    }
892
893    #[test]
894    fn decode_hash_as_hex_encoded_string() -> Result<()> {
895        let metadata = generate_metadata();
896        let transcoder = ContractMessageTranscoder::new(metadata);
897
898        let hash = [
899            52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240,
900            83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222,
901        ];
902        let signature_topic: <DefaultEnvironment as Environment>::Hash =
903            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
904                .unwrap()
905                .into();
906        // raw encoded event with event index prefix
907        let encoded = (hash, [0u32; 8]).encode();
908        // encode again as a Vec<u8> which has a len prefix.
909        let encoded_bytes = encoded.encode();
910        let decoded = transcoder
911            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
912
913        if let Value::Map(ref map) = decoded {
914            let name_field = &map[&Value::String("name".into())];
915            if let Value::Hex(hex) = name_field {
916                assert_eq!(&Hex::from_str("0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de")?, hex);
917                Ok(())
918            } else {
919                Err(anyhow::anyhow!(
920                    "Expected a name field hash encoded as Hex value, was {:?}",
921                    name_field
922                ))
923            }
924        } else {
925            Err(anyhow::anyhow!(
926                "Expected a Value::Map for the decoded event"
927            ))
928        }
929    }
930
931    #[test]
932    fn decode_contract_message() -> Result<()> {
933        let metadata = generate_metadata();
934        let transcoder = ContractMessageTranscoder::new(metadata);
935
936        let encoded_bytes = hex::decode("633aa551").unwrap();
937        let _ = transcoder.decode_contract_message(&mut &encoded_bytes[..])?;
938
939        Ok(())
940    }
941
942    #[test]
943    #[should_panic(
944        expected = "input length was longer than expected by 1 byte(s).\nManaged to decode `flip` but `00` bytes were left unread"
945    )]
946    fn fail_decode_input_with_extra_bytes() {
947        let metadata = generate_metadata();
948        let transcoder = ContractMessageTranscoder::new(metadata);
949
950        let encoded_bytes = hex::decode("633aa55100").unwrap();
951        let _ = transcoder
952            .decode_contract_message(&mut &encoded_bytes[..])
953            .unwrap();
954    }
955
956    #[test]
957    #[should_panic(
958        expected = "input length was longer than expected by 2 byte(s).\nManaged to decode `Event1`, `name`, `from` but `0C10` bytes were left unread"
959    )]
960    fn fail_decode_contract_event_with_extra_bytes() {
961        let metadata = generate_metadata();
962        let transcoder = ContractMessageTranscoder::new(metadata);
963
964        let signature_topic: H256 =
965            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
966                .unwrap()
967                .into();
968        // raw encoded event with event index prefix
969        let encoded = ([0u32; 8], [1u32; 8], [12u8, 16u8]).encode();
970        // encode again as a Vec<u8> which has a len prefix.
971        let encoded_bytes = encoded.encode();
972        let _ = transcoder
973            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])
974            .unwrap();
975    }
976}