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 derive_more::From;
16use ink_primitives::abi::Abi;
17use ir::{
18    Callable,
19    IsDocAttribute as _,
20};
21use itertools::Itertools;
22use proc_macro2::TokenStream as TokenStream2;
23use quote::{
24    quote,
25    quote_spanned,
26};
27use syn::spanned::Spanned as _;
28
29use crate::{
30    generator,
31    GenerateCode,
32};
33
34/// Generates code for the contract reference of the ink! smart contract.
35///
36/// The call builder is the entity that builds up calls for calling of other
37/// smart contract on-chain in a type safe way.
38/// It implements all ink! traits that the associated ink! smart contract implements
39/// so that their underlying implementation directly calls the respective ink!
40/// trait implementation on-chain.
41///
42/// The ink! call builder of a smart contract is directly used by the storage
43/// type of the smart contract itself as well by other entities that use the
44/// smart contract via long-hand calling notation to incrementally build up calls.
45#[derive(From)]
46pub struct ContractRef<'a> {
47    contract: &'a ir::Contract,
48}
49
50impl GenerateCode for ContractRef<'_> {
51    fn generate_code(&self) -> TokenStream2 {
52        let contract_ref = self.generate_struct();
53        let contract_ref_trait_impls = self.generate_contract_trait_impls();
54        let contract_ref_inherent_impls = self.generate_contract_inherent_impls();
55        let call_builder_trait_impl = self.generate_call_builder_trait_impl();
56        let auxiliary_trait_impls = self.generate_auxiliary_trait_impls();
57        quote! {
58            #contract_ref
59            #contract_ref_trait_impls
60            #contract_ref_inherent_impls
61            #call_builder_trait_impl
62            #auxiliary_trait_impls
63        }
64    }
65}
66
67impl ContractRef<'_> {
68    /// Generates the identifier of the contract reference struct.
69    fn generate_contract_ref_ident(&self) -> syn::Ident {
70        quote::format_ident!("{}Ref", self.contract.module().storage().ident())
71    }
72
73    /// Generates the code for the struct representing the contract reference.
74    ///
75    /// The generated struct is the type onto which everything is implemented.
76    /// It is also the type that is going to be used by other smart contract
77    /// dynamically depending on the smart contract. It mirrors the smart contract
78    /// API but is just a typed thin-wrapper around an `AccountId`.
79    fn generate_struct(&self) -> TokenStream2 {
80        let span = self.contract.module().storage().span();
81        let doc_attrs = self
82            .contract
83            .module()
84            .storage()
85            .attrs()
86            .iter()
87            .filter(|&x| syn::Attribute::is_doc_attribute(x))
88            .cloned();
89        let storage_ident = self.contract.module().storage().ident();
90        let ref_ident = self.generate_contract_ref_ident();
91        let abi = default_abi!();
92        let sol_codec = if cfg!(any(ink_abi = "sol", ink_abi = "all")) {
93            // These manual implementations are a bit more efficient than the derived
94            // equivalents.
95            quote_spanned!(span=>
96                impl<Abi> ::ink::SolDecode for #ref_ident<Abi> {
97                    type SolType = ::ink::Address;
98
99                    fn from_sol_type(value: Self::SolType) -> ::core::result::Result<Self, ::ink::sol::Error> {
100                        Ok(Self {
101                            inner: <<#storage_ident
102                                as ::ink::codegen::ContractCallBuilder>::Type<Abi>
103                                as ::ink::env::call::FromAddr>::from_addr(value),
104                            _marker: ::core::marker::PhantomData,
105                        })
106                    }
107                }
108
109                impl<'a, Abi> ::ink::SolEncode<'a> for #ref_ident<Abi> {
110                    type SolType = &'a ::ink::Address;
111
112                    fn to_sol_type(&'a self) -> Self::SolType {
113                        self.as_ref()
114                    }
115                }
116            )
117        } else {
118            quote!()
119        };
120        quote_spanned!(span=>
121            #[derive(
122                ::core::fmt::Debug,
123                ::core::hash::Hash,
124                ::core::cmp::PartialEq,
125                ::core::cmp::Eq,
126                ::core::clone::Clone,
127            )]
128            #[::ink::scale_derive(Encode, Decode)]
129            #( #doc_attrs )*
130            pub struct #ref_ident<Abi = #abi> {
131                inner: <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>,
132                _marker: core::marker::PhantomData<Abi>,
133            }
134
135            const _: () = {
136                impl ::ink::env::ContractReference for #storage_ident {
137                    type Type = #ref_ident;
138                }
139
140                impl<Abi> ::ink::env::ContractReverseReference for #ref_ident<Abi> {
141                    type Type = #storage_ident;
142                }
143
144                impl<Abi> ::ink::env::call::ConstructorReturnType<#ref_ident, Abi> for #storage_ident
145                where
146                    (): ink::env::call::utils::DecodeConstructorError<Abi>
147                {
148                    type Output = #ref_ident;
149                    type Error = ();
150
151                    fn ok(value: #ref_ident) -> Self::Output {
152                        value
153                    }
154                }
155
156                impl<E, Abi> ::ink::env::call::ConstructorReturnType<#ref_ident, Abi>
157                    for ::core::result::Result<#storage_ident, E>
158                where
159                    E: ink::env::call::utils::DecodeConstructorError<Abi>
160                {
161                    const IS_RESULT: bool = true;
162
163                    type Output = ::core::result::Result<#ref_ident, E>;
164                    type Error = E;
165
166                    fn ok(value: #ref_ident) -> Self::Output {
167                        ::core::result::Result::Ok(value)
168                    }
169
170                    fn err(err: Self::Error) -> ::core::option::Option<Self::Output> {
171                        ::core::option::Option::Some(::core::result::Result::Err(err))
172                    }
173                }
174
175                impl<Abi> ::ink::env::ContractEnv for #ref_ident<Abi> {
176                    type Env = <#storage_ident as ::ink::env::ContractEnv>::Env;
177                }
178
179                #[cfg(feature = "std")]
180                // We require this manual implementation since the derive produces incorrect trait bounds.
181                impl<Abi> ::ink::storage::traits::StorageLayout for #ref_ident<Abi> {
182                    fn layout(
183                        __key: &::ink::primitives::Key,
184                    ) -> ::ink::metadata::layout::Layout {
185                        ::ink::metadata::layout::Layout::Struct(
186                            ::ink::metadata::layout::StructLayout::new(
187                                ::core::stringify!(#ref_ident),
188                                [
189                                    ::ink::metadata::layout::FieldLayout::new(
190                                        "inner",
191                                        <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>
192                                            as ::ink::storage::traits::StorageLayout>::layout(__key)
193                                    )
194                                ]
195                            )
196                        )
197                    }
198                }
199
200                #[cfg(feature = "std")]
201                // We require this manual implementation since the derive produces incorrect trait bounds.
202                impl<Abi> ::ink::scale_info::TypeInfo for #ref_ident<Abi>
203                where
204                    ::ink::Address: ::ink::scale_info::TypeInfo + 'static,
205                {
206                    type Identity = <
207                        <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi> as ::ink::scale_info::TypeInfo
208                    >::Identity;
209
210                    fn type_info() -> ::ink::scale_info::Type {
211                        <
212                            <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi> as ::ink::scale_info::TypeInfo
213                        >::type_info()
214                    }
215                }
216
217                #sol_codec
218            };
219        )
220    }
221
222    /// Generates some ink! specific auxiliary trait implementations for the
223    /// smart contract reference type.
224    ///
225    /// These are required to properly interoperate with the contract reference.
226    fn generate_auxiliary_trait_impls(&self) -> TokenStream2 {
227        let span = self.contract.module().storage().span();
228        let storage_ident = self.contract.module().storage().ident();
229        let ref_ident = self.generate_contract_ref_ident();
230        quote_spanned!(span=>
231            impl<Abi> ::ink::env::call::FromAddr for #ref_ident<Abi> {
232                #[inline]
233                fn from_addr(addr: ::ink::Address) -> Self {
234                    Self {
235                        inner: <<#storage_ident
236                            as ::ink::codegen::ContractCallBuilder>::Type<Abi>
237                            as ::ink::env::call::FromAddr>::from_addr(addr),
238                        _marker: ::core::default::Default::default(),
239                    }
240                }
241            }
242
243            impl<Abi> ::ink::ToAddr for #ref_ident<Abi> {
244                #[inline]
245                fn to_addr(&self) -> ::ink::Address {
246                    <<#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>
247                        as ::ink::ToAddr>::to_addr(&self.inner)
248                }
249            }
250
251            impl<Abi> ::core::convert::AsRef<::ink::Address> for #ref_ident<Abi> {
252                fn as_ref(&self) -> &::ink::Address {
253                    <_ as ::core::convert::AsRef<::ink::Address>>::as_ref(&self.inner)
254                }
255            }
256
257            impl<Abi> ::core::convert::AsMut<::ink::Address> for #ref_ident<Abi> {
258                fn as_mut(&mut self) -> &mut ::ink::Address {
259                    <_ as ::core::convert::AsMut<::ink::Address>>::as_mut(&mut self.inner)
260                }
261            }
262        )
263    }
264
265    /// Generates the `CallBuilder` trait implementation for the contract reference.
266    ///
267    /// This creates the bridge between the ink! smart contract type and the
268    /// associated call builder.
269    fn generate_call_builder_trait_impl(&self) -> TokenStream2 {
270        let span = self.contract.module().storage().span();
271        let ref_ident = self.generate_contract_ref_ident();
272        let storage_ident = self.contract.module().storage().ident();
273        quote_spanned!(span=>
274            const _: () = {
275                impl<Abi> ::ink::codegen::TraitCallBuilder for #ref_ident<Abi> {
276                    type Builder = <#storage_ident as ::ink::codegen::ContractCallBuilder>::Type<Abi>;
277
278                    #[inline]
279                    fn call(&self) -> &Self::Builder {
280                        &self.inner
281                    }
282
283                    #[inline]
284                    fn call_mut(&mut self) -> &mut Self::Builder {
285                        &mut self.inner
286                    }
287                }
288            };
289        )
290    }
291
292    /// Generates the code for all ink! trait implementations of the contract itself.
293    ///
294    /// # Note
295    ///
296    /// The generated implementations must live outside of an artificial `const` block
297    /// in order to properly show their documentation using `rustdoc`.
298    fn generate_contract_trait_impls(&self) -> TokenStream2 {
299        self.contract
300            .module()
301            .impls()
302            .filter_map(|impl_block| {
303                // We are only interested in ink! trait implementation block.
304                impl_block.trait_path().map(|trait_path| {
305                    self.generate_contract_trait_impl(trait_path, impl_block)
306                })
307            })
308            .collect()
309    }
310
311    /// Generates the code for a single ink! trait implementation of the contract itself.
312    ///
313    /// The generated implementation mainly forwards the calls to the previously generated
314    /// associated call builder that implements each respective ink! trait.
315    fn generate_contract_trait_impl(
316        &self,
317        trait_path: &syn::Path,
318        impl_block: &ir::ItemImpl,
319    ) -> TokenStream2 {
320        let span = impl_block.span();
321        let attrs = impl_block.attrs();
322        let forwarder_ident = self.generate_contract_ref_ident();
323        generate_abi_impls!(@tokens |abi: TokenStream2| {
324            let messages = self.generate_contract_trait_impl_messages(trait_path, impl_block, abi.clone());
325            quote_spanned!(span=>
326                #( #attrs )*
327                impl #trait_path for #forwarder_ident<#abi> {
328                    type __ink_TraitInfo = <::ink::reflect::TraitDefinitionRegistry<Environment>
329                        as #trait_path>::__ink_TraitInfo;
330
331                    #messages
332                }
333            )
334        })
335    }
336
337    /// Generates the code for all messages of a single ink! trait implementation of
338    /// the ink! smart contract.
339    fn generate_contract_trait_impl_messages(
340        &self,
341        trait_path: &syn::Path,
342        impl_block: &ir::ItemImpl,
343        abi: TokenStream2,
344    ) -> TokenStream2 {
345        impl_block
346            .iter_messages()
347            .map(|message| {
348                self.generate_contract_trait_impl_for_message(
349                    trait_path,
350                    message,
351                    abi.clone(),
352                )
353            })
354            .collect()
355    }
356
357    /// Generates the code for a single message of a single ink! trait implementation
358    /// that is implemented by the ink! smart contract.
359    fn generate_contract_trait_impl_for_message(
360        &self,
361        trait_path: &syn::Path,
362        message: ir::CallableWithSelector<ir::Message>,
363        abi: TokenStream2,
364    ) -> TokenStream2 {
365        use ir::Callable as _;
366        let span = message.span();
367        let trait_info_id = generator::generate_reference_to_trait_info(span, trait_path);
368        let message_ident = message.ident();
369        let output_ident = generator::output_ident(message_ident);
370        let call_operator = match message.receiver() {
371            ir::Receiver::Ref => quote! { call },
372            ir::Receiver::RefMut => quote! { call_mut },
373        };
374        let forward_operator = match message.receiver() {
375            ir::Receiver::Ref => quote! { forward },
376            ir::Receiver::RefMut => quote! { forward_mut },
377        };
378        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
379        let input_idents = generator::input_message_idents(message.inputs());
380        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
381        let cfg_attrs = message.get_cfg_attrs(span);
382        quote_spanned!(span=>
383            #( #cfg_attrs )*
384            type #output_ident =
385                <<Self::__ink_TraitInfo as ::ink::codegen::TraitCallForwarder>::Forwarder<#abi> as #trait_path>::#output_ident;
386
387            #[inline]
388            #( #cfg_attrs )*
389            fn #message_ident(
390                & #mut_token self
391                #( , #input_idents : #input_types )*
392            ) -> Self::#output_ident {
393                <_ as #trait_path>::#message_ident(
394                    <_ as ::ink::codegen::TraitCallForwarderFor<{#trait_info_id}>>::#forward_operator(
395                        <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self),
396                    )
397                    #( , #input_idents )*
398                )
399            }
400        )
401    }
402
403    /// Generates the code for all ink! inherent implementations of the contract itself.
404    ///
405    /// # Note
406    ///
407    /// The generated implementations must live outside of an artificial `const` block
408    /// in order to properly show their documentation using `rustdoc`.
409    fn generate_contract_inherent_impls(&self) -> TokenStream2 {
410        generate_abi_impls!(@type |abi| {
411            let impls = self.contract.module().impls()
412                .filter(|impl_block| {
413                    // We are only interested in ink! trait implementation block.
414                    impl_block.trait_path().is_none()
415                })
416                .map(|impl_block| self.generate_contract_inherent_impl(impl_block, abi));
417            let impl_sol_constructor = match abi {
418                Abi::Ink => quote!(),
419                Abi::Sol => {
420                    // Only one constructor is used for Solidity ABI encoding.
421                    let constructor = self.contract.module().impls()
422                        .flat_map(|item_impl| item_impl.iter_constructors())
423                        .find_or_first(|constructor| {
424                            constructor.is_default()
425                        })
426                        .expect("Expected at least one constructor");
427                    let ctor = self.generate_contract_inherent_impl_for_constructor(constructor, abi);
428                    let span = ctor.span();
429                    let forwarder_ident = self.generate_contract_ref_ident();
430                    quote_spanned!(span=>
431                        impl #forwarder_ident<::ink::abi::Sol> {
432                            #ctor
433                        }
434                    )
435                }
436            };
437            let span = self.contract.module().span();
438            quote_spanned!(span=>
439                #impl_sol_constructor
440                #( #impls )*
441            )
442        })
443    }
444
445    /// Generates the code for a single ink! inherent implementation of the contract
446    /// itself.
447    ///
448    /// # Note
449    ///
450    /// This produces the short-hand calling notation for the inherent contract
451    /// implementation. The generated code simply forwards its calling logic to the
452    /// associated call builder.
453    fn generate_contract_inherent_impl(
454        &self,
455        impl_block: &ir::ItemImpl,
456        abi: Abi,
457    ) -> TokenStream2 {
458        let span = impl_block.span();
459        let attrs = impl_block.attrs();
460        let forwarder_ident = self.generate_contract_ref_ident();
461        let messages = impl_block
462            .iter_messages()
463            .map(|message| self.generate_contract_inherent_impl_for_message(message));
464        let messages = quote! {
465            #( #messages )*
466        };
467        let (abi_ty, constructors) = match abi {
468            Abi::Ink => {
469                let constructors = impl_block.iter_constructors().map(|constructor| {
470                    self.generate_contract_inherent_impl_for_constructor(constructor, abi)
471                });
472                (
473                    quote!(::ink::abi::Ink),
474                    quote! {
475                        #( #constructors )*
476                    },
477                )
478            }
479            Abi::Sol => {
480                (
481                    quote!(::ink::abi::Sol),
482                    // The constructor implementation for Solidity ABI encoding is
483                    // handled in `generate_contract_inherent_impls`.
484                    quote!(),
485                )
486            }
487        };
488        quote_spanned!(span=>
489            #( #attrs )*
490            impl #forwarder_ident<#abi_ty> {
491                #constructors
492                #messages
493            }
494        )
495    }
496
497    /// Generates the code for a single ink! inherent message of the contract itself.
498    ///
499    /// # Note
500    ///
501    /// This produces the short-hand calling notation for the inherent contract message.
502    /// The generated code simply forwards its calling logic to the associated call
503    /// builder.
504    fn generate_contract_inherent_impl_for_message(
505        &self,
506        message: ir::CallableWithSelector<ir::Message>,
507    ) -> TokenStream2 {
508        use ir::Callable as _;
509        let span = message.span();
510        let attrs = self
511            .contract
512            .config()
513            .whitelisted_attributes()
514            .filter_attr(message.attrs().to_vec());
515        let storage_ident = self.contract.module().storage().ident();
516        let message_ident = message.ident();
517        let try_message_ident = message.try_ident();
518        let call_operator = match message.receiver() {
519            ir::Receiver::Ref => quote! { call },
520            ir::Receiver::RefMut => quote! { call_mut },
521        };
522        let mut_token = message.receiver().is_ref_mut().then(|| quote! { mut });
523        let input_idents = generator::input_message_idents(message.inputs());
524        let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
525        let output_type = message.output().map(|ty| quote! { -> #ty });
526        let wrapped_output_type = message.wrapped_output();
527        quote_spanned!(span=>
528            #( #attrs )*
529            #[inline]
530            pub fn #message_ident(
531                & #mut_token self
532                #( , #input_idents : #input_types )*
533            ) #output_type {
534                self.#try_message_ident( #( #input_idents, )* )
535                    .unwrap_or_else(|error| ::core::panic!(
536                        "encountered error while calling {}::{}: {:?}",
537                        ::core::stringify!(#storage_ident),
538                        ::core::stringify!(#message_ident),
539                        error,
540                    ))
541            }
542
543            #( #attrs )*
544            #[inline]
545            pub fn #try_message_ident(
546                & #mut_token self
547                #( , #input_idents : #input_types )*
548            ) -> #wrapped_output_type {
549                <Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
550                    .#message_ident( #( #input_idents ),* )
551                    .try_invoke()
552                    .unwrap_or_else(|error| ::core::panic!(
553                        "encountered error while calling {}::{}: {:?}",
554                        ::core::stringify!(#storage_ident),
555                        ::core::stringify!(#message_ident),
556                        error,
557                    ))
558            }
559        )
560    }
561
562    /// Generates the code for a single ink! inherent constructor of the contract itself.
563    ///
564    /// # Note
565    ///
566    /// Unlike with ink! messages this does not forward to the call builder since
567    /// constructor calls in ink! do not have a short-hand notation and therefore this
568    /// implements the long-hand calling notation code directly.
569    fn generate_contract_inherent_impl_for_constructor(
570        &self,
571        constructor: ir::CallableWithSelector<ir::Constructor>,
572        abi: Abi,
573    ) -> TokenStream2 {
574        let span = constructor.span();
575        let attrs = self
576            .contract
577            .config()
578            .whitelisted_attributes()
579            .filter_attr(constructor.attrs().to_vec());
580        let constructor_ident = constructor.ident();
581        let input_bindings = generator::input_bindings(constructor.inputs());
582        let input_types = generator::input_types(constructor.inputs());
583        let ret_type = constructor
584            .output()
585            .map(quote::ToTokens::to_token_stream)
586            .unwrap_or_else(|| quote::quote! { Self });
587
588        let (abi_ty, exec_input_init) = match abi {
589            Abi::Ink => {
590                let selector_bytes = constructor.composed_selector().hex_lits();
591                (
592                    quote!(::ink::abi::Ink),
593                    quote! {
594                        ::ink::env::call::ExecutionInput::new(
595                            ::ink::env::call::Selector::new([ #( #selector_bytes ),* ])
596                        )
597                    },
598                )
599            }
600            Abi::Sol => {
601                (
602                    quote!(::ink::abi::Sol),
603                    quote! {
604                        ::ink::env::call::ExecutionInput::no_selector()
605                    },
606                )
607            }
608        };
609        let arg_list = generator::generate_argument_list(
610            input_types.iter().cloned(),
611            abi_ty.clone(),
612        );
613
614        quote_spanned!(span =>
615            #( #attrs )*
616            #[inline]
617            #[allow(clippy::type_complexity)]
618            pub fn #constructor_ident(
619                #( #input_bindings : #input_types ),*
620            ) -> ::ink::env::call::CreateBuilder<
621                Environment,
622                Self,
623                ::ink::env::call::utils::Set<::ink::env::call::LimitParamsV2 >,
624                ::ink::env::call::utils::Set<::ink::env::call::ExecutionInput<#arg_list, #abi_ty>>,
625                ::ink::env::call::utils::Set<::ink::env::call::utils::ReturnType<#ret_type>>,
626                #abi_ty
627            > {
628                ::ink::env::call::build_create_abi::<Self, #abi_ty>()
629                    .exec_input(
630                        #exec_input_init
631                        #(
632                            .push_arg(#input_bindings)
633                        )*
634                    )
635                    .returns::<#ret_type>()
636            }
637        )
638    }
639}