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