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