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