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::call::ConstructorReturnType<#ref_ident> for #storage_ident {
111                    type Output = #ref_ident;
112                    type Error = ();
113
114                    fn ok(value: #ref_ident) -> Self::Output {
115                        value
116                    }
117                }
118
119                impl<E> ::ink::env::call::ConstructorReturnType<#ref_ident>
120                    for ::core::result::Result<#storage_ident, E>
121                where
122                    E: ::ink::scale::Decode
123                {
124                    const IS_RESULT: bool = true;
125
126                    type Output = ::core::result::Result<#ref_ident, E>;
127                    type Error = E;
128
129                    fn ok(value: #ref_ident) -> Self::Output {
130                        ::core::result::Result::Ok(value)
131                    }
132
133                    fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
134                        ::core::option::Option::Some(::core::result::Result::Err(err))
135                    }
136                }
137
138                impl ::ink::env::ContractEnv for #ref_ident {
139                    type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
140                }
141            };
142        )
143    }
144
145    /// Generates some ink! specific auxiliary trait implementations for the
146    /// smart contract reference type.
147    ///
148    /// These are required to properly interoperate with the contract reference.
149    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
150        let span = self.contract.module().storage().span();
151        let storage_ident = self.contract.module().storage().ident();
152        let ref_ident = self.generate_contract_ref_ident();
153        quote_spanned!(span=>
154            impl ::ink::env::call::FromAccountId<Environment> for #ref_ident {
155                #[inline]
156                fn from_account_id(account_id: AccountId) -> Self {
157                    Self { inner: <<#storage_ident
158                        as ::ink::codegen::ContractCallBuilder>::Type
159                        as ::ink::env::call::FromAccountId<Environment>>::from_account_id(account_id)
160                    }
161                }
162            }
163
164            impl ::ink::ToAccountId<Environment> for #ref_ident {
165                #[inline]
166                fn to_account_id(&self) -> AccountId {
167                    <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type
168                        as ::ink::ToAccountId<Environment>>::to_account_id(&self.inner)
169                }
170            }
171
172            impl ::core::convert::AsRef<AccountId> for #ref_ident {
173                fn as_ref(&self) -> &AccountId {
174                    <_ as ::core::convert::AsRef<AccountId>>::as_ref(&self.inner)
175                }
176            }
177
178            impl ::core::convert::AsMut<AccountId> for #ref_ident {
179                fn as_mut(&mut self) -> &mut AccountId {
180                    <_ as ::core::convert::AsMut<AccountId>>::as_mut(&mut self.inner)
181                }
182            }
183        )
184    }
185
186    /// Generates the `CallBuilder` trait implementation for the contract reference.
187    ///
188    /// This creates the bridge between the ink! smart contract type and the
189    /// associated call builder.
190    fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
191        let span = self.contract.module().storage().span();
192        let ref_ident = self.generate_contract_ref_ident();
193        let storage_ident = self.contract.module().storage().ident();
194        quote_spanned!(span=>
195            const _: () = {
196                impl ::ink::codegen::TraitCallBuilder for #ref_ident {
197                    type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type;
198
199                    #[inline]
200                    fn call(&self) -> &Self::Builder {
201                        &self.inner
202                    }
203
204                    #[inline]
205                    fn call_mut(&mut self) -> &mut Self::Builder {
206                        &mut self.inner
207                    }
208                }
209            };
210        )
211    }
212
213    /// Generates the code for all ink! trait implementations of the contract itself.
214    ///
215    /// # Note
216    ///
217    /// The generated implementations must live outside of an artificial `const` block
218    /// in order to properly show their documentation using `rustdoc`.
219    fn generate_contract_trait_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_contract_trait_impl(trait_path, impl_block)
227                })
228            })
229            .collect()
230    }
231
232    /// Generates the code for a single ink! trait implementation of the contract itself.
233    ///
234    /// The generated implementation mainly forwards the calls to the previously generated
235    /// associated call builder that implements each respective ink! trait.
236    fn generate_contract_trait_impl(
237        &self,
238        trait_path: &syn::Path,
239        impl_block: &ir::ItemImpl,
240    ) -> TokenStream2 {
241        let span = impl_block.span();
242        let attrs = impl_block.attrs();
243        let forwarder_ident = self.generate_contract_ref_ident();
244        let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block);
245        quote_spanned!(span=>
246            #( #attrs )*
247            impl #trait_path for #forwarder_ident {
248                type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
249                    as #trait_path>::__ink_TraitInfo;
250
251                #messages
252            }
253        )
254    }
255
256    /// Generates the code for all messages of a single ink! trait implementation of
257    /// the ink! smart contract.
258    fn generate_contract_trait_impl_messages(
259        &self,
260        trait_path: &syn::Path,
261        impl_block: &ir::ItemImpl,
262    ) -> TokenStream2 {
263        impl_block
264            .iter_messages()
265            .map(|message| {
266                self.generate_contract_trait_impl_for_message(trait_path, message)
267            })
268            .collect()
269    }
270
271    /// Generates the code for a single message of a single ink! trait implementation
272    /// that is implemented by the ink! smart contract.
273    fn generate_contract_trait_impl_for_message(
274        &self,
275        trait_path: &syn::Path,
276        message: ir::CallableWithSelector<ir::Message>,
277    ) -> TokenStream2 {
278        use ir::Callable as _;
279        let span = message.span();
280        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
281        let message_ident = message.ident();
282        let output_ident = generator::output_ident(message_ident);
283        let call_operator = match message.receiver() {
284            ir::Receiver::Ref => quote! { call },
285            ir::Receiver::RefMut => quote! { call_mut },
286        };
287        let forward_operator = match message.receiver() {
288            ir::Receiver::Ref => quote! { forward },
289            ir::Receiver::RefMut => quote! { forward_mut },
290        };
291        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
292        let input_idents = generator::input_message_idents(message.inputs());
293        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
294        let cfg_attrs = message.get_cfg_attrs(span);
295        quote_spanned!(span=>
296            #( #cfg_attrs )*
297            type #output_ident =
298                <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder as #trait_path>::#output_ident;
299
300            #[inline]
301            #( #cfg_attrs )*
302            fn #message_ident(
303                & #mut_token self
304                #( , #input_idents : #input_types )*
305            ) -> Self::#output_ident {
306                <_ as #trait_path>::#message_ident(
307                    <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
308                        <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
309                    )
310                    #( , #input_idents )*
311                )
312            }
313        )
314    }
315
316    /// Generates the code for all ink! inherent implementations of the contract itself.
317    ///
318    /// # Note
319    ///
320    /// The generated implementations must live outside of an artificial `const` block
321    /// in order to properly show their documentation using `rustdoc`.
322    fn generate_contract_inherent_impls(&self) -> TokenStream2 {
323        self.contract
324            .module()
325            .impls()
326            .filter(|impl_block| {
327                // We are only interested in ink! trait implementation block.
328                impl_block.trait_path().is_none()
329            })
330            .map(|impl_block| self.generate_contract_inherent_impl(impl_block))
331            .collect()
332    }
333
334    /// Generates the code for a single ink! inherent implementation of the contract
335    /// itself.
336    ///
337    /// # Note
338    ///
339    /// This produces the short-hand calling notation for the inherent contract
340    /// implementation. The generated code simply forwards its calling logic to the
341    /// associated call builder.
342    fn generate_contract_inherent_impl(&self, impl_block: &ir::ItemImpl) -> TokenStream2 {
343        let span = impl_block.span();
344        let attrs = impl_block.attrs();
345        let forwarder_ident = self.generate_contract_ref_ident();
346        let messages = impl_block
347            .iter_messages()
348            .map(|message| self.generate_contract_inherent_impl_for_message(message));
349        let constructors = impl_block.iter_constructors().map(|constructor| {
350            self.generate_contract_inherent_impl_for_constructor(constructor)
351        });
352        quote_spanned!(span=>
353            #( #attrs )*
354            impl #forwarder_ident {
355                #( #constructors )*
356                #( #messages )*
357            }
358        )
359    }
360
361    /// Generates the code for a single ink! inherent message of the contract itself.
362    ///
363    /// # Note
364    ///
365    /// This produces the short-hand calling notation for the inherent contract message.
366    /// The generated code simply forwards its calling logic to the associated call
367    /// builder.
368    fn generate_contract_inherent_impl_for_message(
369        &self,
370        message: ir::CallableWithSelector<ir::Message>,
371    ) -> TokenStream2 {
372        use ir::Callable as _;
373        let span = message.span();
374        let attrs = self
375            .contract
376            .config()
377            .whitelisted_attributes()
378            .filter_attr(message.attrs().to_vec());
379        let storage_ident = self.contract.module().storage().ident();
380        let message_ident = message.ident();
381        let try_message_ident = message.try_ident();
382        let call_operator = match message.receiver() {
383            ir::Receiver::Ref => quote! { call },
384            ir::Receiver::RefMut => quote! { call_mut },
385        };
386        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
387        let input_idents = generator::input_message_idents(message.inputs());
388        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
389        let output_type = message.output().map(|ty| quote! { -> #ty });
390        let wrapped_output_type = message.wrapped_output();
391        quote_spanned!(span=>
392            #( #attrs )*
393            #[inline]
394            pub fn #message_ident(
395                & #mut_token self
396                #( , #input_idents : #input_types )*
397            ) #output_type {
398                self.#try_message_ident( #( #input_idents, )* )
399                    .unwrap_or_else(|error| ::core::panic!(
400                        "encountered error while calling {}::{}: {:?}",
401                        ::core::stringify!(#storage_ident),
402                        ::core::stringify!(#message_ident),
403                        error,
404                    ))
405            }
406
407            #( #attrs )*
408            #[inline]
409            pub fn #try_message_ident(
410                & #mut_token self
411                #( , #input_idents : #input_types )*
412            ) -> #wrapped_output_type {
413                <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
414                    .#message_ident( #( #input_idents ),* )
415                    .try_invoke()
416                    .unwrap_or_else(|error| ::core::panic!(
417                        "encountered error while calling {}::{}: {:?}",
418                        ::core::stringify!(#storage_ident),
419                        ::core::stringify!(#message_ident),
420                        error,
421                    ))
422            }
423        )
424    }
425
426    /// Generates the code for a single ink! inherent constructor of the contract itself.
427    ///
428    /// # Note
429    ///
430    /// Unlike with ink! messages this does not forward to the call builder since
431    /// constructor calls in ink! do not have a short-hand notation and therefore this
432    /// implements the long-hand calling notation code directly.
433    fn generate_contract_inherent_impl_for_constructor(
434        &self,
435        constructor: ir::CallableWithSelector<ir::Constructor>,
436    ) -> TokenStream2 {
437        let span = constructor.span();
438        let attrs = self
439            .contract
440            .config()
441            .whitelisted_attributes()
442            .filter_attr(constructor.attrs().to_vec());
443        let constructor_ident = constructor.ident();
444        let selector_bytes = constructor.composed_selector().hex_lits();
445        let input_bindings = generator::input_bindings(constructor.inputs());
446        let input_types = generator::input_types(constructor.inputs());
447        let storage_ident = self.contract.module().storage().ident();
448        let arg_list = generator::generate_argument_list(input_types.iter().cloned());
449        let ret_type = constructor
450            .output()
451            .map(quote::ToTokens::to_token_stream)
452            .unwrap_or_else(|| quote::quote! { Self });
453        quote_spanned!(span =>
454            #( #attrs )*
455            #[inline]
456            #[allow(clippy::type_complexity)]
457            pub fn #constructor_ident(
458                #( #input_bindings : #input_types ),*
459            ) -> ::ink::env::call::CreateBuilder<
460                Environment,
461                Self,
462                ::ink::env::call::utils::Unset<Hash>,
463                ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2<<#storage_ident as ::ink::env::ContractEnv>::Env>>,
464                ::ink::env::call::utils::Unset<Balance>,
465                ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list>>,
466                ::ink::env::call::utils::Unset<::ink::env::call::state::Salt>,
467                ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
468            > {
469                ::ink::env::call::build_create::<Self>()
470                    .exec_input(
471                        ::ink::env::call::ExecutionInput::new(
472                            ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
473                        )
474                        #(
475                            .push_arg(#input_bindings)
476                        )*
477                    )
478                    .returns::<#ret_type>()
479            }
480        )
481    }
482}