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