ink_codegen/generator/as_dependency/
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 ir::Callable;
18use proc_macro2::TokenStream as TokenStream2;
19use quote::{
20    format_ident,
21    quote,
22    quote_spanned,
23};
24use syn::spanned::Spanned as _;
25
26use crate::{
27    generator,
28    generator::sol,
29    GenerateCode,
30};
31
32/// Generates code for the call builder of the ink! smart contract.
33///
34/// The call builder is the entity that builds up calls for calling of other
35/// smart contract on-chain in a type safe way.
36/// It implements all ink! traits that the associated ink! smart contract implements
37/// so that their underlying implementation directly calls the respective ink!
38/// trait implementation on-chain.
39///
40/// The ink! call builder of a smart contract is directly used by the storage
41/// type of the smart contract itself as well by other entities that use the
42/// smart contract via long-hand calling notation to incrementally build up calls.
43#[derive(From)]
44pub struct CallBuilder<'a> {
45    contract: &'a ir::Contract,
46}
47
48impl GenerateCode for CallBuilder<'_> {
49    fn generate_code(&self) -> TokenStream2 {
50        let call_builder_struct = self.generate_struct();
51        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
52        let call_builder_impls = self.generate_call_forwarder_impls();
53        let call_builder_inherent_impls = self.generate_call_builder_inherent_impls();
54        quote! {
55            const _: () = {
56                #call_builder_struct
57                #auxiliary_trait_impls
58                #call_builder_impls
59                #call_builder_inherent_impls
60            };
61        }
62    }
63}
64
65impl CallBuilder<'_> {
66    /// Returns the identifier of the generated ink! call builder struct.
67    ///
68    /// # Note
69    ///
70    /// This identifier must not be used outside of the generated `const`
71    /// block in which the call builder type is going to be defined.
72    /// In order to refer to the call builder of an ink! smart contract
73    /// use the [`ink::TraitCallBuilder`] trait implementation.
74    fn call_builder_ident() -> syn::Ident {
75        format_ident!("CallBuilder")
76    }
77
78    fn generate_struct(&self) -> TokenStream2 {
79        let span = self.contract.module().storage().span();
80        let storage_ident = self.contract.module().storage().ident();
81        let cb_ident = Self::call_builder_ident();
82        let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
83            // These manual implementations are a bit more efficient than the derived
84            // equivalents.
85            quote_spanned!(span=>
86                impl<Abi> ::ink::SolDecode for #cb_ident<Abi> {
87                    type SolType = ::ink::Address;
88
89                    fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
90                        Ok(Self {
91                            addr: value,
92                            _marker: ::core::marker::PhantomData,
93                        })
94                    }
95                }
96
97                impl<'a, Abi> ::ink::SolEncode<'a> for #cb_ident<Abi> {
98                    type SolType = &'a ::ink::Address;
99
100                    fn to_sol_type(&'a self) -> Self::SolType {
101                        &self.addr
102                    }
103                }
104            )
105        } else {
106            quote!()
107        };
108        quote_spanned!(span=>
109            /// The ink! smart contract's call builder.
110            ///
111            /// Implements the underlying on-chain calling of the ink! smart contract
112            /// messages and trait implementations in a type safe way.
113            #[repr(transparent)]
114            #[derive(
115                ::core::fmt::Debug,
116                ::core::hash::Hash,
117                ::core::cmp::PartialEq,
118                ::core::cmp::Eq,
119                ::core::clone::Clone,
120            )]
121            #[::ink::scale_derive(Encode, Decode)]
122            pub struct #cb_ident<Abi> {
123                addr: ::ink::Address,
124                _marker: core::marker::PhantomData<Abi>,
125            }
126
127            const _: () = {
128                impl ::ink::codegen::ContractCallBuilder for #storage_ident {
129                    type Type<Abi> = #cb_ident<Abi>;
130                }
131
132                impl<Abi> ::ink::env::ContractEnv for #cb_ident<Abi> {
133                    type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
134                }
135
136                #[cfg(feature = "std")]
137                impl<Abi> ::ink::storage::traits::StorageLayout for #cb_ident<Abi> {
138                    fn layout(
139                        __key: &::ink::primitives::Key,
140                    ) -> ::ink::metadata::layout::Layout {
141                        ::ink::metadata::layout::Layout::Struct(
142                            ::ink::metadata::layout::StructLayout::new(
143                                ::core::stringify!(#cb_ident),
144                                [
145                                    ::ink::metadata::layout::FieldLayout::new(
146                                        "addr",
147                                        <::ink::Address
148                                            as ::ink::storage::traits::StorageLayout>::layout(__key)
149                                    )
150                                ]
151                            )
152                        )
153                    }
154                }
155
156                #[cfg(feature = "std")]
157                // We require this manual implementation since the derive produces incorrect trait bounds.
158                impl<Abi> ::ink::scale_info::TypeInfo for #cb_ident<Abi>
159                where
160                    ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
161                {
162                    type Identity = ::ink::Address;
163
164                    fn type_info() -> ::ink::scale_info::Type {
165                        <::ink::Address as ::ink::scale_info::TypeInfo>::type_info()
166                    }
167                }
168
169                #sol_codec
170            };
171        )
172    }
173
174    /// Generates some ink! specific auxiliary trait implementations for the
175    /// smart contract call builder type.
176    ///
177    /// These are required to properly interoperate with the call builder.
178    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
179        let span = self.contract.module().storage().span();
180        let cb_ident = Self::call_builder_ident();
181        quote_spanned!(span=>
182            impl<Abi> ::ink::env::call::FromAddr for #cb_ident<Abi> {
183                #[inline]
184                fn from_addr(addr: ::ink::Address) -> Self {
185                    Self { addr, _marker: ::core::default::Default::default(), }
186                }
187            }
188
189            impl<Abi> ::ink::ToAddr for #cb_ident<Abi> {
190                #[inline]
191                fn to_addr(&self) -> ::ink::Address {
192                    <::ink::Address as ::core::clone::Clone>::clone(&self.addr)
193                }
194            }
195
196            impl<Abi> ::core::convert::AsRef<::ink::Address> for #cb_ident<Abi> {
197                fn as_ref(&self) -> &::ink::Address {
198                    &self.addr
199                }
200            }
201
202            impl<Abi> ::core::convert::AsMut<::ink::Address> for #cb_ident<Abi> {
203                fn as_mut(&mut self) -> &mut ::ink::Address {
204                    &mut self.addr
205                }
206            }
207        )
208    }
209
210    /// Generate the `TraitCallForwarder` trait implementations for the call builder
211    /// for every ink! trait implemented by the associated ink! smart contract.
212    ///
213    /// These call forwarder trait implementations are used to dispatch to the global
214    /// call builder for the respective ink! trait definition that is being called.
215    /// The call builder only forwards the actual calls to those global call builders
216    /// and does not have its own calling logic.
217    fn generate_call_forwarder_impls(&self) -> TokenStream2 {
218        self.contract
219            .module()
220            .impls()
221            .filter_map(|impl_block| {
222                // We are only interested in ink! trait implementation block.
223                impl_block.trait_path().map(|trait_path| {
224                    self.generate_code_for_trait_impl(trait_path, impl_block)
225                })
226            })
227            .collect()
228    }
229
230    /// Generates code required by the ink! call builder of an ink! smart contract
231    /// for a single ink! trait definition that the contract implements.
232    fn generate_code_for_trait_impl(
233        &self,
234        trait_path: &syn::Path,
235        impl_block: &ir::ItemImpl,
236    ) -> TokenStream2 {
237        let call_forwarder_impl =
238            self.generate_call_forwarder_for_trait_impl(trait_path, impl_block);
239        let ink_trait_impl = self.generate_ink_trait_impl(trait_path, impl_block);
240        quote! {
241            #call_forwarder_impl
242            #ink_trait_impl
243        }
244    }
245
246    /// Generates code for a single ink! trait implementation to forward calls for
247    /// the associated ink! smart contract call builder.
248    fn generate_call_forwarder_for_trait_impl(
249        &self,
250        trait_path: &syn::Path,
251        impl_block: &ir::ItemImpl,
252    ) -> TokenStream2 {
253        let span = impl_block.span();
254        let cb_ident = Self::call_builder_ident();
255        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
256        generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
257            #[doc(hidden)]
258            impl ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}> for #cb_ident<#abi> {
259                type Forwarder = <<Self as #trait_path>::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder<#abi>;
260
261                #[inline]
262                fn forward(&self) -> &Self::Forwarder {
263                    // SAFETY:
264                    //
265                    // We convert from a shared reference to a type that thinly wraps
266                    // only an `AccountId` to a shared reference to another type of which
267                    // we know that it also thinly wraps an `AccountId`.
268                    // Furthermore both types use `repr(transparent)`.
269                    // todo
270                    unsafe {
271                        &*(&self.addr as *const ::ink::Address as *const Self::Forwarder)
272                    }
273                }
274
275                #[inline]
276                fn forward_mut(&mut self) -> &mut Self::Forwarder {
277                    // SAFETY:
278                    //
279                    // We convert from an exclusive reference to a type that thinly wraps
280                    // only an `AccountId` to an exclusive reference to another type of which
281                    // we know that it also thinly wraps an `AccountId`.
282                    // Furthermore both types use `repr(transparent)`.
283                    unsafe {
284                        &mut *(&mut self.addr as *mut ::ink::Address as *mut Self::Forwarder)
285                    }
286                }
287
288                #[inline]
289                fn build(&self) -> &<Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder {
290                    <_ as ::ink::codegen::TraitCallBuilder>::call(
291                        <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward(self)
292                    )
293                }
294
295                #[inline]
296                fn build_mut(&mut self)
297                    -> &mut <Self::Forwarder as ::ink::codegen::TraitCallBuilder>::Builder
298                {
299                    <_ as ::ink::codegen::TraitCallBuilder>::call_mut(
300                        <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::forward_mut(self)
301                    )
302                }
303            }
304        ))
305    }
306
307    /// Generates the actual ink! trait implementation for the generated call builder.
308    fn generate_ink_trait_impl(
309        &self,
310        trait_path: &syn::Path,
311        impl_block: &ir::ItemImpl,
312    ) -> TokenStream2 {
313        let span = impl_block.span();
314        let cb_ident = Self::call_builder_ident();
315        let messages = impl_block
316            .iter_messages()
317            .map(|message| self.generate_ink_trait_impl_for_message(trait_path, message));
318        let messages = quote! {
319            #( #messages )*
320        };
321        generate_abi_impls!(@tokens |abi| quote_spanned!(span=>
322            impl #trait_path for #cb_ident<#abi> {
323                type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
324                    as #trait_path>::__ink_TraitInfo;
325
326                #messages
327            }
328        ))
329    }
330
331    /// Generates the code for the ink! trait implementation of the call builder
332    /// of a single ink! trait message and its associated output type.
333    fn generate_ink_trait_impl_for_message(
334        &self,
335        trait_path: &syn::Path,
336        message: ir::CallableWithSelector<ir::Message>,
337    ) -> TokenStream2 {
338        use ir::Callable as _;
339        let span = message.span();
340        let message_ident = message.ident();
341        let output_ident = generator::output_ident(message_ident);
342        let cfg_attrs = message.get_cfg_attrs(span);
343        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
344        let (input_bindings, input_types): (Vec<_>, Vec<_>) = message
345            .callable()
346            .inputs()
347            .map(|input| (&input.pat, &input.ty))
348            .unzip();
349        let mut_token = message
350            .receiver()
351            .is_ref_mut()
352            .then(|| Some(quote! { mut }));
353        let build_cmd = match message.receiver() {
354            ir::Receiver::Ref => quote! { build },
355            ir::Receiver::RefMut => quote! { build_mut },
356        };
357        let attrs = self
358            .contract
359            .config()
360            .whitelisted_attributes()
361            .filter_attr(message.attrs().to_vec());
362        quote_spanned!(span=>
363            #( #cfg_attrs )*
364            type #output_ident = <<<
365                Self
366                as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::Forwarder
367                as ::ink::codegen::TraitCallBuilder>::Builder
368                as #trait_path>::#output_ident;
369
370            #[inline]
371            #( #attrs )*
372            fn #message_ident(
373                & #mut_token self
374                #( , #input_bindings: #input_types )*
375            ) -> Self::#output_ident {
376                <_ as #trait_path>::#message_ident(
377                    <Self as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#build_cmd(self)
378                    #( , #input_bindings )*
379                )
380            }
381        )
382    }
383
384    /// Generate call builder code for all ink! inherent ink! implementation blocks.
385    ///
386    /// # Note
387    ///
388    /// This does not provide implementations for ink! constructors as they
389    /// do not have a short-hand notations as their messages counterparts.
390    fn generate_call_builder_inherent_impls(&self) -> TokenStream2 {
391        self.contract
392            .module()
393            .impls()
394            .filter(|impl_block| impl_block.trait_path().is_none())
395            .map(|impl_block| self.generate_call_builder_inherent_impl(impl_block))
396            .collect()
397    }
398
399    /// Generate call builder code for a single inherent ink! implementation block.
400    ///
401    /// # Note
402    ///
403    /// Unlike as with ink! trait implementation blocks we do not have to generate
404    /// associate `*Output` types, ink! trait validating implementation blocks or
405    /// trait forwarder implementations. Instead we build the calls directly.
406    fn generate_call_builder_inherent_impl(
407        &self,
408        impl_block: &ir::ItemImpl,
409    ) -> TokenStream2 {
410        let span = impl_block.span();
411        let cb_ident = Self::call_builder_ident();
412        generate_abi_impls!(@type |abi| {
413            let abi_ty = match abi {
414                Abi::Ink => quote!(::ink::abi::Ink),
415                Abi::Sol => quote!(::ink::abi::Sol),
416            };
417            let messages = impl_block.iter_messages().map(|message| {
418                self.generate_call_builder_inherent_impl_for_message(message, abi)
419            });
420            quote_spanned!(span=>
421                impl #cb_ident<#abi_ty> {
422                    #( #messages )*
423                }
424            )
425        })
426    }
427
428    /// Generate call builder code for a single inherent ink! message.
429    ///
430    /// # Note
431    ///
432    /// Unlike with ink! trait messages the call builder implements the call
433    /// building directly and does not forward to a trait call builder.
434    fn generate_call_builder_inherent_impl_for_message(
435        &self,
436        message: ir::CallableWithSelector<ir::Message>,
437        abi: Abi,
438    ) -> TokenStream2 {
439        let span = message.span();
440        let callable = message.callable();
441        let message_ident = message.ident();
442        let attrs = self
443            .contract
444            .config()
445            .whitelisted_attributes()
446            .filter_attr(message.attrs().to_vec());
447        let input_bindings = generator::input_bindings(callable.inputs());
448        let input_types = generator::input_types(message.inputs());
449        let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut });
450        let return_type = message
451            .output()
452            .map(quote::ToTokens::to_token_stream)
453            .unwrap_or_else(|| quote::quote! { () });
454        let output_span = return_type.span();
455        let (selector_bytes, abi_ty) = match abi {
456            Abi::Ink => {
457                let selector = message.composed_selector();
458                let selector_bytes = selector.hex_lits();
459                (quote!([ #( #selector_bytes ),* ]), quote!(::ink::abi::Ink))
460            }
461            Abi::Sol => (sol::utils::selector(&message), quote!(::ink::abi::Sol)),
462        };
463        let arg_list = generator::generate_argument_list(
464            input_types.iter().cloned(),
465            abi_ty.clone(),
466        );
467        let output_type = quote_spanned!(output_span=>
468            ::ink::env::call::CallBuilder<
469                Environment,
470                ::ink::env::call::utils::Set< ::ink::env::call::Call >,
471                ::ink::env::call::utils::Set< ::ink::env::call::ExecutionInput<#arg_list, #abi_ty> >,
472                ::ink::env::call::utils::Set< ::ink::env::call::utils::ReturnType<#return_type> >,
473            >
474        );
475        quote_spanned!(span=>
476            #( #attrs )*
477            #[allow(clippy::type_complexity)]
478            #[inline]
479            pub fn #message_ident(
480                & #mut_tok self
481                #( , #input_bindings : #input_types )*
482            ) -> #output_type {
483                ::ink::env::call::build_call_abi::<Environment, #abi_ty>()
484                    .call(::ink::ToAddr::to_addr(self))
485                    .exec_input(
486                        ::ink::env::call::ExecutionInput::new(
487                            ::ink::env::call::Selector::new(#selector_bytes)
488                        )
489                        #(
490                            .push_arg(#input_bindings)
491                        )*
492                    )
493                    .returns::<#return_type>()
494            }
495        )
496    }
497}