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