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, Abi>
115            where
116                E: ::ink::env::Environment,
117            {
118                builder: <Self as ::ink::codegen::TraitCallBuilder>::Builder,
119                _marker: ::core::marker::PhantomData<fn() -> Abi>,
120            }
121        )
122    }
123
124    /// Generates the `StorageLayout` trait implementation for the account wrapper.
125    ///
126    /// # Note
127    ///
128    /// Due to the generic parameter `E` and Rust's default rules for derive generated
129    /// trait bounds it is not recommended to derive the `StorageLayout` trait
130    /// implementation.
131    fn generate_storage_layout_impl(&self) -> TokenStream2 {
132        let span = self.span();
133        let call_forwarder_ident = self.ident();
134        quote_spanned!(span=>
135            #[cfg(feature = "std")]
136            impl<E, Abi> ::ink::storage::traits::StorageLayout
137                for #call_forwarder_ident<E, Abi>
138            where
139                E: ::ink::env::Environment,
140                ::ink::Address: ::ink::storage::traits::StorageLayout,
141            {
142                fn layout(
143                    __key: &::ink::primitives::Key,
144                ) -> ::ink::metadata::layout::Layout {
145                    <<Self as ::ink::codegen::TraitCallBuilder>::Builder
146                        as ::ink::storage::traits::StorageLayout>::layout(__key)
147                }
148            }
149        )
150    }
151
152    /// Generates trait implementations for auxiliary traits for the account wrapper.
153    ///
154    /// # Note
155    ///
156    /// Auxiliary traits currently include:
157    ///
158    /// - `Clone`: To allow cloning contract references in the long run.
159    /// - `Debug`: To better debug internal contract state.
160    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
161        let span = self.span();
162        let call_forwarder_ident = self.ident();
163        let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
164            // These manual implementations are a bit more efficient than the derived
165            // equivalents.
166            quote_spanned!(span=>
167                impl<E, Abi> ::ink::SolDecode for #call_forwarder_ident<E, Abi>
168                where
169                    E: ::ink::env::Environment,
170                {
171                    type SolType = ::ink::Address;
172
173                    fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
174                        Ok(Self {
175                            builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
176                                as ::ink::env::call::FromAddr>::from_addr(value),
177                            _marker: ::core::marker::PhantomData,
178                        })
179                    }
180                }
181
182                impl<'a, E, Abi> ::ink::SolEncode<'a> for #call_forwarder_ident<E, Abi>
183                where
184                    E: ::ink::env::Environment,
185                {
186                    type SolType = &'a ::ink::Address;
187
188                    fn to_sol_type(&'a self) -> Self::SolType {
189                        self.as_ref()
190                    }
191                }
192            )
193        } else {
194            quote!()
195        };
196        quote_spanned!(span=>
197            /// We require this manual implementation since the derive produces incorrect trait bounds.
198            impl<E, Abi> ::core::clone::Clone for #call_forwarder_ident<E, Abi>
199            where
200                E: ::ink::env::Environment,
201                ::ink::Address: ::core::clone::Clone,
202            {
203                #[inline]
204                fn clone(&self) -> Self {
205                    Self {
206                        builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
207                            as ::core::clone::Clone>::clone(&self.builder),
208                        _marker: self._marker,
209                    }
210                }
211            }
212
213            /// We require this manual implementation since the derive produces incorrect trait bounds.
214            impl<E, Abi> ::core::fmt::Debug for #call_forwarder_ident<E, Abi>
215            where
216                E: ::ink::env::Environment,
217                ::ink::Address: ::core::fmt::Debug,
218            {
219                fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
220                    f.debug_struct(::core::stringify!(#call_forwarder_ident))
221                        .field("addr", &self.builder.addr)
222                        .finish()
223                }
224            }
225
226            #[cfg(feature = "std")]
227            /// We require this manual implementation since the derive produces incorrect trait bounds.
228            impl<E, Abi> ::ink::scale_info::TypeInfo for #call_forwarder_ident<E, Abi>
229            where
230                E: ::ink::env::Environment,
231                ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
232            {
233                type Identity = <
234                    <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
235                >::Identity;
236
237                fn type_info() -> ::ink::scale_info::Type {
238                    <
239                        <Self as ::ink::codegen::TraitCallBuilder>::Builder as ::ink::scale_info::TypeInfo
240                    >::type_info()
241                }
242            }
243
244            #sol_codec
245        )
246    }
247
248    /// Generate trait impls for `FromAccountId` and `ToAccountId` for the account
249    /// wrapper.
250    ///
251    /// # Note
252    ///
253    /// This allows user code to conveniently transform from and to `AccountId` when
254    /// interacting with typed contracts.
255    fn generate_to_from_addr_impls(&self) -> TokenStream2 {
256        let span = self.span();
257        let call_forwarder_ident = self.ident();
258        quote_spanned!(span=>
259            impl<E, Abi> ::ink::env::call::FromAddr
260                for #call_forwarder_ident<E, Abi>
261            where
262                E: ::ink::env::Environment,
263            {
264                #[inline]
265                fn from_addr(addr: ::ink::Address) -> Self {
266                    Self {
267                        builder: <<Self as ::ink::codegen::TraitCallBuilder>::Builder
268                            as ::ink::env::call::FromAddr>::from_addr(addr),
269                        _marker: ::core::default::Default::default(),
270                    }
271                }
272            }
273
274            impl<E, Abi> ::core::convert::From<::ink::Address> for #call_forwarder_ident<E, Abi>
275            where
276                E: ::ink::env::Environment,
277            {
278                fn from(addr: ::ink::Address) -> Self {
279                    <Self as ::ink::env::call::FromAddr>::from_addr(addr)
280                }
281            }
282
283            impl<E, Abi> ::ink::ToAddr for #call_forwarder_ident<E, Abi>
284            where
285                E: ::ink::env::Environment,
286            {
287                #[inline]
288                fn to_addr(&self) -> ::ink::Address {
289                    <<Self as ::ink::codegen::TraitCallBuilder>::Builder
290                        as ::ink::ToAddr>::to_addr(&self.builder)
291                }
292            }
293
294            impl<E, Abi> ::core::convert::AsRef<::ink::Address> for #call_forwarder_ident<E, Abi>
295            where
296                E: ::ink::env::Environment,
297            {
298                fn as_ref(&self) -> &::ink::Address {
299                    <_ as ::core::convert::AsRef<::ink::Address>>::as_ref(&self.builder)
300                }
301            }
302
303            impl<E, Abi> ::core::convert::AsMut<::ink::Address> for #call_forwarder_ident<E, Abi>
304            where
305                E: ::ink::env::Environment,
306            {
307                fn as_mut(&mut self) -> &mut ::ink::Address {
308                    <_ as ::core::convert::AsMut<::ink::Address>>::as_mut(&mut self.builder)
309                }
310            }
311
312            impl<E, Abi> ::ink::env::ContractEnv for #call_forwarder_ident<E, Abi>
313            where
314                E: ::ink::env::Environment,
315            {
316                type Env = E;
317            }
318        )
319    }
320
321    /// Generate the trait implementation for `CallBuilder` for the ink! trait call
322    /// forwarder.
323    ///
324    /// # Note
325    ///
326    /// Through the implementation of this trait it is possible to refer to the
327    /// ink! trait call builder that is associated to this ink! trait call forwarder.
328    fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
329        let span = self.trait_def.span();
330        let call_forwarder_ident = self.ident();
331        let call_builder_ident = self.trait_def.call_builder_ident();
332        quote_spanned!(span=>
333            /// This trait allows to bridge from call forwarder to call builder.
334            ///
335            /// Also this explains why we designed the generated code so that we have
336            /// both types and why the forwarder is a thin-wrapper around the builder
337            /// as this allows to perform this operation safely.
338            impl<E, Abi> ::ink::codegen::TraitCallBuilder for #call_forwarder_ident<E, Abi>
339            where
340                E: ::ink::env::Environment,
341            {
342                type Builder = #call_builder_ident<E, Abi>;
343
344                #[inline]
345                fn call(&self) -> &<Self as ::ink::codegen::TraitCallBuilder>::Builder {
346                    &self.builder
347                }
348
349                #[inline]
350                fn call_mut(&mut self) -> &mut <Self as ::ink::codegen::TraitCallBuilder>::Builder {
351                    &mut self.builder
352                }
353            }
354        )
355    }
356
357    /// Generates the implementation of the associated ink! trait definition.
358    ///
359    /// # Note
360    ///
361    /// The implementation mainly forwards to the associated ink! call builder
362    /// of the same ink! trait definition.
363    fn generate_ink_trait_impl(&self) -> TokenStream2 {
364        let span = self.trait_def.span();
365        let trait_ident = self.trait_def.trait_def.item().ident();
366        let trait_info_ident = self.trait_def.trait_info_ident();
367        let forwarder_ident = self.ident();
368        let message_impls = self.generate_ink_trait_impl_messages();
369        generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
370            impl<E> #trait_ident for #forwarder_ident<E, #abi>
371            where
372                E: ::ink::env::Environment,
373            {
374                #[allow(non_camel_case_types)]
375                type __ink_TraitInfo = #trait_info_ident<E>;
376
377                #message_impls
378            }
379        ))
380    }
381
382    /// Generate the code for all ink! trait messages implemented by the trait call
383    /// forwarder.
384    fn generate_ink_trait_impl_messages(&self) -> TokenStream2 {
385        let messages =
386            self.trait_def
387                .trait_def
388                .item()
389                .iter_items()
390                .filter_map(|(item, _)| {
391                    item.filter_map_message()
392                        .map(|message| self.generate_ink_trait_impl_for_message(&message))
393                });
394        quote! {
395            #( #messages )*
396        }
397    }
398
399    /// Generate the code for a single ink! trait message implemented by the trait call
400    /// forwarder.
401    fn generate_ink_trait_impl_for_message(
402        &self,
403        message: &ir::InkTraitMessage,
404    ) -> TokenStream2 {
405        let span = message.span();
406        let trait_ident = self.trait_def.trait_def.item().ident();
407        let forwarder_ident = self.ident();
408        let message_ident = message.ident();
409        let attrs = self
410            .trait_def
411            .trait_def
412            .config()
413            .whitelisted_attributes()
414            .filter_attr(message.attrs());
415        let output_ident = generator::output_ident(message_ident);
416        let output_type = message
417            .output()
418            .cloned()
419            .unwrap_or_else(|| syn::parse_quote!(()));
420        let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
421        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
422        let call_op = match message.receiver() {
423            ir::Receiver::Ref => quote! { call },
424            ir::Receiver::RefMut => quote! { call_mut },
425        };
426        let mut_tok = message.mutates().then(|| quote! { mut });
427        let panic_str = format!(
428            "encountered error while calling <{forwarder_ident} as {trait_ident}>::{message_ident}",
429        );
430        let cfg_attrs = message.get_cfg_attrs(span);
431        quote_spanned!(span =>
432            #( #cfg_attrs )*
433            type #output_ident = #output_type;
434
435            #( #attrs )*
436            #[inline]
437            fn #message_ident(
438                & #mut_tok self
439                #( , #input_bindings : #input_types )*
440            ) -> Self::#output_ident {
441                <<Self as ::ink::codegen::TraitCallBuilder>::Builder as #trait_ident>::#message_ident(
442                    <Self as ::ink::codegen::TraitCallBuilder>::#call_op(self)
443                    #(
444                        , #input_bindings
445                    )*
446                )
447                    .try_invoke()
448                    .unwrap_or_else(|env_err| ::core::panic!("{}: {:?}", #panic_str, env_err))
449                    .unwrap_or_else(|lang_err| ::core::panic!("{}: {:?}", #panic_str, lang_err))
450            }
451        )
452    }
453}