ink_codegen/generator/trait_def/
call_forwarder.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 super::TraitDefinition;
16use crate::{
17    generator,
18    traits::GenerateCode,
19};
20use derive_more::From;
21use proc_macro2::{
22    Span,
23    TokenStream as TokenStream2,
24};
25use quote::{
26    quote,
27    quote_spanned,
28};
29
30impl TraitDefinition<'_> {
31    /// Generates code for the global trait call forwarder for an ink! trait.
32    ///
33    /// # Note
34    ///
35    /// - The generated call forwarder type implements the ink! trait definition and
36    ///   allows to build up contract calls that allow for customization by the user to
37    ///   provide gas limit, endowment etc.
38    /// - The call forwarder is associated to the call builder for the same ink! trait
39    ///   definition and handles all ink! trait calls into another contract instance
40    ///   on-chain. For constructing custom calls it forwards to the call builder.
41    pub fn generate_call_forwarder(&self) -> TokenStream2 {
42        CallForwarder::from(*self).generate_code()
43    }
44
45    /// The identifier of the ink! trait call forwarder.
46    pub fn call_forwarder_ident(&self) -> syn::Ident {
47        self.append_trait_suffix(CallForwarder::SUFFIX)
48    }
49}
50
51/// Generates code for the global ink! trait call forwarder.
52#[derive(From)]
53struct CallForwarder<'a> {
54    trait_def: TraitDefinition<'a>,
55}
56
57impl GenerateCode for CallForwarder<'_> {
58    fn generate_code(&self) -> TokenStream2 {
59        let struct_definition = self.generate_struct_definition();
60        let storage_layout_impl = self.generate_storage_layout_impl();
61        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
62        let to_from_addr_impls = self.generate_to_from_addr_impls();
63        let call_builder_impl = self.generate_call_builder_trait_impl();
64        let ink_trait_impl = self.generate_ink_trait_impl();
65        quote! {
66            #struct_definition
67            #storage_layout_impl
68            #auxiliary_trait_impls
69            #to_from_addr_impls
70            #call_builder_impl
71            #ink_trait_impl
72        }
73    }
74}
75
76impl CallForwarder<'_> {
77    /// The name suffix for ink! trait call forwarder.
78    const SUFFIX: &'static str = "TraitCallForwarder";
79
80    /// Returns the span of the ink! trait definition.
81    fn span(&self) -> Span {
82        self.trait_def.span()
83    }
84
85    /// Returns the identifier of the ink! trait call forwarder.
86    fn ident(&self) -> syn::Ident {
87        self.trait_def.call_forwarder_ident()
88    }
89
90    /// Generates the struct type definition for the account wrapper type.
91    ///
92    /// This type is going to implement the trait so that invoking its trait
93    /// methods will perform contract calls via contract's pallet contract
94    /// execution abstraction.
95    ///
96    /// # Note
97    ///
98    /// Unlike the layout specific traits it is possible to derive the SCALE
99    /// `Encode` and `Decode` traits since they generate trait bounds per field
100    /// instead of per generic parameter which is exactly what we need here.
101    /// However, it should be noted that this is not Rust default behavior.
102    fn generate_struct_definition(&self) -> TokenStream2 {
103        let span = self.span();
104        let call_forwarder_ident = self.ident();
105        quote_spanned!(span =>
106            /// The global call forwarder for the ink! trait definition.
107            ///
108            /// All cross-contract calls to contracts implementing the associated ink! trait
109            /// will be handled by this type.
110            #[doc(hidden)]
111            #[allow(non_camel_case_types)]
112            #[::ink::scale_derive(Encode, Decode)]
113            #[repr(transparent)]
114            pub struct #call_forwarder_ident<E>
115            where
116                E: ::ink::env::Environment,
117            {
118                builder: <Self as ::ink::codegen::TraitCallBuilder>::Builder,
119            }
120        )
121    }
122
123    /// Generates the `StorageLayout` trait implementation for the account wrapper.
124    ///
125    /// # Note
126    ///
127    /// Due to the generic parameter `E` and Rust's default rules for derive generated
128    /// trait bounds it is not recommended to derive the `StorageLayout` trait
129    /// implementation.
130    fn generate_storage_layout_impl(&self) -> TokenStream2 {
131        let span = self.span();
132        let call_forwarder_ident = self.ident();
133        quote_spanned!(span=>
134            #[cfg(feature = "std")]
135            impl<E> ::ink::storage::traits::StorageLayout
136                for #call_forwarder_ident<E>
137            where
138                E: ::ink::env::Environment,
139                ::ink::H160: ::ink::storage::traits::StorageLayout,
140            {
141                fn layout(
142                    __key: &::ink::primitives::Key,
143                ) -> ::ink::metadata::layout::Layout {
144                    <<Self as ::ink::codegen::TraitCallBuilder>::Builder
145                        as ::ink::storage::traits::StorageLayout>::layout(__key)
146                }
147            }
148        )
149    }
150
151    /// Generates trait implementations for auxiliary traits for the account wrapper.
152    ///
153    /// # Note
154    ///
155    /// Auxiliary traits currently include:
156    ///
157    /// - `Clone`: To allow cloning contract references in the long run.
158    /// - `Debug`: To better debug internal contract state.
159    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
160        let span = self.span();
161        let call_forwarder_ident = self.ident();
162        quote_spanned!(span=>
163            /// We require this manual implementation since the derive produces incorrect trait bounds.
164            impl<E> ::core::clone::Clone for #call_forwarder_ident<E>
165            where
166                E: ::ink::env::Environment,
167                ::ink::H160: ::core::clone::Clone,
168            {
169                #[inline]
170                fn clone(&self) -> Self {
171                    Self {
172                        builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
173                            as ::core::clone::Clone>::clone(&self.builder),
174                    }
175                }
176            }
177
178            /// We require this manual implementation since the derive produces incorrect trait bounds.
179            impl<E> ::core::fmt::Debug for #call_forwarder_ident<E>
180            where
181                E: ::ink::env::Environment,
182                ::ink::H160: ::core::fmt::Debug,
183            {
184                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
185                    f.debug_struct(::core::stringify!(#call_forwarder_ident))
186                        .field("addr", &self.builder.addr)
187                        .finish()
188                }
189            }
190
191            #[cfg(feature = "std")]
192            /// We require this manual implementation since the derive produces incorrect trait bounds.
193            impl<E> ::ink::scale_info::TypeInfo for #call_forwarder_ident<E>
194            where
195                E: ::ink::env::Environment,
196                ::ink::H160: ::ink::scale_info::TypeInfo + 'static,
197            {
198                type Identity = <
199                    <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
200                >::Identity;
201
202                fn type_info() -> ::ink::scale_info::Type {
203                    <
204                        <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
205                    >::type_info()
206                }
207            }
208        )
209    }
210
211    /// Generate trait impls for `FromAccountId` and `ToAccountId` for the account
212    /// wrapper.
213    ///
214    /// # Note
215    ///
216    /// This allows user code to conveniently transform from and to `AccountId` when
217    /// interacting with typed contracts.
218    fn generate_to_from_addr_impls(&self) -> TokenStream2 {
219        let span = self.span();
220        let call_forwarder_ident = self.ident();
221        quote_spanned!(span=>
222            impl<E> ::ink::env::call::FromAddr
223                for #call_forwarder_ident<E>
224            where
225                E: ::ink::env::Environment,
226            {
227                #[inline]
228                fn from_addr(addr: ::ink::H160) -> Self {
229                    Self { builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
230                        as ::ink::env::call::FromAddr>::from_addr(addr) }
231                }
232            }
233
234            impl<E> ::core::convert::From<::ink::H160> for #call_forwarder_ident<E>
235            where
236                E: ::ink::env::Environment,
237            {
238                fn from(addr: ::ink::H160) -> Self {
239                    <Self as ::ink::env::call::FromAddr>::from_addr(addr)
240                }
241            }
242
243            impl<E> ::ink::ToAddr for #call_forwarder_ident<E>
244            where
245                E: ::ink::env::Environment,
246            {
247                #[inline]
248                fn to_addr(&self) -> ::ink::H160 {
249                    <<Self as ::ink::codegen::TraitCallBuilder>::Builder
250                        as ::ink::ToAddr>::to_addr(&self.builder)
251                }
252            }
253
254            impl<E> ::core::convert::AsRef<::ink::H160> for #call_forwarder_ident<E>
255            where
256                E: ::ink::env::Environment,
257            {
258                fn as_ref(&self) -> &::ink::H160 {
259                    <_ as ::core::convert::AsRef<::ink::H160>>::as_ref(&self.builder)
260                }
261            }
262
263            impl<E> ::core::convert::AsMut<::ink::H160> for #call_forwarder_ident<E>
264            where
265                E: ::ink::env::Environment,
266            {
267                fn as_mut(&mut self) -> &mut ::ink::H160 {
268                    <_ as ::core::convert::AsMut<::ink::H160>>::as_mut(&mut self.builder)
269                }
270            }
271        )
272    }
273
274    /// Generate the trait implementation for `CallBuilder` for the ink! trait call
275    /// forwarder.
276    ///
277    /// # Note
278    ///
279    /// Through the implementation of this trait it is possible to refer to the
280    /// ink! trait call builder that is associated to this ink! trait call forwarder.
281    fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
282        let span = self.trait_def.span();
283        let call_forwarder_ident = self.ident();
284        let call_builder_ident = self.trait_def.call_builder_ident();
285        quote_spanned!(span=>
286            /// This trait allows to bridge from call forwarder to call builder.
287            ///
288            /// Also this explains why we designed the generated code so that we have
289            /// both types and why the forwarder is a thin-wrapper around the builder
290            /// as this allows to perform this operation safely.
291            impl<E> ::ink::codegen::TraitCallBuilder for #call_forwarder_ident<E>
292            where
293                E: ::ink::env::Environment,
294            {
295                type Builder = #call_builder_ident<E>;
296
297                #[inline]
298                fn call(&self) -> &<Self as ::ink::codegen::TraitCallBuilder>::Builder {
299                    &self.builder
300                }
301
302                #[inline]
303                fn call_mut(&mut self) -> &mut <Self as ::ink::codegen::TraitCallBuilder>::Builder {
304                    &mut self.builder
305                }
306            }
307        )
308    }
309
310    /// Generates the implementation of the associated ink! trait definition.
311    ///
312    /// # Note
313    ///
314    /// The implementation mainly forwards to the associated ink! call builder
315    /// of the same ink! trait definition.
316    fn generate_ink_trait_impl(&self) -> TokenStream2 {
317        let span = self.trait_def.span();
318        let trait_ident = self.trait_def.trait_def.item().ident();
319        let trait_info_ident = self.trait_def.trait_info_ident();
320        let forwarder_ident = self.ident();
321        let message_impls = self.generate_ink_trait_impl_messages();
322        quote_spanned!(span=>
323            impl<E> ::ink::env::ContractEnv for #forwarder_ident<E>
324            where
325                E: ::ink::env::Environment,
326            {
327                type Env = E;
328            }
329
330            impl<E> #trait_ident for #forwarder_ident<E>
331            where
332                E: ::ink::env::Environment,
333            {
334                #[allow(non_camel_case_types)]
335                type __ink_TraitInfo = #trait_info_ident<E>;
336
337                #message_impls
338            }
339        )
340    }
341
342    /// Generate the code for all ink! trait messages implemented by the trait call
343    /// forwarder.
344    fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
345        let messages =
346            self.trait_def
347                .trait_def
348                .item()
349                .iter_items()
350                .filter_map(|(item, _)| {
351                    item.filter_map_message()
352                        .map(|message| self.generate_ink_trait_impl_for_message(&message))
353                });
354        quote! {
355            #( #messages )*
356        }
357    }
358
359    /// Generate the code for a single ink! trait message implemented by the trait call
360    /// forwarder.
361    fn generate_ink_trait_impl_for_message(
362        &self,
363        message: &ir::InkTraitMessage,
364    ) -> TokenStream2 {
365        let span = message.span();
366        let trait_ident = self.trait_def.trait_def.item().ident();
367        let forwarder_ident = self.ident();
368        let message_ident = message.ident();
369        let attrs = self
370            .trait_def
371            .trait_def
372            .config()
373            .whitelisted_attributes()
374            .filter_attr(message.attrs());
375        let output_ident = generator::output_ident(message_ident);
376        let output_type = message
377            .output()
378            .cloned()
379            .unwrap_or_else(|| syn::parse_quote!(()));
380        let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
381        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
382        let call_op = match message.receiver() {
383            ir::Receiver::Ref => quote! { call },
384            ir::Receiver::RefMut => quote! { call_mut },
385        };
386        let mut_tok = message.mutates().then(|| quote! { mut });
387        let panic_str = format!(
388            "encountered error while calling <{forwarder_ident} as {trait_ident}>::{message_ident}",
389        );
390        let cfg_attrs = message.get_cfg_attrs(span);
391        quote_spanned!(span =>
392            #( #cfg_attrs )*
393            type #output_ident = #output_type;
394
395            #( #attrs )*
396            #[inline]
397            fn #message_ident(
398                & #mut_tok self
399                #( , #input_bindings : #input_types )*
400            ) -> Self::#output_ident {
401                <<Self as ::ink::codegen::TraitCallBuilder>::Builder as #trait_ident>::#message_ident(
402                    <Self as ::ink::codegen::TraitCallBuilder>::#call_op(self)
403                    #(
404                        , #input_bindings
405                    )*
406                )
407                    .try_invoke()
408                    .unwrap_or_else(|env_err| ::core::panic!("{}: {:?}", #panic_str, env_err))
409                    .unwrap_or_else(|lang_err| ::core::panic!("{}: {:?}", #panic_str, lang_err))
410            }
411        )
412    }
413}