ink_codegen/generator/
dispatch.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::iter;
16
17use crate::{
18    generator,
19    generator::solidity::{
20        solidity_call_type_ident,
21        solidity_selector,
22        solidity_selector_id,
23    },
24    GenerateCode,
25};
26use derive_more::From;
27use ir::{
28    Callable,
29    CallableWithSelector,
30    Constructor,
31    HexLiteral as _,
32    Message,
33};
34use proc_macro2::{
35    Ident,
36    TokenStream as TokenStream2,
37};
38use quote::{
39    format_ident,
40    quote,
41    quote_spanned,
42};
43use syn::{
44    spanned::Spanned as _,
45    LitInt,
46};
47
48/// A message to be dispatched.
49/// Contains its callable and calculated unique id
50pub struct MessageDispatchable<'a> {
51    message: CallableWithSelector<'a, Message>,
52    id: TokenStream2,
53}
54
55/// A constructor to be dispatched.
56/// Contains its callable and calculated unique id
57pub struct ConstructorDispatchable<'a> {
58    constructor: CallableWithSelector<'a, Constructor>,
59    id: LitInt,
60}
61
62/// Generates code for the message and constructor dispatcher.
63///
64/// This code efficiently selects the dispatched ink! constructor or message
65/// by inspecting the first four bytes (selector) of the given input bytes.
66///
67/// As this happens on every contract execution this code must be highly optimized.
68/// For that purpose a so-called dispatch enum is being generated that has a
69/// specialized `scale::Decode` implementation taking the first four bytes of
70/// the input stream in order to identify the enum variant that it is going to
71/// produce out of the rest of the input buffer.
72///
73/// The rest of the input buffer is then automatically decoded directly into the
74/// expected input types of the respective ink! constructor or message.
75#[derive(From)]
76pub struct Dispatch<'a> {
77    contract: &'a ir::Contract,
78}
79impl_as_ref_for_generator!(Dispatch);
80
81impl GenerateCode for Dispatch<'_> {
82    fn generate_code(&self) -> TokenStream2 {
83        let messages = self.compose_messages_with_ids();
84        let constructors = self.compose_constructors_with_ids();
85
86        let contract_dispatchable_constructor_infos =
87            self.generate_dispatchable_constructor_infos();
88        let contract_dispatchable_messages_infos =
89            self.generate_dispatchable_message_infos();
90        let constructor_decoder_type =
91            self.generate_constructor_decoder_type(&constructors);
92        let message_decoder_type = self.generate_message_decoder_type(&messages);
93        let entry_points = self.generate_entry_points(&constructors, &messages);
94
95        let solidity_call_info = self.generate_solidity_call_info();
96
97        quote! {
98            #contract_dispatchable_constructor_infos
99            #contract_dispatchable_messages_infos
100            #constructor_decoder_type
101            #message_decoder_type
102
103            #solidity_call_info
104
105            #[cfg(not(any(test, feature = "std", feature = "ink-as-dependency")))]
106            mod __do_not_access__ {
107                use super::*;
108                #entry_points
109            }
110        }
111    }
112}
113
114impl Dispatch<'_> {
115    /// Returns the index of the ink! message which has a wildcard selector, if existent.
116    fn query_wildcard_message(&self) -> Option<usize> {
117        self.contract
118            .module()
119            .impls()
120            .flat_map(|item_impl| item_impl.iter_messages())
121            .position(|item| item.has_wildcard_selector())
122    }
123
124    /// Returns the index of the ink! constructor which has a wildcard selector, if
125    /// existent.
126    fn query_wildcard_constructor(&self) -> Option<usize> {
127        self.contract
128            .module()
129            .impls()
130            .flat_map(|item_impl| item_impl.iter_constructors())
131            .position(|item| item.has_wildcard_selector())
132    }
133
134    /// Puts messages and their calculated selector ids in a single data structure
135    ///
136    /// See [`MessageDispatchable`]
137    fn compose_messages_with_ids(&self) -> Vec<MessageDispatchable> {
138        let storage_ident = self.contract.module().storage().ident();
139        self.contract
140            .module()
141            .impls()
142            .flat_map(|item_impl| {
143                iter::repeat(item_impl.trait_path()).zip(item_impl.iter_messages())
144            })
145            .flat_map(|(trait_path, message)| {
146                let mut message_dispatchables = Vec::new();
147
148                if self.contract.config().abi().is_ink() {
149                    let span = message.span();
150                    let id = if let Some(trait_path) = trait_path {
151                        let local_id = message.local_id().hex_padded_suffixed();
152                        quote_spanned!(span=>
153                            {
154                                ::core::primitive::u32::from_be_bytes(
155                                    <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
156                                        as #trait_path>::__ink_TraitInfo
157                                        as ::ink::reflect::TraitMessageInfo<#local_id>>::SELECTOR
158                                )
159                            }
160                        )
161                    } else {
162                        let id = message
163                            .composed_selector()
164                            .into_be_u32()
165                            .hex_padded_suffixed();
166                        quote_spanned!(span=>
167                            #id
168                        )
169                    };
170                    message_dispatchables.push(MessageDispatchable { message, id });
171                }
172
173                if self.contract.config().abi().is_solidity() {
174                    let id = solidity_selector_id(&message);
175                    message_dispatchables.push(MessageDispatchable { message, id });
176                }
177
178                message_dispatchables
179            })
180            .collect::<Vec<_>>()
181    }
182
183    /// Puts constructors and their calculated selector ids in a single data structure
184    ///
185    /// See [`ConstructorDispatchable`]
186    fn compose_constructors_with_ids(&self) -> Vec<ConstructorDispatchable> {
187        self.contract
188            .module()
189            .impls()
190            .flat_map(|item_impl| item_impl.iter_constructors())
191            .map(|constructor| {
192                let id = constructor
193                    .composed_selector()
194                    .into_be_u32()
195                    .hex_padded_suffixed();
196                ConstructorDispatchable { constructor, id }
197            })
198            .collect::<Vec<_>>()
199    }
200
201    /// Generate code for the [`ink::DispatchableConstructorInfo`] trait implementations.
202    ///
203    /// These trait implementations store relevant dispatch information for every
204    /// dispatchable ink! constructor of the ink! smart contract.
205    fn generate_dispatchable_constructor_infos(&self) -> TokenStream2 {
206        let span = self.contract.module().storage().span();
207        let storage_ident = self.contract.module().storage().ident();
208        let constructor_infos = self
209            .contract
210            .module()
211            .impls()
212            .flat_map(|item_impl| item_impl.iter_constructors())
213            .map(|constructor| {
214                let constructor_span = constructor.span();
215                let constructor_ident = constructor.ident();
216                let payable = constructor.is_payable();
217                let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed();
218                let selector_bytes = constructor.composed_selector().hex_lits();
219                let cfg_attrs = constructor.get_cfg_attrs(constructor_span);
220                let output_type = constructor.output().map(quote::ToTokens::to_token_stream)
221                    .unwrap_or_else(|| quote! { () });
222                let input_bindings = generator::input_bindings(constructor.inputs());
223                let input_tuple_type = generator::input_types_tuple(constructor.inputs());
224                let input_tuple_bindings = generator::input_bindings_tuple(constructor.inputs());
225                let constructor_return_type = quote_spanned!(constructor_span=>
226                    <::ink::reflect::ConstructorOutputValue<#output_type>
227                        as ::ink::reflect::ConstructorOutput<#storage_ident>>
228                );
229                quote_spanned!(constructor_span =>
230                    #( #cfg_attrs )*
231                    impl ::ink::reflect::DispatchableConstructorInfo<#selector_id> for #storage_ident {
232                        type Input = #input_tuple_type;
233                        type Output = #output_type;
234                        type Storage = #storage_ident;
235                        type Error = #constructor_return_type::Error;
236                        const IS_RESULT: ::core::primitive::bool = #constructor_return_type::IS_RESULT;
237
238                        const CALLABLE: fn(Self::Input) -> Self::Output = |#input_tuple_bindings| {
239                            #storage_ident::#constructor_ident(#( #input_bindings ),* )
240                        };
241                        const PAYABLE: ::core::primitive::bool = #payable;
242                        const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
243                        const LABEL: &'static ::core::primitive::str = ::core::stringify!(#constructor_ident);
244                    }
245                )
246            });
247        quote_spanned!(span=>
248            #( #constructor_infos )*
249        )
250    }
251
252    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
253    ///
254    /// These trait implementations store relevant dispatch information for every
255    /// dispatchable ink! message of the ink! smart contract.
256    fn generate_dispatchable_message_infos(&self) -> TokenStream2 {
257        let span = self.contract.module().storage().span();
258        let abi = self.contract.config().abi();
259
260        let inherent_message_infos = self
261            .contract
262            .module()
263            .impls()
264            .filter(|item_impl| item_impl.trait_path().is_none())
265            .flat_map(|item_impl| item_impl.iter_messages())
266            .flat_map(|message| {
267                let mut message_infos = Vec::new();
268                if abi.is_ink() {
269                    message_infos
270                        .push(self.dispatchable_inherent_message_infos(&message));
271                }
272                if abi.is_solidity() {
273                    message_infos.push(self.solidity_message_info(&message, None));
274                }
275                message_infos
276            });
277        let trait_message_infos = self
278            .contract
279            .module()
280            .impls()
281            .filter_map(|item_impl| {
282                item_impl
283                    .trait_path()
284                    .map(|trait_path| {
285                        let trait_ident = item_impl.trait_ident().expect(
286                            "must have an ink! trait identifier if it is an ink! trait implementation"
287                        );
288                        iter::repeat((trait_ident, trait_path)).zip(item_impl.iter_messages())
289                    })
290            })
291            .flatten()
292            .flat_map(|((trait_ident, trait_path), message)| {
293                let mut message_infos = Vec::new();
294                if abi.is_ink() {
295                   message_infos.push(self.dispatchable_trait_message_infos(&message, trait_ident, trait_path));
296                }
297                if abi.is_solidity() {
298                    // NOTE: Solidity selectors are always computed from the call signature and input types.
299                    message_infos.push(self.solidity_message_info(&message, Some((trait_ident, trait_path))));
300                }
301                message_infos
302            });
303        quote_spanned!(span=>
304            #( #inherent_message_infos )*
305            #( #trait_message_infos )*
306        )
307    }
308
309    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
310    ///
311    /// These trait implementations store relevant dispatch information for every
312    /// inherent dispatchable ink! message of the ink! smart contract.
313    fn dispatchable_inherent_message_infos(
314        &self,
315        message: &CallableWithSelector<Message>,
316    ) -> TokenStream2 {
317        let storage_ident = self.contract.module().storage().ident();
318
319        let message_span = message.span();
320        let message_ident = message.ident();
321        let payable = message.is_payable();
322        let mutates = message.receiver().is_ref_mut();
323
324        let cfg_attrs = message.get_cfg_attrs(message_span);
325        let output_tuple_type = message
326            .output()
327            .map(quote::ToTokens::to_token_stream)
328            .unwrap_or_else(|| quote! { () });
329        let input_bindings = generator::input_bindings(message.inputs());
330        let input_tuple_type = generator::input_types_tuple(message.inputs());
331        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
332
333        let selector_id = message
334            .composed_selector()
335            .into_be_u32()
336            .hex_padded_suffixed();
337        let selector_bytes = message.composed_selector().hex_lits();
338
339        #[cfg(feature = "std")]
340        let return_type = quote! { () };
341        #[cfg(not(feature = "std"))]
342        let return_type = quote! { ! };
343
344        quote_spanned!(message_span=>
345            #( #cfg_attrs )*
346            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
347                type Input = #input_tuple_type;
348                type Output = #output_tuple_type;
349                type Storage = #storage_ident;
350
351                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
352                    |storage, #input_tuple_bindings| {
353                        #storage_ident::#message_ident( storage #( , #input_bindings )* )
354                    };
355                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
356                    |input| {
357                        <Self::Input as ::ink::scale::Decode>::decode(input)
358                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)
359                    };
360                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
361                    |flags, output| {
362                        ::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
363                            flags,
364                            // Currently no `LangError`s are raised at this level of the
365                            // dispatch logic so `Ok` is always returned to the caller.
366                            &::ink::MessageResult::Ok(output),
367                        )
368                    };
369                const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
370                const PAYABLE: ::core::primitive::bool = #payable;
371                const MUTATES: ::core::primitive::bool = #mutates;
372                const LABEL: &'static ::core::primitive::str = ::core::stringify!(#message_ident);
373                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
374            }
375        )
376    }
377
378    /// Generate code for the [`ink::DispatchableMessageInfo`] trait implementations.
379    ///
380    /// These trait implementations store relevant dispatch information for every
381    /// trait implementation dispatchable ink! message of the ink! smart contract.
382    fn dispatchable_trait_message_infos(
383        &self,
384        message: &CallableWithSelector<Message>,
385        trait_ident: &Ident,
386        trait_path: &syn::Path,
387    ) -> TokenStream2 {
388        let storage_ident = self.contract.module().storage().ident();
389
390        let message_span = message.span();
391        let message_ident = message.ident();
392        let mutates = message.receiver().is_ref_mut();
393        let local_id = message.local_id().hex_padded_suffixed();
394        let payable = quote! {{
395            <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
396                as #trait_path>::__ink_TraitInfo
397                as ::ink::reflect::TraitMessageInfo<#local_id>>::PAYABLE
398        }};
399        let selector = quote! {{
400            <<::ink::reflect::TraitDefinitionRegistry<<#storage_ident as ::ink::env::ContractEnv>::Env>
401                as #trait_path>::__ink_TraitInfo
402                as ::ink::reflect::TraitMessageInfo<#local_id>>::SELECTOR
403        }};
404        let selector_id = quote! {{
405            ::core::primitive::u32::from_be_bytes(#selector)
406        }};
407        let output_tuple_type = message
408            .output()
409            .map(quote::ToTokens::to_token_stream)
410            .unwrap_or_else(|| quote! { () });
411        let input_bindings = generator::input_bindings(message.inputs());
412        let input_tuple_type = generator::input_types_tuple(message.inputs());
413        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
414        let label = format!("{trait_ident}::{message_ident}");
415        let cfg_attrs = message.get_cfg_attrs(message_span);
416
417        #[cfg(feature = "std")]
418        let return_type = quote! { () };
419        #[cfg(not(feature = "std"))]
420        let return_type = quote! { ! };
421
422        quote_spanned!(message_span=>
423            #( #cfg_attrs )*
424            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
425                type Input = #input_tuple_type;
426                type Output = #output_tuple_type;
427                type Storage = #storage_ident;
428
429                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
430                    |storage, #input_tuple_bindings| {
431                        <#storage_ident as #trait_path>::#message_ident( storage #( , #input_bindings )* )
432                    };
433                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
434                    |input| {
435                        <Self::Input as ::ink::scale::Decode>::decode(input)
436                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)
437                    };
438                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
439                    |flags, output| {
440                        ::ink::env::return_value::<::ink::MessageResult::<Self::Output>>(
441                            flags,
442                            // Currently no `LangError`s are raised at this level of the
443                            // dispatch logic so `Ok` is always returned to the caller.
444                            &::ink::MessageResult::Ok(output),
445                        )
446                    };
447                const SELECTOR: [::core::primitive::u8; 4usize] = #selector;
448                const PAYABLE: ::core::primitive::bool = #payable;
449                const MUTATES: ::core::primitive::bool = #mutates;
450                const LABEL: &'static ::core::primitive::str = #label;
451                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Scale;
452            }
453        )
454    }
455
456    /// Generate code for [`ink::DispatchableMessageInfo`] trait implementations for
457    /// Solidity ABI compatible calls.
458    fn solidity_message_info(
459        &self,
460        message: &CallableWithSelector<Message>,
461        trait_info: Option<(&Ident, &syn::Path)>,
462    ) -> TokenStream2 {
463        let storage_ident = self.contract.module().storage().ident();
464        let message_span = message.span();
465        let message_ident = message.ident();
466        let payable = message.is_payable();
467        let mutates = message.receiver().is_ref_mut();
468
469        let cfg_attrs = message.get_cfg_attrs(message_span);
470        let output_tuple_type = message
471            .output()
472            .map(quote::ToTokens::to_token_stream)
473            .unwrap_or_else(|| quote! { () });
474        let input_bindings = generator::input_bindings(message.inputs());
475        let input_tuple_type = generator::input_types_tuple(message.inputs());
476        let input_tuple_bindings = generator::input_bindings_tuple(message.inputs());
477
478        let selector_id = solidity_selector_id(message);
479        let selector_bytes = solidity_selector(message);
480
481        let (call_prefix, label) = match trait_info {
482            None => {
483                (
484                    quote! {
485                        #storage_ident
486                    },
487                    message_ident.to_string(),
488                )
489            }
490            Some((trait_ident, trait_path)) => {
491                (
492                    quote! {
493                        <#storage_ident as #trait_path>
494                    },
495                    format!("{trait_ident}::{message_ident}"),
496                )
497            }
498        };
499
500        #[cfg(feature = "std")]
501        let return_type = quote! { () };
502        #[cfg(not(feature = "std"))]
503        let return_type = quote! { ! };
504
505        quote_spanned!(message_span=>
506            #( #cfg_attrs )*
507            impl ::ink::reflect::DispatchableMessageInfo<#selector_id> for #storage_ident {
508                type Input = #input_tuple_type;
509                type Output = #output_tuple_type;
510                type Storage = #storage_ident;
511
512                const CALLABLE: fn(&mut Self::Storage, Self::Input) -> Self::Output =
513                    |storage, #input_tuple_bindings| {
514                        #call_prefix::#message_ident( storage #( , #input_bindings )* )
515                    };
516                const DECODE: fn(&mut &[::core::primitive::u8]) -> ::core::result::Result<Self::Input, ::ink::env::DispatchError> =
517                    |input| {
518                        <Self::Input as ::ink::SolDecode>::decode(input)
519                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)
520                    };
521                const RETURN: fn(::ink::env::ReturnFlags, Self::Output) -> #return_type =
522                    |flags, output| {
523                        ::ink::env::return_value_solidity::<Self::Output>(
524                            flags,
525                            &output,
526                        )
527                    };
528                const SELECTOR: [::core::primitive::u8; 4usize] = #selector_bytes;
529                const PAYABLE: ::core::primitive::bool = #payable;
530                const MUTATES: ::core::primitive::bool = #mutates;
531                const LABEL: &'static ::core::primitive::str = #label;
532                const ENCODING: ::ink::reflect::Encoding = ::ink::reflect::Encoding::Solidity;
533            }
534        )
535    }
536
537    /// Generates code for the entry points of the root ink! smart contract.
538    ///
539    /// This generates the `deploy` and `call` functions with which the smart
540    /// contract runtime mainly interacts with the ink! smart contract.
541    fn generate_entry_points(
542        &self,
543        constructors: &[ConstructorDispatchable],
544        messages: &[MessageDispatchable],
545    ) -> TokenStream2 {
546        let span = self.contract.module().storage().span();
547        let storage_ident = self.contract.module().storage().ident();
548        let any_constructor_accept_payment =
549            self.any_constructor_accepts_payment(constructors);
550        let any_message_accepts_payment = self.any_message_accepts_payment(messages);
551
552        let fn_call: syn::ItemFn = syn::parse_quote! {
553            #[cfg(target_arch = "riscv64")]
554            #[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
555            pub extern "C" fn call() {
556                internal_call()
557            }
558        };
559        let fn_deploy: syn::ItemFn = syn::parse_quote! {
560            #[cfg(target_arch = "riscv64")]
561            #[::ink::polkavm_export(abi = ::ink::polkavm_derive::default_abi)]
562            pub extern "C" fn deploy() {
563                internal_deploy()
564            }
565        };
566        quote_spanned!(span=>
567            #[allow(dead_code)] // clippy throws a false positive otherwise
568            #[allow(clippy::nonminimal_bool)]
569            #[cfg(target_arch = "riscv64")]
570            fn internal_deploy() {
571                if !#any_constructor_accept_payment {
572                    ::ink::codegen::deny_payment()
573                        .unwrap_or_else(|error| ::core::panic!("{}", error))
574                }
575
576                let dispatchable = match ::ink::env::decode_input::<
577                    <#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type,
578                >() {
579                    ::core::result::Result::Ok(decoded_dispatchable) => {
580                        decoded_dispatchable
581                    }
582                    ::core::result::Result::Err(_decoding_error) => {
583                        let error = ::ink::ConstructorResult::Err(::ink::LangError::CouldNotReadInput);
584
585                        // At this point we're unable to set the `Ok` variant to be the "real"
586                        // constructor output since we were unable to figure out what the caller wanted
587                        // to dispatch in the first place, so we set it to `()`.
588                        //
589                        // This is okay since we're going to only be encoding the `Err` variant
590                        // into the output buffer anyway.
591                        ::ink::env::return_value::<::ink::ConstructorResult<()>>(
592                            ::ink::env::ReturnFlags::REVERT,
593                            &error,
594                        );
595                    }
596                };
597
598                <<#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type
599                    as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
600                .unwrap_or_else(|error| {
601                    ::core::panic!("dispatching ink! constructor failed: {}", error)
602                })
603            }
604
605            #[allow(dead_code)] // clippy throws a false positive otherwise
606            #[allow(clippy::nonminimal_bool)]
607            #[cfg(target_arch = "riscv64")]
608            fn internal_call() {
609                if !#any_message_accepts_payment {
610                    ::ink::codegen::deny_payment()
611                        .unwrap_or_else(|error| ::core::panic!("{}", error))
612                }
613
614                let dispatchable = match ::ink::env::decode_input::<
615                    <#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type,
616                >() {
617                    ::core::result::Result::Ok(decoded_dispatchable) => {
618                        decoded_dispatchable
619                    }
620                    ::core::result::Result::Err(_decoding_error) => {
621                        let error = ::ink::MessageResult::Err(::ink::LangError::CouldNotReadInput);
622
623                        // At this point we're unable to set the `Ok` variant to be the "real"
624                        // message output since we were unable to figure out what the caller wanted
625                        // to dispatch in the first place, so we set it to `()`.
626                        //
627                        // This is okay since we're going to only be encoding the `Err` variant
628                        // into the output buffer anyway.
629                        ::ink::env::return_value::<::ink::MessageResult<()>>(
630                            ::ink::env::ReturnFlags::REVERT,
631                            &error,
632                        );
633                    }
634                };
635
636                <<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type
637                    as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
638                .unwrap_or_else(|error| {
639                    ::core::panic!("dispatching ink! message failed: {}", error)
640                })
641            }
642
643            #fn_call
644
645            #fn_deploy
646        )
647    }
648
649    /// Generates code for the ink! constructor decoder type of the ink! smart contract.
650    ///
651    /// This type can be used in order to decode the input bytes received by a call to
652    /// `deploy` into one of the available dispatchable ink! constructors and their
653    /// arguments.
654    fn generate_constructor_decoder_type(
655        &self,
656        constructors: &[ConstructorDispatchable],
657    ) -> TokenStream2 {
658        /// Expands into the token sequence to represent the
659        /// input type of the ink! constructor at the given index.
660        fn expand_constructor_input(
661            span: proc_macro2::Span,
662            storage_ident: &syn::Ident,
663            constructor_id: LitInt,
664        ) -> TokenStream2 {
665            quote_spanned!(span=>
666                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #constructor_id >>::Input
667            )
668        }
669
670        /// Returns the n-th ink! constructor identifier for the decoder type.
671        fn constructor_variant_ident(n: usize) -> syn::Ident {
672            quote::format_ident!("Constructor{}", n)
673        }
674
675        let span = self.contract.module().storage().span();
676        let storage_ident = self.contract.module().storage().ident();
677        let constructors_variants =
678            constructors.iter().enumerate().map(|(index, item)| {
679                let constructor_span = item.constructor.span();
680                let constructor_ident = constructor_variant_ident(index);
681                let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
682                let constructor_input = expand_constructor_input(
683                    constructor_span,
684                    storage_ident,
685                    item.id.clone(),
686                );
687                quote_spanned!(constructor_span=>
688                    #( #cfg_attrs )*
689                    #constructor_ident(#constructor_input)
690                )
691            });
692
693        let constructor_selector=
694            constructors.iter().enumerate().map(|(index, item)| {
695            let constructor_span = item.constructor.span();
696            let const_ident = format_ident!("CONSTRUCTOR_{}", index);
697            let id = item.id.clone();
698            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
699            quote_spanned!(span=>
700                #( #cfg_attrs )*
701                const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::SELECTOR;
702            )
703        });
704
705        let constructor_match = constructors.iter().enumerate().map(|(index, item)| {
706            let constructor_span = item.constructor.span();
707            let constructor_ident = constructor_variant_ident(index);
708            let const_ident = format_ident!("CONSTRUCTOR_{}", index);
709            let constructor_input = expand_constructor_input(
710                constructor_span,
711                storage_ident,
712                item.id.clone(),
713            );
714            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
715            quote_spanned!(constructor_span=>
716                #( #cfg_attrs )*
717                #const_ident => {
718                    ::core::result::Result::Ok(Self::#constructor_ident(
719                        <#constructor_input as ::ink::scale::Decode>::decode(input)
720                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
721                    ))
722                }
723            )
724        });
725        let possibly_wildcard_selector_constructor = match self
726            .query_wildcard_constructor()
727        {
728            Some(wildcard_index) => {
729                let item = &constructors[wildcard_index];
730                let constructor_span = item.constructor.span();
731                let constructor_ident = constructor_variant_ident(wildcard_index);
732                let constructor_input = expand_constructor_input(
733                    constructor_span,
734                    storage_ident,
735                    item.id.clone(),
736                );
737                quote! {
738                    ::core::result::Result::Ok(Self::#constructor_ident(
739                        <#constructor_input as ::ink::scale::Decode>::decode(input)
740                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
741                    ))
742                }
743            }
744            None => {
745                quote! {
746                    ::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
747                }
748            }
749        };
750
751        let constructor_execute = constructors.iter().enumerate().map(|(index, item)| {
752            let constructor_span = item.constructor.span();
753            let constructor_ident = constructor_variant_ident(index);
754            let id = item.id.clone();
755            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
756            let constructor_callable = quote_spanned!(constructor_span=>
757                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::CALLABLE
758            );
759            let constructor_output = quote_spanned!(constructor_span=>
760                <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::Output
761            );
762            let deny_payment = quote_spanned!(constructor_span=>
763                !<#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE
764            );
765            let constructor_value = quote_spanned!(constructor_span=>
766                <::ink::reflect::ConstructorOutputValue<#constructor_output>
767                    as ::ink::reflect::ConstructorOutput::<#storage_ident>>
768            );
769
770            let constructor_accept_payment_assignment =
771                self.any_constructor_accepts_payment(constructors);
772
773            quote_spanned!(constructor_span=>
774                #( #cfg_attrs )*
775                Self::#constructor_ident(input) => {
776                    if #constructor_accept_payment_assignment && #deny_payment {
777                        ::ink::codegen::deny_payment()?;
778                    }
779
780                    let result: #constructor_output = #constructor_callable(input);
781                    let output_value = ::ink::reflect::ConstructorOutputValue::new(result);
782                    let output_result = #constructor_value::as_result(&output_value);
783
784                    if let ::core::result::Result::Ok(contract) = output_result.as_ref() {
785                        ::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
786                            &<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
787                            contract,
788                        );
789                    }
790
791                    // NOTE: we can't use an if/else expression here
792                    // It fails inside quote_spanned! macro.
793                    // See https://github.com/rust-lang/rust-clippy/issues/6249
794                    let mut flag = ::ink::env::ReturnFlags::empty();
795                    if output_result.is_err() {
796                        flag = ::ink::env::ReturnFlags::REVERT;
797                    }
798
799                    ::ink::env::return_value::<
800                        ::ink::ConstructorResult<
801                            ::core::result::Result<(), &#constructor_value::Error>
802                        >,
803                    >(
804                        flag,
805                        // Currently no `LangError`s are raised at this level of the
806                        // dispatch logic so `Ok` is always returned to the caller.
807                        &::ink::ConstructorResult::Ok(output_result.map(|_| ())),
808                    );
809
810                    #[cfg(feature = "std")]
811                    return ::core::result::Result::Ok(());
812
813                    #[cfg(not(feature = "std"))]
814                    #[cfg_attr(not(feature = "std"), allow(unreachable_code))]
815                    {
816                        ::core::unreachable!("either `return_value` or the `return` before will already have returned");
817                    }
818                }
819            )
820        });
821        quote_spanned!(span=>
822            const _: () = {
823                #[allow(non_camel_case_types)]
824                pub enum __ink_ConstructorDecoder {
825                    #( #constructors_variants ),*
826                }
827
828                impl ::ink::env::DecodeDispatch for __ink_ConstructorDecoder {
829                    fn decode_dispatch(input: &mut &[::core::primitive::u8])
830                        -> ::core::result::Result<Self, ::ink::env::DispatchError> {
831                        #(
832                            #constructor_selector
833                        )*
834                        match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
835                            .map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
836                        {
837                            #( #constructor_match , )*
838                            _invalid => #possibly_wildcard_selector_constructor
839                        }
840                    }
841                }
842
843                impl ::ink::reflect::ExecuteDispatchable for __ink_ConstructorDecoder {
844                    #[allow(clippy::nonminimal_bool, dead_code)]
845                    fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink::env::DispatchError> {
846                        match self {
847                            #( #constructor_execute ),*
848                        }
849                    }
850                }
851
852                impl ::ink::reflect::ContractConstructorDecoder for #storage_ident {
853                    type Type = __ink_ConstructorDecoder;
854                }
855            };
856        )
857    }
858
859    /// Generates code for the ink! message decoder type of the ink! smart contract.
860    ///
861    /// This type can be used in order to decode the input bytes received by a call to
862    /// `call` into one of the available dispatchable ink! messages and their
863    /// arguments.
864    fn generate_message_decoder_type(
865        &self,
866        messages: &[MessageDispatchable],
867    ) -> TokenStream2 {
868        /// Expands into the token sequence to represent the
869        /// input type of the ink! message at the given index.
870        fn expand_message_input(
871            span: proc_macro2::Span,
872            storage_ident: &syn::Ident,
873            message_id: TokenStream2,
874        ) -> TokenStream2 {
875            quote_spanned!(span=>
876                <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #message_id >>::Input
877            )
878        }
879
880        /// Returns the n-th ink! message identifier for the decoder type.
881        fn message_variant_ident(n: usize) -> syn::Ident {
882            quote::format_ident!("Message{}", n)
883        }
884
885        let span = self.contract.module().storage().span();
886        let storage_ident = self.contract.module().storage().ident();
887        let message_variants = messages.iter().enumerate().map(|(index, item)| {
888            let message_span = item.message.span();
889            let message_ident = message_variant_ident(index);
890            let cfg_attrs = item.message.get_cfg_attrs(message_span);
891            let message_input =
892                expand_message_input(message_span, storage_ident, item.id.clone());
893            quote_spanned!(message_span=>
894                #( #cfg_attrs )*
895                #message_ident(#message_input)
896            )
897        });
898
899        let message_selector = messages
900            .iter()
901            .enumerate()
902            .map(|(index, item)|  {
903                let message_span = item.message.span();
904                let const_ident = format_ident!("MESSAGE_{}", index);
905                let cfg_attrs = item.message.get_cfg_attrs(message_span);
906                let id = item.id.clone();
907                quote_spanned!(span=>
908                    #( #cfg_attrs )*
909                    const #const_ident: [::core::primitive::u8; 4usize] = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::SELECTOR;
910                )
911        });
912
913        let message_match =  messages
914            .iter()
915            .enumerate()
916            .map(|(index, item)| {
917                let message_ident = message_variant_ident(index);
918                let const_ident = format_ident!("MESSAGE_{}", index);
919                let message_span = item.message.span();
920                let cfg_attrs = item.message.get_cfg_attrs(message_span);
921                let id = item.id.clone();
922                quote_spanned!(message_span=>
923                   #( #cfg_attrs )*
924                    #const_ident => {
925                        ::core::result::Result::Ok(Self::#message_ident(
926                            <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::DECODE(input)?
927                        ))
928                    }
929                )
930        });
931        let possibly_wildcard_selector_message = match self.query_wildcard_message() {
932            Some(wildcard_index) => {
933                let item = messages
934                    .get(wildcard_index)
935                    .expect("unable to get wildcard index");
936                let message_span = item.message.span();
937                let message_ident = message_variant_ident(wildcard_index);
938                let message_input =
939                    expand_message_input(message_span, storage_ident, item.id.clone());
940                quote! {
941                    ::core::result::Result::Ok(Self::#message_ident(
942                        <#message_input as ::ink::scale::Decode>::decode(input)
943                            .map_err(|_| ::ink::env::DispatchError::InvalidParameters)?
944                    ))
945                }
946            }
947            None => {
948                quote! {
949                    ::core::result::Result::Err(::ink::env::DispatchError::UnknownSelector)
950                }
951            }
952        };
953
954        let message_execute = messages
955            .iter()
956            .enumerate()
957            .map(|(index, item)| {
958                let message_span = item.message.span();
959                let message_ident = message_variant_ident(index);
960                let id = item.id.clone();
961                let cfg_attrs = item.message.get_cfg_attrs(message_span);
962                let message_callable = quote_spanned!(message_span=>
963                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::CALLABLE
964                );
965                let message_return = quote_spanned!(message_span=>
966                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::RETURN
967                );
968                let message_output = quote_spanned!(message_span=>
969                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::Output
970                );
971                let deny_payment = quote_spanned!(message_span=>
972                    !<#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE
973                );
974                let mutates_storage = quote_spanned!(message_span=>
975                    <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::MUTATES
976                );
977
978                let any_message_accepts_payment =
979                    self.any_message_accepts_payment(messages);
980
981                quote_spanned!(message_span=>
982                    #( #cfg_attrs )*
983                    Self::#message_ident(input) => {
984                        if #any_message_accepts_payment && #deny_payment {
985                            ::ink::codegen::deny_payment()?;
986                        }
987
988                        let result: #message_output = #message_callable(&mut contract, input);
989                        let is_reverted = ::ink::is_result_type!(#message_output)
990                            && ::ink::is_result_err!(result);
991
992                        // NOTE: we can't use an if/else expression here
993                        // It fails inside quote_spanned! macro.
994                        // See https://github.com/rust-lang/rust-clippy/issues/6249
995                        let mut flag = ::ink::env::ReturnFlags::REVERT;
996
997                        // no need to push back results: transaction gets reverted anyway
998                        if !is_reverted {
999                            flag = ::ink::env::ReturnFlags::empty();
1000                            push_contract(contract, #mutates_storage);
1001                        }
1002
1003                        #message_return(flag, result);
1004
1005                        #[cfg(feature = "std")]
1006                        return ::core::result::Result::Ok(());
1007
1008                        #[cfg(not(feature = "std"))]
1009                        #[cfg_attr(not(feature = "std"), allow(unreachable_code))]
1010                        {
1011                            ::core::unreachable!("either `return_value` or the `return` before will already have returned");
1012                        }
1013                    }
1014                )
1015        });
1016
1017        quote_spanned!(span=>
1018            const _: () = {
1019                #[allow(non_camel_case_types)]
1020                pub enum __ink_MessageDecoder {
1021                    #( #message_variants ),*
1022                }
1023
1024                impl ::ink::env::DecodeDispatch for __ink_MessageDecoder {
1025                    fn decode_dispatch(input: &mut &[::core::primitive::u8])
1026                        -> ::core::result::Result<Self, ::ink::env::DispatchError> {
1027                        #(
1028                            #message_selector
1029                        )*
1030                        match <[::core::primitive::u8; 4usize] as ::ink::scale::Decode>::decode(input)
1031                            .map_err(|_| ::ink::env::DispatchError::InvalidSelector)?
1032                        {
1033                            #( #message_match , )*
1034                            _invalid => #possibly_wildcard_selector_message
1035                        }
1036                    }
1037                }
1038
1039                fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) {
1040                    if mutates {
1041                        ::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
1042                            &<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
1043                            &contract,
1044                        );
1045                    }
1046                }
1047
1048                impl ::ink::reflect::ExecuteDispatchable for __ink_MessageDecoder {
1049                    #[allow(clippy::nonminimal_bool, clippy::let_unit_value, dead_code)]
1050                    fn execute_dispatchable(
1051                        self
1052                    ) -> ::core::result::Result<(), ::ink::env::DispatchError> {
1053                        let key = <#storage_ident as ::ink::storage::traits::StorageKey>::KEY;
1054                        let mut contract: ::core::mem::ManuallyDrop<#storage_ident> =
1055                            ::core::mem::ManuallyDrop::new(
1056                                match ::ink::env::get_contract_storage(&key) {
1057                                    ::core::result::Result::Ok(::core::option::Option::Some(value)) => value,
1058                                    ::core::result::Result::Ok(::core::option::Option::None) => {
1059                                        ::core::panic!("storage entry was empty")
1060                                    },
1061                                    ::core::result::Result::Err(_) => {
1062                                        ::core::panic!("could not properly decode storage entry")
1063                                    },
1064                                }
1065                            );
1066
1067                        match self {
1068                            #( #message_execute ),*
1069                        }
1070                    }
1071                }
1072
1073                impl ::ink::reflect::ContractMessageDecoder for #storage_ident {
1074                    type Type = __ink_MessageDecoder;
1075                }
1076            };
1077        )
1078    }
1079
1080    /// Generates code to express if any dispatchable ink! message accepts payment.
1081    ///
1082    /// Generates code in the form of variable assignments
1083    /// which can be conditionally omitted
1084    /// in which case the default assignment `let message_{id} = false` exists.
1085    ///
1086    /// This information can be used to speed-up dispatch since denying of payment
1087    /// can be generalized to work before dispatch happens if none of the ink! messages
1088    /// accept payment anyways.
1089    fn any_message_accepts_payment(
1090        &self,
1091        messages: &[MessageDispatchable],
1092    ) -> TokenStream2 {
1093        let span = self.contract.module().storage().span();
1094        let storage_ident = self.contract.module().storage().ident();
1095        let message_is_payable =  messages
1096            .iter()
1097            .enumerate()
1098            .map(|(index, item)| {
1099            let message_span = item.message.span();
1100            let cfg_attrs = item.message.get_cfg_attrs(message_span);
1101            let id = item.id.clone();
1102            let ident = quote::format_ident!("message_{}", index);
1103            quote_spanned!(message_span=>
1104                {
1105                    let #ident = false;
1106                    #( #cfg_attrs )*
1107                    let #ident = <#storage_ident as ::ink::reflect::DispatchableMessageInfo< #id >>::PAYABLE;
1108                    #ident
1109                }
1110            )
1111        });
1112        quote_spanned!(span=>
1113            {
1114                false #( || #message_is_payable )*
1115            }
1116        )
1117    }
1118
1119    /// Generates code to express if any dispatchable ink! constructor accepts payment.
1120    ///
1121    /// Generates code in the form of variable assignments
1122    /// which can be conditionally omitted
1123    /// in which case the default assignment `let constructor_{id} = false` exists.
1124    ///
1125    /// This information can be used to speed-up dispatch since denying of payment
1126    /// can be generalized to work before dispatch happens if none of the ink!
1127    /// constructors accept payment anyways.
1128    fn any_constructor_accepts_payment(
1129        &self,
1130        constructors: &[ConstructorDispatchable],
1131    ) -> TokenStream2 {
1132        let span = self.contract.module().storage().span();
1133        let storage_ident = self.contract.module().storage().ident();
1134        let constructor_is_payable = constructors.iter().enumerate().map(|(index, item)| {
1135            let constructor_span = item.constructor.span();
1136            let cfg_attrs = item.constructor.get_cfg_attrs(constructor_span);
1137            let id = item.id.clone();
1138            let ident = quote::format_ident!("constructor_{}", index);
1139            quote_spanned!(constructor_span=>
1140                {
1141                    let #ident = false;
1142                    #( #cfg_attrs )*
1143                    let #ident = <#storage_ident as ::ink::reflect::DispatchableConstructorInfo< #id >>::PAYABLE;
1144                    #ident
1145                }
1146            )
1147        });
1148        quote_spanned!(span=>
1149            {
1150                false #( || #constructor_is_payable )*
1151            }
1152        )
1153    }
1154
1155    /// Generates code for Solidity call info types for dispatchable ink! messages.
1156    fn generate_solidity_call_info(&self) -> TokenStream2 {
1157        if self.contract.config().abi().is_solidity() {
1158            let span = self.contract.module().span();
1159            let call_tys = self
1160                .contract
1161                .module()
1162                .impls()
1163                .flat_map(|item_impl| item_impl.iter_messages())
1164                .map(|message| {
1165                    let span = message.span();
1166                    let ident = message.ident();
1167                    let ident_str = ident.to_string();
1168                    let call_type_ident = solidity_call_type_ident(&message);
1169
1170                    // Solidity call signature parts.
1171                    let sig_param_tys = message.inputs().map(|input| {
1172                        let ty = &*input.ty;
1173                        let span = input.span();
1174                        quote_spanned!(span=>
1175                            <#ty as ::ink::SolDecode>::SOL_NAME
1176                        )
1177                    });
1178                    let input_types_len = generator::input_types(message.inputs()).len();
1179                    let sig_arg_fmt_params = (0..input_types_len).map(|_| "{}").collect::<Vec<_>>().join(",");
1180                    let sig_fmt_lit = format!("{{}}({})", sig_arg_fmt_params);
1181
1182                    quote_spanned!(span=>
1183                        pub struct #call_type_ident;
1184
1185                        const _: () = {
1186                            impl #call_type_ident {
1187                                pub const SIGNATURE: &'static ::core::primitive::str = ::ink::codegen::const_format!(#sig_fmt_lit, #ident_str #(,#sig_param_tys)*);
1188                                pub const SELECTOR: [::core::primitive::u8; 4usize] = ::ink::codegen::sol_selector_bytes(Self::SIGNATURE);
1189                                pub const SELECTOR_ID: ::core::primitive::u32 = ::core::primitive::u32::from_be_bytes(Self::SELECTOR);
1190                            }
1191                        };
1192                    )
1193                });
1194            quote_spanned!(span=>
1195                #[allow(non_camel_case_types)]
1196                mod __ink_sol_interop__ {
1197                    #[allow(unused)]
1198                    use super::*;
1199
1200                    #(#call_tys)*
1201                }
1202            )
1203        } else {
1204            quote!()
1205        }
1206    }
1207}