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