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