ink_codegen/generator/as_dependency/
contract_ref.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    GenerateCode,
18};
19use derive_more::From;
20use ir::{
21    Callable,
22    IsDocAttribute as _,
23};
24use proc_macro2::TokenStream as TokenStream2;
25use quote::{
26    format_ident,
27    quote,
28    quote_spanned,
29};
30use syn::spanned::Spanned as _;
31
32/// Generates code for the contract reference 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 ContractRef<'a> {
45    contract: &'a ir::Contract,
46}
47
48impl GenerateCode for ContractRef<'_> {
49    fn generate_code(&self) -> TokenStream2 {
50        let contract_ref = self.generate_struct();
51        let contract_ref_trait_impls = self.generate_contract_trait_impls();
52        let contract_ref_inherent_impls = self.generate_contract_inherent_impls();
53        let call_builder_trait_impl = self.generate_call_builder_trait_impl();
54        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
55        quote! {
56            #contract_ref
57            #contract_ref_trait_impls
58            #contract_ref_inherent_impls
59            #call_builder_trait_impl
60            #auxiliary_trait_impls
61        }
62    }
63}
64
65impl ContractRef<'_> {
66    /// Generates the identifier of the contract reference struct.
67    fn generate_contract_ref_ident(&self) -> syn::Ident {
68        quote::format_ident!("{}Ref", self.contract.module().storage().ident())
69    }
70
71    /// Generates the code for the struct representing the contract reference.
72    ///
73    /// The generated struct is the type onto which everything is implemented.
74    /// It is also the type that is going to be used by other smart contract
75    /// dynamically depending on the smart contract. It mirrors the smart contract
76    /// API but is just a typed thin-wrapper around an `AccountId`.
77    fn generate_struct(&self) -> TokenStream2 {
78        let span = self.contract.module().storage().span();
79        let doc_attrs = self
80            .contract
81            .module()
82            .storage()
83            .attrs()
84            .iter()
85            .filter(|&x| syn::Attribute::is_doc_attribute(x))
86            .cloned();
87        let storage_ident = self.contract.module().storage().ident();
88        let ref_ident = self.generate_contract_ref_ident();
89        quote_spanned!(span=>
90            #[cfg_attr(feature = "std", derive(
91                ::ink::storage::traits::StorageLayout,
92            ))]
93            #[derive(
94                ::core::fmt::Debug,
95                ::core::hash::Hash,
96                ::core::cmp::PartialEq,
97                ::core::cmp::Eq,
98                ::core::clone::Clone,
99            )]
100            #[::ink::scale_derive(Encode, Decode, TypeInfo)]
101            #( #doc_attrs )*
102            pub struct #ref_ident {
103                inner: <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type,
104            }
105
106            const _: () = {
107                impl ::ink::env::ContractReference for #storage_ident {
108                    type Type = #ref_ident;
109                }
110
111                impl ::ink::env::ContractReverseReference for #ref_ident {
112                    type Type = #storage_ident;
113                }
114
115                impl ::ink::env::call::ConstructorReturnType<#ref_ident> for #storage_ident {
116                    type Output = #ref_ident;
117                    type Error = ();
118
119                    fn ok(value: #ref_ident) -> Self::Output {
120                        value
121                    }
122                }
123
124                impl<E> ::ink::env::call::ConstructorReturnType<#ref_ident>
125                    for ::core::result::Result<#storage_ident, E>
126                where
127                    E: ::ink::scale::Decode
128                {
129                    const IS_RESULT: bool = true;
130
131                    type Output = ::core::result::Result<#ref_ident, E>;
132                    type Error = E;
133
134                    fn ok(value: #ref_ident) -> Self::Output {
135                        ::core::result::Result::Ok(value)
136                    }
137
138                    fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
139                        ::core::option::Option::Some(::core::result::Result::Err(err))
140                    }
141                }
142
143                impl ::ink::env::ContractEnv for #ref_ident {
144                    type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
145                }
146            };
147        )
148    }
149
150    /// Generates some ink! specific auxiliary trait implementations for the
151    /// smart contract reference type.
152    ///
153    /// These are required to properly interoperate with the contract reference.
154    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
155        let span = self.contract.module().storage().span();
156        let storage_ident = self.contract.module().storage().ident();
157        let ref_ident = self.generate_contract_ref_ident();
158        quote_spanned!(span=>
159            impl ::ink::env::call::FromAddr for #ref_ident {
160                #[inline]
161                fn from_addr(addr: ::ink::H160) -> Self {
162                    Self { inner: <<#storage_ident
163                        as ::ink::codegen::ContractCallBuilder>::Type
164                        as ::ink::env::call::FromAddr>::from_addr(addr)
165                    }
166                }
167            }
168
169            impl ::ink::ToAddr for #ref_ident {
170                #[inline]
171                fn to_addr(&self) -> ::ink::H160 {
172                    <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type
173                        as ::ink::ToAddr>::to_addr(&self.inner)
174                }
175            }
176
177            impl ::core::convert::AsRef<::ink::H160> for #ref_ident {
178                fn as_ref(&self) -> &::ink::H160 {
179                    <_ as ::core::convert::AsRef<::ink::H160>>::as_ref(&self.inner)
180                }
181            }
182
183            impl ::core::convert::AsMut<::ink::H160> for #ref_ident {
184                fn as_mut(&mut self) -> &mut ::ink::H160 {
185                    <_ as ::core::convert::AsMut<::ink::H160>>::as_mut(&mut self.inner)
186                }
187            }
188        )
189    }
190
191    /// Generates the `CallBuilder` trait implementation for the contract reference.
192    ///
193    /// This creates the bridge between the ink! smart contract type and the
194    /// associated call builder.
195    fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
196        let span = self.contract.module().storage().span();
197        let ref_ident = self.generate_contract_ref_ident();
198        let storage_ident = self.contract.module().storage().ident();
199        quote_spanned!(span=>
200            const _: () = {
201                impl ::ink::codegen::TraitCallBuilder for #ref_ident {
202                    type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type;
203
204                    #[inline]
205                    fn call(&self) -> &Self::Builder {
206                        &self.inner
207                    }
208
209                    #[inline]
210                    fn call_mut(&mut self) -> &mut Self::Builder {
211                        &mut self.inner
212                    }
213                }
214            };
215        )
216    }
217
218    /// Generates the code for all ink! trait implementations of the contract itself.
219    ///
220    /// # Note
221    ///
222    /// The generated implementations must live outside of an artificial `const` block
223    /// in order to properly show their documentation using `rustdoc`.
224    fn generate_contract_trait_impls(&self) -> TokenStream2 {
225        self.contract
226            .module()
227            .impls()
228            .filter_map(|impl_block| {
229                // We are only interested in ink! trait implementation block.
230                impl_block.trait_path().map(|trait_path| {
231                    self.generate_contract_trait_impl(trait_path, impl_block)
232                })
233            })
234            .collect()
235    }
236
237    /// Generates the code for a single ink! trait implementation of the contract itself.
238    ///
239    /// The generated implementation mainly forwards the calls to the previously generated
240    /// associated call builder that implements each respective ink! trait.
241    fn generate_contract_trait_impl(
242        &self,
243        trait_path: &syn::Path,
244        impl_block: &ir::ItemImpl,
245    ) -> TokenStream2 {
246        let span = impl_block.span();
247        let attrs = impl_block.attrs();
248        let forwarder_ident = self.generate_contract_ref_ident();
249        let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block);
250        quote_spanned!(span=>
251            #( #attrs )*
252            impl #trait_path for #forwarder_ident {
253                type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
254                    as #trait_path>::__ink_TraitInfo;
255
256                #messages
257            }
258        )
259    }
260
261    /// Generates the code for all messages of a single ink! trait implementation of
262    /// the ink! smart contract.
263    fn generate_contract_trait_impl_messages(
264        &self,
265        trait_path: &syn::Path,
266        impl_block: &ir::ItemImpl,
267    ) -> TokenStream2 {
268        impl_block
269            .iter_messages()
270            .map(|message| {
271                self.generate_contract_trait_impl_for_message(trait_path, message)
272            })
273            .collect()
274    }
275
276    /// Generates the code for a single message of a single ink! trait implementation
277    /// that is implemented by the ink! smart contract.
278    fn generate_contract_trait_impl_for_message(
279        &self,
280        trait_path: &syn::Path,
281        message: ir::CallableWithSelector<ir::Message>,
282    ) -> TokenStream2 {
283        use ir::Callable as _;
284        let span = message.span();
285        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
286        let message_ident = message.ident();
287        let output_ident = generator::output_ident(message_ident);
288        let call_operator = match message.receiver() {
289            ir::Receiver::Ref => quote! { call },
290            ir::Receiver::RefMut => quote! { call_mut },
291        };
292        let forward_operator = match message.receiver() {
293            ir::Receiver::Ref => quote! { forward },
294            ir::Receiver::RefMut => quote! { forward_mut },
295        };
296        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
297        let input_idents = generator::input_message_idents(message.inputs());
298        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
299        let cfg_attrs = message.get_cfg_attrs(span);
300        quote_spanned!(span=>
301            #( #cfg_attrs )*
302            type #output_ident =
303                <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder as #trait_path>::#output_ident;
304
305            #[inline]
306            #( #cfg_attrs )*
307            fn #message_ident(
308                & #mut_token self
309                #( , #input_idents : #input_types )*
310            ) -> Self::#output_ident {
311                <_ as #trait_path>::#message_ident(
312                    <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
313                        <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
314                    )
315                    #( , #input_idents )*
316                )
317            }
318        )
319    }
320
321    /// Generates the code for all ink! inherent implementations of the contract itself.
322    ///
323    /// # Note
324    ///
325    /// The generated implementations must live outside of an artificial `const` block
326    /// in order to properly show their documentation using `rustdoc`.
327    fn generate_contract_inherent_impls(&self) -> TokenStream2 {
328        let abi = self.contract.config().abi();
329        self.contract
330            .module()
331            .impls()
332            .filter(|impl_block| {
333                // We are only interested in ink! trait implementation block.
334                impl_block.trait_path().is_none()
335            })
336            .map(|impl_block| self.generate_contract_inherent_impl(impl_block, abi))
337            .collect()
338    }
339
340    /// Generates the code for a single ink! inherent implementation of the contract
341    /// itself.
342    ///
343    /// # Note
344    ///
345    /// This produces the short-hand calling notation for the inherent contract
346    /// implementation. The generated code simply forwards its calling logic to the
347    /// associated call builder.
348    fn generate_contract_inherent_impl(
349        &self,
350        impl_block: &ir::ItemImpl,
351        abi: &ir::Abi,
352    ) -> TokenStream2 {
353        let span = impl_block.span();
354        let attrs = impl_block.attrs();
355        let forwarder_ident = self.generate_contract_ref_ident();
356        let messages = impl_block
357            .iter_messages()
358            .map(|message| self.generate_contract_inherent_impl_for_message(message));
359        let constructors = impl_block.iter_constructors().map(|constructor| {
360            self.generate_contract_inherent_impl_for_constructor(constructor, abi)
361        });
362        quote_spanned!(span=>
363            #( #attrs )*
364            impl #forwarder_ident {
365                #( #constructors )*
366                #( #messages )*
367            }
368        )
369    }
370
371    /// Generates the code for a single ink! inherent message of the contract itself.
372    ///
373    /// # Note
374    ///
375    /// This produces the short-hand calling notation for the inherent contract message.
376    /// The generated code simply forwards its calling logic to the associated call
377    /// builder.
378    fn generate_contract_inherent_impl_for_message(
379        &self,
380        message: ir::CallableWithSelector<ir::Message>,
381    ) -> TokenStream2 {
382        use ir::Callable as _;
383        let span = message.span();
384        let attrs = self
385            .contract
386            .config()
387            .whitelisted_attributes()
388            .filter_attr(message.attrs().to_vec());
389        let storage_ident = self.contract.module().storage().ident();
390        let message_ident = message.ident();
391        let try_message_ident = message.try_ident();
392        let call_operator = match message.receiver() {
393            ir::Receiver::Ref => quote! { call },
394            ir::Receiver::RefMut => quote! { call_mut },
395        };
396        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
397        let input_idents = generator::input_message_idents(message.inputs());
398        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
399        let output_type = message.output().map(|ty| quote! { -> #ty });
400        let wrapped_output_type = message.wrapped_output();
401        quote_spanned!(span=>
402            #( #attrs )*
403            #[inline]
404            pub fn #message_ident(
405                & #mut_token self
406                #( , #input_idents : #input_types )*
407            ) #output_type {
408                self.#try_message_ident( #( #input_idents, )* )
409                    .unwrap_or_else(|error| ::core::panic!(
410                        "encountered error while calling {}::{}: {:?}",
411                        ::core::stringify!(#storage_ident),
412                        ::core::stringify!(#message_ident),
413                        error,
414                    ))
415            }
416
417            #( #attrs )*
418            #[inline]
419            pub fn #try_message_ident(
420                & #mut_token self
421                #( , #input_idents : #input_types )*
422            ) -> #wrapped_output_type {
423                <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
424                    .#message_ident( #( #input_idents ),* )
425                    .try_invoke()
426                    .unwrap_or_else(|error| ::core::panic!(
427                        "encountered error while calling {}::{}: {:?}",
428                        ::core::stringify!(#storage_ident),
429                        ::core::stringify!(#message_ident),
430                        error,
431                    ))
432            }
433        )
434    }
435
436    /// Generates the code for a single ink! inherent constructor of the contract itself.
437    ///
438    /// # Note
439    ///
440    /// Unlike with ink! messages this does not forward to the call builder since
441    /// constructor calls in ink! do not have a short-hand notation and therefore this
442    /// implements the long-hand calling notation code directly.
443    fn generate_contract_inherent_impl_for_constructor(
444        &self,
445        constructor: ir::CallableWithSelector<ir::Constructor>,
446        abi: &ir::Abi,
447    ) -> TokenStream2 {
448        let span = constructor.span();
449        let attrs = self
450            .contract
451            .config()
452            .whitelisted_attributes()
453            .filter_attr(constructor.attrs().to_vec());
454        let constructor_ident = constructor.ident();
455        let selector_bytes = constructor.composed_selector().hex_lits();
456        let input_bindings = generator::input_bindings(constructor.inputs());
457        let input_types = generator::input_types(constructor.inputs());
458        let _storage_ident = self.contract.module().storage().ident();
459        let ret_type = constructor
460            .output()
461            .map(quote::ToTokens::to_token_stream)
462            .unwrap_or_else(|| quote::quote! { Self });
463
464        let mut create_builders = Vec::new();
465        if abi.is_ink() {
466            let arg_list = generator::generate_argument_list(
467                input_types.iter().cloned(),
468                quote!(::ink::reflect::ScaleEncoding),
469            );
470
471            let create_builder = quote_spanned!(span =>
472                #( #attrs )*
473                #[inline]
474                #[allow(clippy::type_complexity)]
475                pub fn #constructor_ident(
476                    #( #input_bindings : #input_types ),*
477                ) -> ::ink::env::call::CreateBuilder<
478                    Environment,
479                    Self,
480                    ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
481                    ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::ScaleEncoding>>,
482                    ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
483                > {
484                    ::ink::env::call::build_create::<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::<#ret_type>()
494                }
495            );
496            create_builders.push(create_builder);
497        }
498
499        if abi.is_solidity() {
500            // If ABI is all, we generate a second constructor with a "_sol" postfix.
501            // Otherwise, we use the same name.
502            let sol_constructor_ident = if abi.is_all() {
503                format_ident!("{}_sol", constructor_ident)
504            } else {
505                constructor_ident.clone()
506            };
507            let arg_list = generator::generate_argument_list(
508                input_types.iter().cloned(),
509                quote!(::ink::reflect::SolEncoding),
510            );
511
512            let create_builder = quote_spanned!(span =>
513                #( #attrs )*
514                #[inline]
515                #[allow(clippy::type_complexity)]
516                pub fn #sol_constructor_ident(
517                    #( #input_bindings : #input_types ),*
518                ) -> ::ink::env::call::CreateBuilder<
519                    Environment,
520                    Self,
521                    ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
522                    ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, ::ink::reflect::SolEncoding>>,
523                    ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
524                > {
525                    ::ink::env::call::build_create_solidity::<Self>()
526                        .exec_input(
527                            ::ink::env::call::ExecutionInput::new(
528                                ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
529                            )
530                            #(
531                                .push_arg(#input_bindings)
532                            )*
533                        )
534                        .returns::<#ret_type>()
535                }
536            );
537            create_builders.push(create_builder);
538        }
539
540        quote_spanned!(span=>
541            #( #create_builders )*
542        )
543    }
544}