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    Field,
142    form::{
143        Form,
144        PortableForm,
145    },
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 '{name}'"
223                ))
224            }
225            (None, None) => {
226                let constructors = self.constructors().map(|c| c.label());
227                let messages = self.messages().map(|c| c.label());
228                let possible_values: Vec<_> = constructors.chain(messages).collect();
229                let help_txt = did_you_mean(name, possible_values.clone())
230                    .first()
231                    .map(|suggestion| format!("Did you mean '{suggestion}'?"))
232                    .unwrap_or_else(|| {
233                        format!("Should be one of: {}", possible_values.iter().join(", "))
234                    });
235
236                return Err(anyhow::anyhow!(
237                    "No constructor or message with the name '{name}' found.\n{help_txt}",
238                ))
239            }
240        };
241
242        let args: Vec<_> = args.into_iter().collect();
243        if spec_args.len() != args.len() {
244            anyhow::bail!(
245                "Invalid number of input arguments: expected {}, {} provided",
246                spec_args.len(),
247                args.len()
248            )
249        }
250
251        let mut encoded = selector.to_bytes().to_vec();
252        for (spec, arg) in spec_args.iter().zip(args) {
253            assert_not_shortened_hex(arg.as_ref());
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
438// Assert that `arg` is not in a shortened format a la `0xbc3f…f58a`.
439fn assert_not_shortened_hex(arg: &str) {
440    let re = Regex::new(r"^0x[a-fA-F0-9]+…[a-fA-F0-9]+$").unwrap();
441    if re.is_match(arg) {
442        panic!(
443            "Error: You are attempting to transcode a shortened hex value: `{arg:?}`.\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."
446        );
447    }
448}
449
450impl TryFrom<contract_metadata::ContractMetadata> for ContractMessageTranscoder {
451    type Error = anyhow::Error;
452
453    fn try_from(
454        metadata: contract_metadata::ContractMetadata,
455    ) -> Result<Self, Self::Error> {
456        Ok(Self::new(serde_json::from_value(
457            serde_json::Value::Object(metadata.abi),
458        )?))
459    }
460}
461
462#[derive(Debug)]
463pub enum CompositeTypeFields {
464    Named(Vec<CompositeTypeNamedField>),
465    Unnamed(Vec<Field<PortableForm>>),
466    NoFields,
467}
468
469#[derive(Debug)]
470pub struct CompositeTypeNamedField {
471    name: <PortableForm as Form>::String,
472    field: Field<PortableForm>,
473}
474
475impl CompositeTypeNamedField {
476    pub fn name(&self) -> &str {
477        &self.name
478    }
479
480    pub fn field(&self) -> &Field<PortableForm> {
481        &self.field
482    }
483}
484
485impl CompositeTypeFields {
486    pub fn from_fields(fields: &[Field<PortableForm>]) -> Result<Self> {
487        if fields.iter().next().is_none() {
488            Ok(Self::NoFields)
489        } else if fields.iter().all(|f| f.name.is_some()) {
490            let fields = fields
491                .iter()
492                .map(|field| {
493                    CompositeTypeNamedField {
494                        name: field
495                            .name
496                            .as_ref()
497                            .expect("All fields have a name; qed")
498                            .to_owned(),
499                        field: field.clone(),
500                    }
501                })
502                .collect();
503            Ok(Self::Named(fields))
504        } else if fields.iter().all(|f| f.name.is_none()) {
505            Ok(Self::Unnamed(fields.to_vec()))
506        } else {
507            Err(anyhow::anyhow!(
508                "Struct fields should either be all named or all unnamed"
509            ))
510        }
511    }
512}
513
514#[cfg(test)]
515mod tests {
516    use super::*;
517    use crate::scon::Hex;
518    use ink_env::{
519        DefaultEnvironment,
520        Environment,
521    };
522    use primitive_types::H256;
523    use scale::Encode;
524    use scon::Value;
525    use std::str::FromStr;
526
527    #[allow(clippy::extra_unused_lifetimes, unexpected_cfgs, non_local_definitions)]
528    #[ink::contract]
529    pub mod transcode {
530        #[ink(storage)]
531        pub struct Transcode {
532            value: bool,
533        }
534
535        #[ink(event)]
536        pub struct Event1 {
537            #[ink(topic)]
538            name: Hash,
539            #[ink(topic)]
540            from: AccountId,
541        }
542
543        impl Transcode {
544            #[ink(constructor)]
545            pub fn new(init_value: bool) -> Self {
546                Self { value: init_value }
547            }
548
549            #[ink(constructor)]
550            pub fn default() -> Self {
551                Self::new(Default::default())
552            }
553
554            #[ink(message)]
555            pub fn flip(&mut self) {
556                self.value = !self.value;
557            }
558
559            #[ink(message)]
560            pub fn get(&self) -> bool {
561                self.value
562            }
563
564            #[ink(message)]
565            pub fn get_complex(
566                &self,
567            ) -> (u32, ink::H160, ink::H256, ink::U256, AccountId) {
568                (
569                    32u32,
570                    self.env().address(),
571                    self.env().own_code_hash(),
572                    //self.env().transferred_value()
573                    ink::U256::one(),
574                    AccountId::from([0x17; 32]),
575                )
576            }
577            //pub fn get_complex(&mut self) -> (ink::H160, Hash, ink::H256, ink::U256) {
578            //(self.env().address(), [0xABu8; 32].into(),
579            /*
580            pub fn get_complex(&self) -> (ink::H160, ink::H256, ink::U256) {
581                (self.env().address(),
582                self.env().own_code_hash(), self.env().transferred_value())
583            }
584             */
585
586            #[ink(message)]
587            pub fn set_account_id(&self, account_id: AccountId) {
588                let _ = account_id;
589            }
590
591            #[ink(message)]
592            pub fn set_account_ids_vec(&self, account_ids: Vec<AccountId>) {
593                let _ = account_ids;
594            }
595
596            #[ink(message)]
597            pub fn primitive_vec_args(&self, args: Vec<u32>) {
598                let _ = args;
599            }
600
601            #[ink(message)]
602            pub fn uint_args(
603                &self,
604                _u8: u8,
605                _u16: u16,
606                _u32: u32,
607                _u64: u64,
608                _u128: u128,
609            ) {
610            }
611
612            #[ink(message)]
613            pub fn uint_array_args(&self, arr: [u8; 4]) {
614                let _ = arr;
615            }
616
617            #[ink(message)]
618            pub fn h160(&self, addr: ink::H160) {
619                let _ = addr;
620            }
621        }
622    }
623
624    fn generate_metadata() -> InkProject {
625        unsafe extern "Rust" {
626            fn __ink_generate_metadata() -> InkProject;
627        }
628
629        unsafe { __ink_generate_metadata() }
630    }
631
632    #[test]
633    fn encode_single_primitive_arg() -> Result<()> {
634        let metadata = generate_metadata();
635        let transcoder = ContractMessageTranscoder::new(metadata);
636
637        let encoded = transcoder.encode("new", ["true"])?;
638        // encoded args follow the 4 byte selector
639        let encoded_args = &encoded[4..];
640
641        assert_eq!(true.encode(), encoded_args);
642        Ok(())
643    }
644
645    #[test]
646    fn encode_misspelled_arg() {
647        let metadata = generate_metadata();
648        let transcoder = ContractMessageTranscoder::new(metadata);
649        assert_eq!(
650            transcoder.encode("fip", ["true"]).unwrap_err().to_string(),
651            "No constructor or message with the name 'fip' found.\nDid you mean 'flip'?"
652        );
653    }
654
655    #[test]
656    fn encode_mismatching_args_length() {
657        let metadata = generate_metadata();
658        let transcoder = ContractMessageTranscoder::new(metadata);
659
660        let result: Result<Vec<u8>> = transcoder.encode("new", Vec::<&str>::new());
661        assert!(result.is_err(), "Should return an error");
662        assert_eq!(
663            result.unwrap_err().to_string(),
664            "Invalid number of input arguments: expected 1, 0 provided"
665        );
666
667        let result: Result<Vec<u8>> = transcoder.encode("new", ["true", "false"]);
668        assert!(result.is_err(), "Should return an error");
669        assert_eq!(
670            result.unwrap_err().to_string(),
671            "Invalid number of input arguments: expected 1, 2 provided"
672        );
673    }
674
675    #[test]
676    fn encode_account_id_custom_ss58_encoding() -> Result<()> {
677        let metadata = generate_metadata();
678        let transcoder = ContractMessageTranscoder::new(metadata);
679
680        let encoded = transcoder.encode(
681            "set_account_id",
682            ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"],
683        )?;
684
685        // encoded args follow the 4 byte selector
686        let encoded_args = &encoded[4..];
687
688        let expected =
689            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
690                .unwrap();
691        assert_eq!(expected.encode(), encoded_args);
692        Ok(())
693    }
694
695    #[test]
696    fn encode_account_ids_vec_args() -> Result<()> {
697        let metadata = generate_metadata();
698        let transcoder = ContractMessageTranscoder::new(metadata);
699
700        let encoded = transcoder.encode(
701            "set_account_ids_vec",
702            ["[5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty]"],
703        )?;
704
705        // encoded args follow the 4 byte selector
706        let encoded_args = &encoded[4..];
707
708        let expected = vec![
709            AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")
710                .unwrap(),
711            AccountId32::from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")
712                .unwrap(),
713        ];
714        assert_eq!(expected.encode(), encoded_args);
715        Ok(())
716    }
717
718    #[test]
719    fn encode_primitive_vec_args() -> Result<()> {
720        let metadata = generate_metadata();
721        let transcoder = ContractMessageTranscoder::new(metadata);
722
723        let encoded = transcoder.encode("primitive_vec_args", ["[1, 2]"])?;
724
725        // encoded args follow the 4 byte selector
726        let encoded_args = &encoded[4..];
727
728        let expected = vec![1, 2];
729        assert_eq!(expected.encode(), encoded_args);
730        Ok(())
731    }
732
733    #[test]
734    fn encode_uint_hex_literals() -> Result<()> {
735        let metadata = generate_metadata();
736        let transcoder = ContractMessageTranscoder::new(metadata);
737
738        let encoded = transcoder.encode(
739            "uint_args",
740            [
741                "0x00",
742                "0xDEAD",
743                "0xDEADBEEF",
744                "0xDEADBEEF12345678",
745                "0xDEADBEEF0123456789ABCDEF01234567",
746            ],
747        )?;
748
749        // encoded args follow the 4 byte selector
750        let encoded_args = &encoded[4..];
751
752        let expected = (
753            0x00u8,
754            0xDEADu16,
755            0xDEADBEEFu32,
756            0xDEADBEEF12345678u64,
757            0xDEADBEEF0123456789ABCDEF01234567u128,
758        );
759        assert_eq!(expected.encode(), encoded_args);
760        Ok(())
761    }
762
763    #[test]
764    fn encode_uint_arr_hex_literals() -> Result<()> {
765        let metadata = generate_metadata();
766        let transcoder = ContractMessageTranscoder::new(metadata);
767
768        let encoded =
769            transcoder.encode("uint_array_args", ["[0xDE, 0xAD, 0xBE, 0xEF]"])?;
770
771        // encoded args follow the 4 byte selector
772        let encoded_args = &encoded[4..];
773
774        let expected: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
775        assert_eq!(expected.encode(), encoded_args);
776        Ok(())
777    }
778
779    #[test]
780    #[should_panic(
781        expected = "Error: You are attempting to transcode a shortened hex value: `\"0xbc3f…f58a\"`"
782    )]
783    fn encode_must_panic_on_shortened_hex() {
784        let metadata = generate_metadata();
785        let transcoder = ContractMessageTranscoder::new(metadata);
786
787        let _encoded = transcoder.encode("h160", ["0xbc3f…f58a"]);
788    }
789
790    #[test]
791    fn decode_complex_return() {
792        let metadata = generate_metadata();
793        let transcoder = ContractMessageTranscoder::new(metadata);
794
795        let addr: ink::H160 = ink::H160::from([0x42; 20]);
796        let account_id: AccountId32 = AccountId32::from([0x13; 32]);
797        let _hash: [u8; 32] = [0xAB; 32];
798        let h256: ink::H256 = ink::H256::from([0x17; 32]);
799        // todo let value: ink::U256 = ink::U256::MAX;
800        let value: ink::U256 = ink::U256::one();
801
802        let encoded = Result::<
803            (u32, ink::H160, ink::H256, ink::U256, AccountId32),
804            ink::primitives::LangError,
805        >::Ok((32, addr, h256, value, account_id))
806        .encode();
807
808        let decoded = transcoder
809            .decode_message_return("get_complex", &mut &encoded[..])
810            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
811
812        let expected = Value::Tuple(Tuple::new(
813            "Ok".into(),
814            [
815                Value::Tuple(Tuple::new(
816                    None,
817                    [
818                        Value::UInt(32),
819                        Value::Hex(Hex::from_str("0x4242424242424242424242424242424242424242").unwrap()),
820                        Value::Hex(Hex::from_str("0x1717171717171717171717171717171717171717171717171717171717171717").unwrap()),
821                        Value::Literal("1".to_string()),
822                        Value::Literal("5CViS5pKamF1VbJ9tmQKPNDpLpJaBCfpPw2m49UzQ8zgiDGT".to_string()),
823                    ]
824                    .into_iter().collect()
825                ))
826            ]
827            .into_iter().collect(),
828        ));
829        assert_eq!(expected, decoded);
830    }
831
832    #[test]
833    fn decode_primitive_return() {
834        let metadata = generate_metadata();
835        let transcoder = ContractMessageTranscoder::new(metadata);
836
837        let encoded = Result::<bool, ink::primitives::LangError>::Ok(true).encode();
838        let decoded = transcoder
839            .decode_message_return("get", &mut &encoded[..])
840            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
841
842        let expected = Value::Tuple(Tuple::new(
843            "Ok".into(),
844            [Value::Bool(true)].into_iter().collect(),
845        ));
846        assert_eq!(expected, decoded);
847    }
848
849    #[test]
850    fn decode_lang_error() {
851        use ink::primitives::LangError;
852
853        let metadata = generate_metadata();
854        let transcoder = ContractMessageTranscoder::new(metadata);
855
856        let encoded =
857            Result::<bool, LangError>::Err(LangError::CouldNotReadInput).encode();
858        let decoded = transcoder
859            .decode_message_return("get", &mut &encoded[..])
860            .unwrap_or_else(|e| panic!("Error decoding return value {e}"));
861
862        let expected = Value::Tuple(Tuple::new(
863            "Err".into(),
864            [Value::Tuple(Tuple::new(
865                Some("CouldNotReadInput"),
866                Vec::new(),
867            ))]
868            .to_vec(),
869        ));
870        assert_eq!(expected, decoded);
871    }
872
873    #[test]
874    fn decode_contract_event() -> Result<()> {
875        let metadata = generate_metadata();
876        let transcoder = ContractMessageTranscoder::new(metadata);
877
878        let signature_topic: <DefaultEnvironment as Environment>::Hash =
879            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
880                .unwrap()
881                .into();
882        // raw encoded event
883        let encoded = ([0u32; 8], [1u32; 8]).encode();
884        // encode again as a Vec<u8> which has a len prefix.
885        let encoded_bytes = encoded.encode();
886        let _ = transcoder
887            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
888
889        // todo assert is missing
890
891        Ok(())
892    }
893
894    #[test]
895    fn decode_hash_as_hex_encoded_string() -> Result<()> {
896        let metadata = generate_metadata();
897        let transcoder = ContractMessageTranscoder::new(metadata);
898
899        let hash = [
900            52u8, 40, 235, 225, 70, 245, 184, 36, 21, 218, 130, 114, 75, 207, 117, 240,
901            83, 118, 135, 56, 220, 172, 95, 131, 171, 125, 130, 167, 10, 15, 242, 222,
902        ];
903        let signature_topic: <DefaultEnvironment as Environment>::Hash =
904            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
905                .unwrap()
906                .into();
907        // raw encoded event with event index prefix
908        let encoded = (hash, [0u32; 8]).encode();
909        // encode again as a Vec<u8> which has a len prefix.
910        let encoded_bytes = encoded.encode();
911        let decoded = transcoder
912            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])?;
913
914        if let Value::Map(ref map) = decoded {
915            let name_field = &map[&Value::String("name".into())];
916            if let Value::Hex(hex) = name_field {
917                assert_eq!(
918                    &Hex::from_str(
919                        "0x3428ebe146f5b82415da82724bcf75f053768738dcac5f83ab7d82a70a0ff2de"
920                    )?,
921                    hex
922                );
923                Ok(())
924            } else {
925                Err(anyhow::anyhow!(
926                    "Expected a name field hash encoded as Hex value, was {name_field:?}"
927                ))
928            }
929        } else {
930            Err(anyhow::anyhow!(
931                "Expected a Value::Map for the decoded event"
932            ))
933        }
934    }
935
936    #[test]
937    fn decode_contract_message() -> Result<()> {
938        let metadata = generate_metadata();
939        let transcoder = ContractMessageTranscoder::new(metadata);
940
941        let encoded_bytes = hex::decode("633aa551").unwrap();
942        let _ = transcoder.decode_contract_message(&mut &encoded_bytes[..])?;
943
944        Ok(())
945    }
946
947    #[test]
948    #[should_panic(
949        expected = "input length was longer than expected by 1 byte(s).\nManaged to decode `flip` but `00` bytes were left unread"
950    )]
951    fn fail_decode_input_with_extra_bytes() {
952        let metadata = generate_metadata();
953        let transcoder = ContractMessageTranscoder::new(metadata);
954
955        let encoded_bytes = hex::decode("633aa55100").unwrap();
956        let _ = transcoder
957            .decode_contract_message(&mut &encoded_bytes[..])
958            .unwrap();
959    }
960
961    #[test]
962    #[should_panic(
963        expected = "input length was longer than expected by 2 byte(s).\nManaged to decode `Event1`, `name`, `from` but `0C10` bytes were left unread"
964    )]
965    fn fail_decode_contract_event_with_extra_bytes() {
966        let metadata = generate_metadata();
967        let transcoder = ContractMessageTranscoder::new(metadata);
968
969        let signature_topic: H256 =
970            <transcode::Event1 as ink::env::Event>::SIGNATURE_TOPIC
971                .unwrap()
972                .into();
973        // raw encoded event with event index prefix
974        let encoded = ([0u32; 8], [1u32; 8], [12u8, 16u8]).encode();
975        // encode again as a Vec<u8> which has a len prefix.
976        let encoded_bytes = encoded.encode();
977        let _ = transcoder
978            .decode_contract_event(&signature_topic, &mut &encoded_bytes[..])
979            .unwrap();
980    }
981}