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