ink_codegen/generator/as_dependency/
contract_ref.rs1use 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#[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 fn generate_contract_ref_base_ident(&self) -> syn::Ident {
71 format_ident!("{}Ref", self.contract.module().storage().ident())
72 }
73
74 fn generate_contract_ref_ident(&self) -> syn::Ident {
76 format_ident!("{}For", self.generate_contract_ref_base_ident())
77 }
78
79 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 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 #[allow(dead_code)]
155 pub type #ref_ident_default_abi = #ref_ident::<#abi>;
156 #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 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 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 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 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 fn generate_contract_trait_impls(&self) -> TokenStream2 {
323 self.contract
324 .module()
325 .impls()
326 .filter_map(|impl_block| {
327 impl_block.trait_path().map(|trait_path| {
329 self.generate_contract_trait_impl(trait_path, impl_block)
330 })
331 })
332 .collect()
333 }
334
335 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 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 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 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 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 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 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 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 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 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}